Inspector enhancements

This commit is contained in:
Steven Hildreth 2019-06-23 10:45:45 -05:00
parent f8e97a5880
commit 114dc26ed2
27 changed files with 511 additions and 242 deletions

View file

@ -7,17 +7,22 @@
<ItemGroup>
<None Remove="appsettings.json" />
<None Remove="PreInspectScript.ps1" />
</ItemGroup>
<ItemGroup>
<Content Include="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="PreInspectScript.ps1">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="McMaster.Extensions.CommandLineUtils" Version="2.3.4" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.2.0" />
<PackageReference Include="Microsoft.PowerShell.SDK" Version="6.2.1" />
</ItemGroup>
<ItemGroup>

View file

@ -0,0 +1 @@
gci -Path "N:\_complete" -r -include *.zip,*.rar,*.7z | foreach { & 'C:\Program Files\7-Zip\7z.exe' -x -y $_.FullName -o"$(' " '+$_.DirectoryName+' " ')"}

View file

@ -2,7 +2,7 @@
"profiles": {
"Inspector": {
"commandName": "Project",
"commandLineArgs": "-f \"N:\\_complete\" -d \"M:\\inbound\" -r"
"commandLineArgs": "-f \"N:\\_complete\" -d \"M:\\inbound\""
}
}
}

View file

@ -18,7 +18,7 @@
"Width": 320
},
"DontDoMetaDataProvidersSearchArtists": [ "Various Artists", "Sound Tracks" ],
"FileExtensionsToDelete": [ ".accurip", ".cue", ".db", ".gif", ".html", ".ini", ".jpg", ".jpeg", ".log", ".mpg", ".m3u", ".png", ".nfo", ".nzb", ".sfv", ".srr", ".txt", ".url" ],
"FileExtensionsToDelete": [ ".accurip", ".cue", ".db", ".exe", ".html", ".ini", ".log", ".md5", ".mht", ".mpg", ".m3u", ".nfo", ".nzb", ".pls", ".sfv", ".srr", ".txt", ".url" ],
"RecordNoResultSearches": true,
"ArtistNameReplace": {
"AC/DC": [ "AC; DC", "AC;DC", "AC/ DC", "AC DC" ],
@ -57,6 +57,8 @@
"MaximumArtistImagesToAdd": 12,
"MaximumReleaseImagesToAdd": 12,
"MaxImageWidth": 2048,
"PreInspectScript": "PreInspectScript.ps1",
"PostInspectScript": "",
"ReleaseRemoveStringsRegex": "(\\\\s*(-\\\\s)*((CD[_\\-#\\s]*[0-9]*)))|((\\\\(|\\\\[)+([0-9]|,|self|bonus|re(leas|master|(e|d)*)*|th|anniversary|cd|disc|deluxe|dig(ipack)*|vinyl|japan(ese)*|asian|remastered|limited|ltd|expanded|edition|\\\\s)+(]|\\\\)*))",
"TrackRemoveStringsRegex": "^([0-9]+)(\\.|-|\\s)*",
"ReplaceStrings": [

View file

@ -1,15 +1,18 @@
using Microsoft.EntityFrameworkCore;
using Mapster;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Roadie.Library.Caching;
using Roadie.Library.Configuration;
using Roadie.Library.Data;
using Roadie.Library.Engines;
using Roadie.Library.Extensions;
using Roadie.Library.Factories;
using Roadie.Library.MetaData.ID3Tags;
using Roadie.Library.Processors;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
@ -66,7 +69,60 @@ namespace Roadie.Library.Tests
var a = artistLookupEngine.DatabaseQueryForArtistName("Nas");
}
}
//[Fact]
//public void Update_Releases_Special_Name()
//{
// var optionsBuilder = new DbContextOptionsBuilder<RoadieDbContext>();
// optionsBuilder.UseMySql("server=viking;userid=roadie;password=MenAtW0rk668;persistsecurityinfo=True;database=roadie;ConvertZeroDateTime=true");
// using (var context = new RoadieDbContext(optionsBuilder.Options))
// {
// var now = DateTime.UtcNow;
// foreach(var release in context.Releases)
// {
// var releaseModel = release.Adapt<Roadie.Library.Models.Releases.Release>();
// var specialReleaseTitle = release.Title.ToAlphanumericName();
// if (!releaseModel.AlternateNamesList.Contains(specialReleaseTitle, StringComparer.OrdinalIgnoreCase))
// {
// var alt = new List<string>(releaseModel.AlternateNamesList)
// {
// specialReleaseTitle
// };
// release.AlternateNames = alt.ToDelimitedList();
// release.LastUpdated = now;
// }
// }
// context.SaveChanges();
// }
//}
//[Fact]
//public void Update_Artist_Special_Name()
//{
// var optionsBuilder = new DbContextOptionsBuilder<RoadieDbContext>();
// optionsBuilder.UseMySql("server=viking;userid=roadie;password=MenAtW0rk668;persistsecurityinfo=True;database=roadie;ConvertZeroDateTime=true");
// using (var context = new RoadieDbContext(optionsBuilder.Options))
// {
// var now = DateTime.UtcNow;
// foreach (var artist in context.Artists)
// {
// var artistModel = artist.Adapt<Roadie.Library.Models.Artist>();
// var specialArtistName = artist.Name.ToAlphanumericName();
// if (!artistModel.AlternateNamesList.Contains(specialArtistName, StringComparer.OrdinalIgnoreCase))
// {
// var alt = new List<string>(artistModel.AlternateNamesList)
// {
// specialArtistName
// };
// artist.AlternateNames = alt.ToDelimitedList();
// artist.LastUpdated = now;
// }
// }
// context.SaveChanges();
// }
//}
}
}

View file

@ -60,6 +60,7 @@ namespace Roadie.Library.Tests
[InlineData("folder.JPG")]
[InlineData("front.jpg")]
[InlineData("FrOnt.jpg")]
[InlineData("Art.jpg")]
[InlineData("Art - front.jpg")]
[InlineData("Art - Front.jpg")]
[InlineData("Art-Front.jpg")]
@ -70,6 +71,7 @@ namespace Roadie.Library.Tests
[InlineData("F-1.jpg")]
[InlineData("front_.jpg")]
[InlineData("BIG.JPg")]
[InlineData("bigart.JPg")]
[InlineData("BIG.PNG")]
public void Test_Should_Be_Release_Images(string input)
{
@ -191,6 +193,11 @@ namespace Roadie.Library.Tests
[InlineData("CD3.jpg")]
[InlineData("Scan-1.jpg")]
[InlineData("Scan-12.jpg")]
[InlineData("Scan 1.jpg")]
[InlineData("sc 1.jpg")]
[InlineData("sc01.jpg")]
[InlineData("sc-01.jpg")]
[InlineData("sc 01.jpg")]
[InlineData("cover_01.jpg")]
[InlineData("cover 03.jpg")]
[InlineData("cover 1.jpg")]

View file

@ -66,6 +66,24 @@ namespace Roadie.Library.Tests
Assert.Null(t);
}
[Theory]
[InlineData("Bob", "bob")]
[InlineData("Bob Marley", "bobmarley")]
[InlineData("Ringo Starr And His All-Starr Band", "ringostarrandhisallstarrband")]
[InlineData("Leslie & Tom", "leslieandtom")]
[InlineData(" Leslie & Tom", "leslieandtom")]
[InlineData("Leslie And Tom", "leslieandtom")]
[InlineData("Leslie Tom", "leslietom")]
[InlineData("Hüsker Dü", "huskerdu")]
[InlineData("Motörhead", "motorhead")] //
[InlineData("Alright, Still", "alrightstill")]
[InlineData("Something, SOMETHING & somEthing!", "somethingsomethingandsomething")]
public void ToAlphanumericNameShouldStripAndMatch(string input, string shouldBe)
{
var t = input.ToAlphanumericName();
Assert.Equal(shouldBe, t);
}
[Fact]
public void RemoveFirst()
{

View file

@ -24,7 +24,8 @@ namespace Roadie.Library.Configuration
public string ArtistRemoveStringsRegex { get; set; }
public string ReleaseRemoveStringsRegex { get; set; }
public string TrackRemoveStringsRegex { get; set; }
public string PreInspectScript { get; set; }
public string PostInspectScript { get; set; }
public List<ReplacementString> ReplaceStrings { get; set; }
public string UnknownFolder { get; set; }

View file

@ -183,8 +183,10 @@ namespace Roadie.Library.Engines
return (from a in this.DbContext.Artists
where (a.Name == searchName ||
a.Name == specialSearchName ||
a.SortName == searchName ||
a.SortName == searchSortName ||
a.SortName == specialSearchName ||
a.AlternateNames.StartsWith(searchNameStart) ||
a.AlternateNames.Contains(searchNameIn) ||
a.AlternateNames.EndsWith(searchNameEnd) ||

View file

@ -112,15 +112,16 @@ namespace Roadie.Library.Engines
var release = (from r in this.DbContext.Releases
where (r.ArtistId == artist.Id)
where (r.Title.ToLower() == searchName ||
r.AlternateNames.ToLower() == searchName ||
r.AlternateNames.ToLower() == specialSearchName ||
r.AlternateNames.ToLower().Contains(altStart) ||
r.AlternateNames.ToLower().Contains(altIn) ||
r.AlternateNames.ToLower().Contains(altEnds) ||
r.AlternateNames.ToLower().Contains(altStartSpecial) ||
r.AlternateNames.ToLower().Contains(altInSpecial) ||
r.AlternateNames.ToLower().Contains(altEndsSpecial))
where (r.Title == searchName ||
r.Title == specialSearchName ||
r.AlternateNames == searchName ||
r.AlternateNames == specialSearchName ||
r.AlternateNames.Contains(altStart) ||
r.AlternateNames.Contains(altIn) ||
r.AlternateNames.Contains(altEnds) ||
r.AlternateNames.Contains(altStartSpecial) ||
r.AlternateNames.Contains(altInSpecial) ||
r.AlternateNames.Contains(altEndsSpecial))
select r
).FirstOrDefault();

View file

@ -201,6 +201,21 @@ namespace Roadie.Library.Extensions
return input;
}
public static String RemoveDiacritics(this string s)
{
String normalizedString = s.Normalize(NormalizationForm.FormD);
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < normalizedString.Length; i++)
{
Char c = normalizedString[i];
if (CharUnicodeInfo.GetUnicodeCategory(c) != UnicodeCategory.NonSpacingMark)
{
stringBuilder.Append(c);
}
}
return stringBuilder.ToString();
}
public static string ToAlphanumericName(this string input)
{
if (string.IsNullOrEmpty(input))
@ -210,7 +225,7 @@ namespace Roadie.Library.Extensions
input = input.ToLower().Trim().Replace("&", "and");
char[] arr = input.ToCharArray();
arr = Array.FindAll<char>(arr, (c => (char.IsLetterOrDigit(c))));
return new string(arr);
return new string(arr).RemoveDiacritics();
}
public static string ToContentDispositionFriendly(this string input)

View file

@ -328,6 +328,7 @@ namespace Roadie.Library.Factories
existingReleaseMedia.TrackCount++;
existingReleaseMedia.LastUpdated = now;
releaseToMergeInto.TrackCount++;
mergedTracksToMove.Add(mergeTrack);
}
else
{

View file

@ -1,4 +1,5 @@
using Roadie.Library.Enums;
using Roadie.Library.Extensions;
using Roadie.Library.SearchEngines.Imaging;
using Roadie.Library.Utility;
using SixLabors.ImageSharp;
@ -156,7 +157,7 @@ namespace Roadie.Library.Imaging
{
return false;
}
return Regex.IsMatch(fileinfo.Name, @"((f[-_\s]*[0-9]*)|big|cover|cvr|folder|release|front[-_\s]*)\.(jpg|jpeg|png|bmp|gif)", RegexOptions.IgnoreCase);
return Regex.IsMatch(fileinfo.Name, @"((f[-_\s]*[0-9]*)|art|big[art]*|cover|cvr|folder|release|front[-_\s]*)\.(jpg|jpeg|png|bmp|gif)", RegexOptions.IgnoreCase);
}
public static bool IsReleaseSecondaryImage(FileInfo fileinfo)
@ -165,7 +166,7 @@ namespace Roadie.Library.Imaging
{
return false;
}
return Regex.IsMatch(fileinfo.Name, @"((img[\s-_]*[0-9]*[\s-_]*[0-9]*)|(book[let]*[#-_\s(]*[0-9]*-*[0-9]*(\))*)|(encartes[-_\s]*[(]*[0-9]*[)]*)|scan(.)?[0-9]*|matrix(.)?[0-9]*|(cover[\s_-]*[0-9]+)|back|traycard|jewel case|disc|(.*)[in]*side(.*)|in([side|lay|let|site])*[0-9]*|cd(.)?[0-9]*|(release[\s_-]+[0-9]+))\.(jpg|jpeg|png|bmp|gif)", RegexOptions.IgnoreCase);
return Regex.IsMatch(fileinfo.Name, @"((img[\s-_]*[0-9]*[\s-_]*[0-9]*)|(book[let]*[#-_\s(]*[0-9]*-*[0-9]*(\))*)|(encartes[-_\s]*[(]*[0-9]*[)]*)|sc[an]*(.)?[0-9]*|matrix(.)?[0-9]*|(cover[\s_-]*[0-9]+)|back|traycard|jewel case|disc|(.*)[in]*side(.*)|in([side|lay|let|site])*[0-9]*|cd(.)?[0-9]*|(release[\s_-]+[0-9]+))\.(jpg|jpeg|png|bmp|gif)", RegexOptions.IgnoreCase);
}
public static bool IsLabelImage(FileInfo fileinfo)
@ -191,10 +192,13 @@ namespace Roadie.Library.Imaging
}
if (imageFilesInFolder.Any())
{
name = name.ToAlphanumericName();
foreach (var imageFileInFolder in imageFilesInFolder)
{
var image = new FileInfo(imageFileInFolder);
if(image.Name.Equals(name, StringComparison.OrdinalIgnoreCase))
var filenameWithoutExtension = Path.GetFileName(imageFileInFolder).ToAlphanumericName();
var imageName = image.Name.ToAlphanumericName();
if(imageName.Equals(name) || filenameWithoutExtension.Equals(name))
{
result.Add(image);
}

View file

@ -18,6 +18,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Management.Automation;
namespace Roadie.Library.Inspect
{
@ -53,7 +54,7 @@ namespace Roadie.Library.Inspect
}
else
{
Console.WriteLine($"__ Not Loading Disabled Pluging [{ plugin.Description }]");
Console.WriteLine($"╠╣ Not Loading Disabled Plugin [{ plugin.Description }]");
}
}
}
@ -93,7 +94,7 @@ namespace Roadie.Library.Inspect
}
else
{
Console.WriteLine($"__ Not Loading Disabled Pluging [{ plugin.Description }]");
Console.WriteLine($"╠╣ Not Loading Disabled Plugin [{ plugin.Description }]");
}
}
}
@ -169,16 +170,27 @@ namespace Roadie.Library.Inspect
var artistsFound = new List<string>();
var releasesFound = new List<string>();
var mp3FilesFoundCount = 0;
Console.BackgroundColor = ConsoleColor.White;
Console.ForegroundColor = ConsoleColor.Blue;
Console.WriteLine($"✨ Inspector Start, UTC [{ DateTime.UtcNow.ToString("s") }]");
Console.ResetColor();
// Run PreInspect script
var scriptResult = RunScript(Configuration.Processing.PreInspectScript, doCopy, isReadOnly, directoryToInspect, destination);
if(!string.IsNullOrEmpty(scriptResult))
{
Console.BackgroundColor = ConsoleColor.Blue;
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine($"PreInspectScript Results: { System.Environment.NewLine + scriptResult + System.Environment.NewLine }");
Console.ResetColor();
}
// Create a new destination subfolder for each Inspector run by Current timestamp
var dest = Path.Combine(destination, DateTime.UtcNow.ToString("yyyyMMddHHmm"));
if (isReadOnly || dontAppendSubFolder)
{
dest = destination;
}
if (!isReadOnly && !Directory.Exists(dest))
{
Directory.CreateDirectory(dest);
}
// Get all the directorys in the directory
var directoryDirectories = Directory.GetDirectories(directoryToInspect, "*.*", SearchOption.AllDirectories);
var directories = new List<string>
@ -190,198 +202,205 @@ namespace Roadie.Library.Inspect
var inspectedImagesInDirectories = new List<string>();
try
{
var createdDestinationFolder = false;
foreach (var directory in directories.OrderBy(x => x))
{
var directoryInfo = new DirectoryInfo(directory);
var sw = Stopwatch.StartNew();
Console.ForegroundColor = ConsoleColor.Blue;
Console.WriteLine($"╔ ░▒▓ Inspecting [{ directory }] ▓▒░");
Console.WriteLine($"╔ 📂 Inspecting [{ directory }]");
Console.ResetColor();
Console.WriteLine("╠╦════════════════════════╣");
// Get all the MP3 files in 'directory'
var files = Directory.GetFiles(directory, "*.mp3", SearchOption.TopDirectoryOnly);
if (files == null || !files.Any())
if (files != null && files.Any())
{
continue;
}
// Run directory plugins against current directory
foreach (var plugin in DirectoryPlugins.Where(x => !x.IsPostProcessingPlugin).OrderBy(x => x.Order))
{
Console.WriteLine($"╠╬═ Running Directory Plugin { plugin.Description }");
var pluginResult = plugin.Process(directoryInfo);
if (!pluginResult.IsSuccess)
if (!isReadOnly && !createdDestinationFolder && !Directory.Exists(dest))
{
Console.WriteLine($"Plugin Failed: Error [{ JsonConvert.SerializeObject(pluginResult)}]");
return;
Directory.CreateDirectory(dest);
createdDestinationFolder = true;
}
else if (!string.IsNullOrEmpty(pluginResult.Data))
// Run directory plugins against current directory
foreach (var plugin in DirectoryPlugins.Where(x => !x.IsPostProcessingPlugin).OrderBy(x => x.Order))
{
Console.WriteLine($"╠╣ Directory Plugin Message: { pluginResult.Data }");
}
}
Console.WriteLine($"╠╝");
Console.WriteLine($"╟─ Found [{ files.Length }] mp3 Files");
List<AudioMetaData> fileMetaDatas = new List<AudioMetaData>();
List<FileInfo> fileInfos = new List<FileInfo>();
// Inspect the found MP3 files in 'directory'
foreach (var file in files)
{
mp3FilesFoundCount++;
var fileInfo = new FileInfo(file);
Console.ForegroundColor = ConsoleColor.DarkGreen;
Console.WriteLine($"╟─ Inspecting [{ fileInfo.FullName }]");
var tagLib = TagsHelper.MetaDataForFile(fileInfo.FullName, true);
Console.ForegroundColor = ConsoleColor.Cyan;
if (!tagLib?.IsSuccess ?? false)
{
Console.ForegroundColor = ConsoleColor.DarkYellow;
}
Console.WriteLine($"╟ (Pre ) : { tagLib.Data }");
Console.ResetColor();
tagLib.Data.Filename = fileInfo.FullName;
var originalMetaData = tagLib.Data.Adapt<AudioMetaData>();
var pluginMetaData = tagLib.Data;
// Run all file plugins against the MP3 file modifying the MetaData
foreach (var plugin in FilePlugins.OrderBy(x => x.Order))
{
Console.WriteLine($"╟┤ Running File Plugin { plugin.Description }");
OperationResult<AudioMetaData> pluginResult = null;
pluginResult = plugin.Process(pluginMetaData);
Console.WriteLine($"╠╬═ Running Directory Plugin { plugin.Description }");
var pluginResult = plugin.Process(directoryInfo);
if (!pluginResult.IsSuccess)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"Plugin Failed: Error [{ JsonConvert.SerializeObject(pluginResult)}]");
Console.ResetColor();
Console.WriteLine($"📛 Plugin Failed: Error [{ JsonConvert.SerializeObject(pluginResult)}]");
return;
}
pluginMetaData = pluginResult.Data;
}
// See if the MetaData from the Plugins is different from the original
if (originalMetaData != null && pluginMetaData != null)
{
var differences = AutoCompare.Comparer.Compare(originalMetaData, pluginMetaData);
if (differences.Any())
else if (!string.IsNullOrEmpty(pluginResult.Data))
{
var skipDifferences = new List<string> { "AudioMetaDataWeights", "FileInfo", "Images", "TrackArtists", "ValidWeight" };
var differencesDescription = $"{ System.Environment.NewLine }";
foreach (var difference in differences)
{
if (skipDifferences.Contains(difference.Name))
{
continue;
}
differencesDescription += $"╟ || { difference.Name } : Was [{ difference.OldValue}] Now [{ difference.NewValue}]{ System.Environment.NewLine }";
}
Console.Write($"╟ ≡ != ID3 Tag Modified: { differencesDescription }");
if (!isReadOnly)
{
TagsHelper.WriteTags(pluginMetaData, pluginMetaData.Filename);
}
else
{
Console.WriteLine("╟ ■ Read Only Mode: Not Modifying File ID3 Tags.");
}
}
else
{
Console.WriteLine($"╟ ≡ == ID3 Tag NOT Modified");
Console.WriteLine($"╠╣ Directory Plugin Message: { pluginResult.Data }");
}
}
else
{
var oBad = originalMetaData == null;
var pBad = pluginMetaData == null;
Console.WriteLine($"╟ !! MetaData comparison skipped. { (oBad ? "Pre MetaData is Invalid" : "")} { (pBad ? "Post MetaData is Invalid" : "") }");
}
if (!pluginMetaData.IsValid)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"╟ ■■ INVALID: Missing: { ID3TagsHelper.DetermineMissingRequiredMetaData(pluginMetaData) }");
Console.ResetColor();
}
else
Console.WriteLine($"╠╝");
Console.WriteLine($"╟─ Found [{ files.Length }] mp3 Files");
List<AudioMetaData> fileMetaDatas = new List<AudioMetaData>();
List<FileInfo> fileInfos = new List<FileInfo>();
// Inspect the found MP3 files in 'directory'
foreach (var file in files)
{
mp3FilesFoundCount++;
var fileInfo = new FileInfo(file);
Console.ForegroundColor = ConsoleColor.DarkGreen;
Console.WriteLine($"╟─ 🎵 Inspecting [{ fileInfo.FullName }]");
var tagLib = TagsHelper.MetaDataForFile(fileInfo.FullName, true);
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine($"╟ (Post) : { pluginMetaData }");
if (!tagLib?.IsSuccess ?? false)
{
Console.ForegroundColor = ConsoleColor.DarkYellow;
}
Console.WriteLine($"╟ (Pre ) : { tagLib.Data }");
Console.ResetColor();
var artistToken = ArtistInspectorToken(tagLib.Data);
if (!artistsFound.Contains(artistToken))
tagLib.Data.Filename = fileInfo.FullName;
var originalMetaData = tagLib.Data.Adapt<AudioMetaData>();
var pluginMetaData = tagLib.Data;
// Run all file plugins against the MP3 file modifying the MetaData
foreach (var plugin in FilePlugins.OrderBy(x => x.Order))
{
artistsFound.Add(artistToken);
}
var releaseToken = ReleaseInspectorToken(tagLib.Data);
if (!releasesFound.Contains(releaseToken))
{
releasesFound.Add(releaseToken);
}
var newFileName = $"CD{ (tagLib.Data.Disc ?? ID3TagsHelper.DetermineDiscNumber(tagLib.Data)).ToString("000") }_{ tagLib.Data.TrackNumber.Value.ToString("0000") }.mp3";
// Artist sub folder is created to hold Releases for Artist and Artist Images
var artistSubDirectory = directory == dest ? fileInfo.DirectoryName : Path.Combine(dest, artistToken);
// Each release is put into a subfolder into the current run Inspector folder to hold MP3 Files and Release Images
var subDirectory = directory == dest ? fileInfo.DirectoryName : Path.Combine(dest, artistToken, releaseToken);
if (!isReadOnly && !Directory.Exists(subDirectory))
{
Directory.CreateDirectory(subDirectory);
}
// Inspect images
if (!inspectedImagesInDirectories.Contains(directoryInfo.FullName))
{
// Get all artist images and move to artist folder
var foundArtistImages = new List<FileInfo>();
foundArtistImages.AddRange(ImageHelper.FindImagesByName(directoryInfo.Parent, tagLib.Data.Artist, SearchOption.TopDirectoryOnly));
foundArtistImages.AddRange(ImageHelper.FindImageTypeInDirectory(directoryInfo.Parent, Enums.ImageType.Artist, SearchOption.TopDirectoryOnly));
foundArtistImages.AddRange(ImageHelper.FindImageTypeInDirectory(directoryInfo.Parent, Enums.ImageType.ArtistSecondary, SearchOption.TopDirectoryOnly));
foundArtistImages.AddRange(ImageHelper.FindImageTypeInDirectory(directoryInfo, Enums.ImageType.Artist, SearchOption.TopDirectoryOnly));
foundArtistImages.AddRange(ImageHelper.FindImageTypeInDirectory(directoryInfo, Enums.ImageType.ArtistSecondary, SearchOption.TopDirectoryOnly));
foreach (var artistImage in foundArtistImages)
Console.WriteLine($"╟┤ Running File Plugin { plugin.Description }");
OperationResult<AudioMetaData> pluginResult = null;
pluginResult = plugin.Process(pluginMetaData);
if (!pluginResult.IsSuccess)
{
InspectImage(isReadOnly, doCopy, dest, artistSubDirectory, artistImage);
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"📛 Plugin Failed: Error [{ JsonConvert.SerializeObject(pluginResult)}]");
Console.ResetColor();
return;
}
// Get all release images and move to release folder
var foundReleaseImages = new List<FileInfo>();
foundReleaseImages.AddRange(ImageHelper.FindImagesByName(directoryInfo, tagLib.Data.Release, SearchOption.AllDirectories));
foundReleaseImages.AddRange(ImageHelper.FindImageTypeInDirectory(directoryInfo, Enums.ImageType.Release, SearchOption.AllDirectories));
foundReleaseImages.AddRange(ImageHelper.FindImageTypeInDirectory(directoryInfo, Enums.ImageType.ReleaseSecondary, SearchOption.AllDirectories));
foreach (var foundReleaseImage in foundReleaseImages)
{
InspectImage(isReadOnly, doCopy, dest, subDirectory, foundReleaseImage);
}
inspectedImagesInDirectories.Add(directoryInfo.FullName);
pluginMetaData = pluginResult.Data;
}
// If enabled move MP3 to new folder
var newPath = Path.Combine(dest, subDirectory, newFileName.ToFileNameFriendly());
if (isReadOnly)
// See if the MetaData from the Plugins is different from the original
if (originalMetaData != null && pluginMetaData != null)
{
Console.WriteLine($"╟ ■ Read Only Mode: File would be [{ (doCopy ? "Copied" : "Moved") }] to [{ newPath }]");
}
else
{
if (!doCopy)
var differences = AutoCompare.Comparer.Compare(originalMetaData, pluginMetaData);
if (differences.Any())
{
if (fileInfo.FullName != newPath)
var skipDifferences = new List<string> { "AudioMetaDataWeights", "FileInfo", "Images", "TrackArtists", "ValidWeight" };
var differencesDescription = $"{ System.Environment.NewLine }";
foreach (var difference in differences)
{
if (File.Exists(newPath))
if (skipDifferences.Contains(difference.Name))
{
File.Delete(newPath);
continue;
}
fileInfo.MoveTo(newPath);
differencesDescription += $"╟ || { difference.Name } : Was [{ difference.OldValue}] Now [{ difference.NewValue}]{ System.Environment.NewLine }";
}
Console.Write($"╟ ≡ != ID3 Tag Modified: { differencesDescription }");
if (!isReadOnly)
{
TagsHelper.WriteTags(pluginMetaData, pluginMetaData.Filename);
}
else
{
Console.WriteLine("╟ 🔒 Read Only Mode: Not Modifying File ID3 Tags.");
}
}
else
{
fileInfo.CopyTo(newPath, true);
Console.WriteLine($"╟ ≡ == ID3 Tag NOT Modified");
}
Console.ForegroundColor = ConsoleColor.DarkYellow;
Console.WriteLine($"╠═» { (doCopy ? "Copied" : "Moved")} MP3 File to [{ newPath }]");
}
else
{
var oBad = originalMetaData == null;
var pBad = pluginMetaData == null;
Console.WriteLine($"╟ !! MetaData comparison skipped. { (oBad ? "Pre MetaData is Invalid" : "")} { (pBad ? "Post MetaData is Invalid" : "") }");
}
if (!pluginMetaData.IsValid)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"╟ ❗ INVALID: Missing: { ID3TagsHelper.DetermineMissingRequiredMetaData(pluginMetaData) }");
Console.ResetColor();
}
Console.WriteLine("╠════════════════════════╣");
else
{
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine($"╟ (Post) : { pluginMetaData }");
Console.ResetColor();
var artistToken = ArtistInspectorToken(tagLib.Data);
if (!artistsFound.Contains(artistToken))
{
artistsFound.Add(artistToken);
}
var releaseToken = ReleaseInspectorToken(tagLib.Data);
if (!releasesFound.Contains(releaseToken))
{
releasesFound.Add(releaseToken);
}
var newFileName = $"CD{ (tagLib.Data.Disc ?? ID3TagsHelper.DetermineDiscNumber(tagLib.Data)).ToString("000") }_{ tagLib.Data.TrackNumber.Value.ToString("0000") }.mp3";
// Artist sub folder is created to hold Releases for Artist and Artist Images
var artistSubDirectory = directory == dest ? fileInfo.DirectoryName : Path.Combine(dest, artistToken);
// Each release is put into a subfolder into the current run Inspector folder to hold MP3 Files and Release Images
var subDirectory = directory == dest ? fileInfo.DirectoryName : Path.Combine(dest, artistToken, releaseToken);
if (!isReadOnly && !Directory.Exists(subDirectory))
{
Directory.CreateDirectory(subDirectory);
}
// Inspect images
if (!inspectedImagesInDirectories.Contains(directoryInfo.FullName))
{
// Get all artist images and move to artist folder
var foundArtistImages = new List<FileInfo>();
foundArtistImages.AddRange(ImageHelper.FindImagesByName(directoryInfo, tagLib.Data.Artist, SearchOption.TopDirectoryOnly));
foundArtistImages.AddRange(ImageHelper.FindImagesByName(directoryInfo.Parent, tagLib.Data.Artist, SearchOption.TopDirectoryOnly));
foundArtistImages.AddRange(ImageHelper.FindImageTypeInDirectory(directoryInfo.Parent, Enums.ImageType.Artist, SearchOption.TopDirectoryOnly));
foundArtistImages.AddRange(ImageHelper.FindImageTypeInDirectory(directoryInfo.Parent, Enums.ImageType.ArtistSecondary, SearchOption.TopDirectoryOnly));
foundArtistImages.AddRange(ImageHelper.FindImageTypeInDirectory(directoryInfo, Enums.ImageType.Artist, SearchOption.TopDirectoryOnly));
foundArtistImages.AddRange(ImageHelper.FindImageTypeInDirectory(directoryInfo, Enums.ImageType.ArtistSecondary, SearchOption.TopDirectoryOnly));
foreach (var artistImage in foundArtistImages)
{
InspectImage(isReadOnly, doCopy, dest, artistSubDirectory, artistImage);
}
// Get all release images and move to release folder
var foundReleaseImages = new List<FileInfo>();
foundReleaseImages.AddRange(ImageHelper.FindImagesByName(directoryInfo, tagLib.Data.Release, SearchOption.AllDirectories));
foundReleaseImages.AddRange(ImageHelper.FindImageTypeInDirectory(directoryInfo, Enums.ImageType.Release, SearchOption.AllDirectories));
foundReleaseImages.AddRange(ImageHelper.FindImageTypeInDirectory(directoryInfo, Enums.ImageType.ReleaseSecondary, SearchOption.AllDirectories));
foreach (var foundReleaseImage in foundReleaseImages)
{
InspectImage(isReadOnly, doCopy, dest, subDirectory, foundReleaseImage);
}
inspectedImagesInDirectories.Add(directoryInfo.FullName);
}
// If enabled move MP3 to new folder
var newPath = Path.Combine(dest, subDirectory, newFileName.ToFileNameFriendly());
if (isReadOnly)
{
Console.WriteLine($"╟ 🔒 Read Only Mode: File would be [{ (doCopy ? "Copied" : "Moved") }] to [{ newPath }]");
}
else
{
if (!doCopy)
{
if (fileInfo.FullName != newPath)
{
if (File.Exists(newPath))
{
File.Delete(newPath);
}
fileInfo.MoveTo(newPath);
}
}
else
{
fileInfo.CopyTo(newPath, true);
}
Console.ForegroundColor = ConsoleColor.DarkYellow;
Console.WriteLine($"╠═ 🚛 { (doCopy ? "Copied" : "Moved")} MP3 File to [{ newPath }]");
Console.ResetColor();
}
Console.WriteLine("╠════════════════════════╣");
}
}
}
// Run post-processing directory plugins against current directory
@ -391,7 +410,7 @@ namespace Roadie.Library.Inspect
var pluginResult = plugin.Process(directoryInfo);
if (!pluginResult.IsSuccess)
{
Console.WriteLine($"Plugin Failed: Error [{ JsonConvert.SerializeObject(pluginResult)}]");
Console.WriteLine($"📛 Plugin Failed: Error [{ JsonConvert.SerializeObject(pluginResult)}]");
return;
}
else if (!string.IsNullOrEmpty(pluginResult.Data))
@ -407,20 +426,56 @@ namespace Roadie.Library.Inspect
catch (Exception ex)
{
Logger.LogError(ex);
Console.WriteLine("!! Exception: " + ex.ToString());
Console.WriteLine("📛 Exception: " + ex.ToString());
}
if (!dontDeleteEmptyFolders)
{
var delEmptyFolderIn = new DirectoryInfo(directoryToInspect);
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine($"X Deleting Empty folders in [{ delEmptyFolderIn.FullName }]");
Console.WriteLine($" Deleting Empty folders in [{ delEmptyFolderIn.FullName }]");
Console.ResetColor();
FolderPathHelper.DeleteEmptyDirs(directoryToInspect, false);
}
else
{
Console.WriteLine("X ■ Read Only Mode: Not deleting empty folders.");
Console.WriteLine("🔒 Read Only Mode: Not deleting empty folders.");
}
// Run PreInspect script
scriptResult = RunScript(Configuration.Processing.PostInspectScript, doCopy, isReadOnly, directoryToInspect, destination);
if (!string.IsNullOrEmpty(scriptResult))
{
Console.BackgroundColor = ConsoleColor.Blue;
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine($"PostInspectScript Results: { Environment.NewLine + scriptResult + Environment.NewLine }");
Console.ResetColor();
}
}
private string RunScript(string scriptFilename, bool doCopy, bool isReadOnly, string directoryToInspect, string dest)
{
if(string.IsNullOrEmpty(scriptFilename))
{
return null;
}
try
{
var script = File.ReadAllText(scriptFilename);
using (var ps = PowerShell.Create())
{
var r = "";
var results = ps.AddScript(script).Invoke();
foreach (var result in results)
{
r += result.ToString() + System.Environment.NewLine;
}
return r;
}
}
catch (Exception ex)
{
Console.WriteLine($"📛 Error with Script File [{scriptFilename}], Error [{ ex.ToString() }] ");
}
return null;
}
private void InspectImage(bool isReadOnly, bool doCopy, string dest, string subdirectory, FileInfo image)
@ -433,7 +488,13 @@ namespace Roadie.Library.Inspect
return;
}
Console.WriteLine($"╟─ Inspecting Image [{ image.FullName }]");
var newImageFolder = new DirectoryInfo(Path.Combine(dest, subdirectory));
if(!newImageFolder.Exists)
{
newImageFolder.Create();
}
var newImagePath = Path.Combine(dest, subdirectory, image.Name);
if (image.FullName != newImagePath)
{
var looper = 0;
@ -444,25 +505,60 @@ namespace Roadie.Library.Inspect
}
if (isReadOnly)
{
Console.WriteLine($"╟ Read Only Mode: Would be [{ (doCopy ? "Copied" : "Moved") }] to [{ newImagePath }]");
Console.WriteLine($"╟ 🔒 Read Only Mode: Would be [{ (doCopy ? "Copied" : "Moved") }] to [{ newImagePath }]");
}
else
{
if (!doCopy)
try
{
image.MoveTo(newImagePath);
if (!doCopy)
{
image.MoveTo(newImagePath);
}
else
{
image.CopyTo(newImagePath, true);
}
Console.ForegroundColor = ConsoleColor.DarkYellow;
Console.WriteLine($"╠═ 🚛 { (doCopy ? "Copied" : "Moved")} Image File to [{ newImagePath }]");
}
else
catch (Exception ex)
{
image.CopyTo(newImagePath, true);
Logger.LogError(ex);
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"📛 Error file [{ image.FullName }], newImagePath [{ newImagePath }], Exception: [{ ex.ToString() }]");
}
Console.ForegroundColor = ConsoleColor.DarkYellow;
Console.WriteLine($"╠═» { (doCopy ? "Copied" : "Moved")} Image File to [{ newImagePath }]");
}
}
Console.ResetColor();
}
private void MessageLogger_Messages(object sender, EventMessage e) => Console.WriteLine($"Log Level [{ e.Level }] Log Message [{ e.Message }] ");
private void MessageLogger_Messages(object sender, EventMessage e)
{
Console.WriteLine($"Log Level [{ e.Level }] Log Message [{ e.Message }] ");
var message = e.Message;
switch (e.Level)
{
case LogLevel.Trace:
this.Logger.LogTrace(message);
break;
case LogLevel.Debug:
this.Logger.LogDebug(message);
break;
case LogLevel.Information:
this.Logger.LogInformation(message);
break;
case LogLevel.Warning:
this.Logger.LogWarning(message);
break;
case LogLevel.Critical:
this.Logger.LogCritical(message);
break;
}
}
}
}

View file

@ -5,6 +5,7 @@ using Roadie.Library.Utility;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Runtime.Serialization;
namespace Roadie.Library.Models
@ -23,13 +24,12 @@ namespace Roadie.Library.Models
{
if (this._alternateNamesList == null)
{
if (string.IsNullOrEmpty(this.AlternateNames))
if (!string.IsNullOrEmpty(this.AlternateNames))
{
return null;
this._alternateNamesList = this.AlternateNames.Split('|');
}
return this.AlternateNames.Split('|');
}
return this._alternateNamesList;
return this._alternateNamesList ?? Enumerable.Empty<string>();
}
set
{

View file

@ -1,4 +1,6 @@
using System;
using Roadie.Library.Enums;
using Roadie.Library.Utility;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@ -98,7 +100,14 @@ namespace Roadie.Library.Models.Pagination
return result.ToString();
}
public bool? FilterOnlyMissing { get; set; }
public short? FilterToStatus { get; set; }
public Statuses FilterToStatusValue
{
get
{
return SafeParser.ToEnum<Statuses>(FilterToStatus);
}
}
public Guid? FilterToArtistId { get; set; }
public Guid? FilterToReleaseId { get; set; }

View file

@ -11,17 +11,18 @@
<PackageReference Include="AutoCompare.Core" Version="1.0.0" />
<PackageReference Include="CsvHelper" Version="12.1.2" />
<PackageReference Include="EFCore.BulkExtensions" Version="2.4.7" />
<PackageReference Include="FluentFTP" Version="25.0.1" />
<PackageReference Include="FluentFTP" Version="25.0.3" />
<PackageReference Include="Hashids.net" Version="1.2.2" />
<PackageReference Include="HtmlAgilityPack" Version="1.11.7" />
<PackageReference Include="IdSharp.Common" Version="1.0.1" />
<PackageReference Include="IdSharp.Tagging" Version="1.0.0-rc3" />
<PackageReference Include="Inflatable.Lastfm" Version="1.1.0.339" />
<PackageReference Include="Mapster" Version="4.0.0" />
<PackageReference Include="Mapster" Version="4.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="2.2.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.2.4" />
<PackageReference Include="Microsoft.Extensions.Caching.Redis" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.2.0" />
<PackageReference Include="Microsoft.PowerShell.5.ReferenceAssemblies" Version="1.1.0" />
<PackageReference Include="MimeMapping" Version="1.0.1.12" />
<PackageReference Include="NodaTime" Version="2.4.5" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="2.2.0" />

View file

@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Identity;
using Mapster;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.SignalR;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
@ -484,31 +485,37 @@ namespace Roadie.Api.Services
});
}
public async Task<OperationResult<bool>> ScanAllCollections(ApplicationUser user, bool isReadOnly = false, bool doPurgeFirst = true)
public async Task<OperationResult<bool>> ScanAllCollections(ApplicationUser user, bool isReadOnly = false, bool doPurgeFirst = false)
{
var sw = new Stopwatch();
sw.Start();
var errors = new List<Exception>();
var collections = this.DbContext.Collections.Where(x => x.Status != Statuses.Complete).ToArray();
var collections = this.DbContext.Collections.Where(x => x.IsLocked == false).ToArray();
var updatedReleaseIds = new List<int>();
foreach (var collection in collections)
{
try
{
var result = await this.ScanCollection(user, collection.RoadieId, isReadOnly, doPurgeFirst);
var result = await this.ScanCollection(user, collection.RoadieId, isReadOnly, doPurgeFirst, false);
if (!result.IsSuccess)
{
errors.AddRange(result.Errors);
}
updatedReleaseIds.AddRange((int[])result.AdditionalData["updatedReleaseIds"]);
}
catch (Exception ex)
{
this.Logger.LogError(ex);
await this.LogAndPublish(ex.ToString(), LogLevel.Error);
errors.Add(ex);
}
}
foreach (var updatedReleaseId in updatedReleaseIds.Distinct())
{
await this.UpdateReleaseRank(updatedReleaseId);
}
sw.Stop();
this.Logger.LogInformation(string.Format("ScanAllCollections, By User `{0}`", user));
await this.LogAndPublish($"ScanAllCollections, By User `{user}`, Updated Release Count [{ updatedReleaseIds.Distinct().Count() }], ElapsedTime [{ sw.ElapsedMilliseconds}]", LogLevel.Information);
return new OperationResult<bool>
{
IsSuccess = !errors.Any(),
@ -561,12 +568,13 @@ namespace Roadie.Api.Services
};
}
public async Task<OperationResult<bool>> ScanCollection(ApplicationUser user, Guid collectionId, bool isReadOnly = false, bool doPurgeFirst = true)
public async Task<OperationResult<bool>> ScanCollection(ApplicationUser user, Guid collectionId, bool isReadOnly = false, bool doPurgeFirst = false, bool doUpdateRanks = true)
{
var sw = new Stopwatch();
sw.Start();
CollectionRelease[] crs = new CollectionRelease[0];
var releaseIdsInCollection = new List<int>();
var updatedReleaseIds = new List<int>();
var result = new List<PositionArtistRelease>();
var errors = new List<Exception>();
var collection = this.DbContext.Collections.FirstOrDefault(x => x.RoadieId == collectionId);
@ -579,7 +587,8 @@ namespace Roadie.Api.Services
{
if (doPurgeFirst)
{
crs = this.DbContext.CollectionReleases.Where(x => x.CollectionId == collection.Id).ToArray();
await this.LogAndPublish($"ScanCollection Purgeing Collection [{ collectionId}]", LogLevel.Warning);
var crs = this.DbContext.CollectionReleases.Where(x => x.CollectionId == collection.Id).ToArray();
this.DbContext.CollectionReleases.RemoveRange(crs);
await this.DbContext.SaveChangesAsync();
}
@ -603,7 +612,7 @@ namespace Roadie.Api.Services
select a).ToArray();
if (!artistResults.Any())
{
this.Logger.LogWarning("Unable To Find Artist [{0}], SearchName [{1}]", csvRelease.Artist, searchName);
await this.LogAndPublish($"Unable To Find Artist [{csvRelease.Artist }], SearchName [{ searchName}]", LogLevel.Warning);
csvRelease.Status = Library.Enums.Statuses.Missing;
continue;
}
@ -626,13 +635,14 @@ namespace Roadie.Api.Services
}
if (release == null)
{
this.Logger.LogWarning("Unable To Find Release [{0}], SearchName [{1}]", csvRelease.Release, searchName);
await this.LogAndPublish($"Unable To Find Release [{csvRelease.Release}] for Artist [{csvRelease.Artist}], SearchName [{searchName}]", LogLevel.Warning );
csvRelease.Status = Library.Enums.Statuses.Missing;
continue;
}
var isInCollection = this.DbContext.CollectionReleases.FirstOrDefault(x => x.CollectionId == collection.Id &&
x.ListNumber == csvRelease.Position &&
x.ReleaseId == release.Id);
var updated = false;
// Found in Database but not in collection add to Collection
if (isInCollection == null)
{
@ -642,13 +652,20 @@ namespace Roadie.Api.Services
ReleaseId = release.Id,
ListNumber = csvRelease.Position,
});
updated = true;
}
// If Item in Collection is at different List number update CollectionRelease
else if (isInCollection.ListNumber != csvRelease.Position)
{
isInCollection.LastUpdated = now;
isInCollection.ListNumber = csvRelease.Position;
updated = true;
}
if(updated && !updatedReleaseIds.Any(x => x == release.Id))
{
updatedReleaseIds.Add(release.Id);
}
releaseIdsInCollection.Add(release.Id);
}
collection.LastUpdated = now;
await this.DbContext.SaveChangesAsync();
@ -669,10 +686,22 @@ namespace Roadie.Api.Services
{
collection.Status = Statuses.Incomplete;
}
await this.DbContext.SaveChangesAsync();
foreach (var cr in crs)
var collectionReleasesToRemove = (from cr in this.DbContext.CollectionReleases
where cr.CollectionId == collection.Id
where !releaseIdsInCollection.Contains(cr.ReleaseId)
select cr).ToArray();
if (collectionReleasesToRemove.Any())
{
await this.UpdateReleaseRank(cr.ReleaseId);
await this.LogAndPublish($"Removing [{ collectionReleasesToRemove.Count() }] Stale Release Records from Collection.", LogLevel.Information);
this.DbContext.CollectionReleases.RemoveRange(collectionReleasesToRemove);
}
await this.DbContext.SaveChangesAsync();
if (doUpdateRanks)
{
foreach(var updatedReleaseId in updatedReleaseIds)
{
await this.UpdateReleaseRank(updatedReleaseId);
}
}
this.CacheManager.ClearRegion(collection.CacheRegion);
}
@ -687,6 +716,7 @@ namespace Roadie.Api.Services
return new OperationResult<bool>
{
AdditionalData = new Dictionary<string, object> { { "updatedReleaseIds", updatedReleaseIds.ToArray() } },
IsSuccess = !errors.Any(),
Data = true,
OperationTime = sw.ElapsedMilliseconds,

View file

@ -147,7 +147,7 @@ namespace Roadie.Api.Services
var sw = new Stopwatch();
sw.Start();
int[] favoriteArtistIds = new int[0];
IQueryable<int> favoriteArtistIds = null;
if (request.FilterFavoriteOnly)
{
favoriteArtistIds = (from a in this.DbContext.Artists
@ -155,9 +155,9 @@ namespace Roadie.Api.Services
where ua.IsFavorite ?? false
where (roadieUser == null || ua.UserId == roadieUser.Id)
select a.Id
).ToArray();
);
}
int[] labelArtistIds = new int[0];
IQueryable<int> labelArtistIds = null;
if (request.FilterToLabelId.HasValue)
{
labelArtistIds = (from l in this.DbContext.Labels
@ -165,10 +165,9 @@ namespace Roadie.Api.Services
join r in this.DbContext.Releases on rl.ReleaseId equals r.Id
where l.RoadieId == request.FilterToLabelId
select r.ArtistId)
.Distinct()
.ToArray();
.Distinct();
}
int[] genreArtistIds = new int[0];
IQueryable<int> genreArtistIds = null;
var isFilteredToGenre = false;
if (!string.IsNullOrEmpty(request.Filter) && request.Filter.StartsWith(":genre", StringComparison.OrdinalIgnoreCase))
{
@ -177,8 +176,7 @@ namespace Roadie.Api.Services
join g in this.DbContext.Genres on ag.GenreId equals g.Id
where g.Name.Contains(genreFilter)
select ag.ArtistId)
.Distinct()
.ToArray();
.Distinct();
isFilteredToGenre = true;
request.Filter = null;
}
@ -278,14 +276,17 @@ namespace Roadie.Api.Services
sw.Stop();
if (!string.IsNullOrEmpty(request.Filter) && rowCount == 0)
{
// Create request for no artist found
var req = new data.Request
if (Configuration.RecordNoResultSearches)
{
UserId = roadieUser?.Id,
Description = request.Filter
};
this.DbContext.Requests.Add(req);
await this.DbContext.SaveChangesAsync();
// Create request for no artist found
var req = new data.Request
{
UserId = roadieUser?.Id,
Description = request.Filter
};
this.DbContext.Requests.Add(req);
await this.DbContext.SaveChangesAsync();
}
}
return new Library.Models.Pagination.PagedResult<ArtistList>
{
@ -465,7 +466,13 @@ namespace Roadie.Api.Services
{
var now = DateTime.UtcNow;
var originalArtistFolder = artist.ArtistFileFolder(this.Configuration, this.Configuration.LibraryFolder);
artist.AlternateNames = model.AlternateNamesList.ToDelimitedList();
var specialArtistName = model.Name.ToAlphanumericName();
var alt = new List<string>(model.AlternateNamesList);
if (!model.AlternateNamesList.Contains(specialArtistName, StringComparer.OrdinalIgnoreCase))
{
alt.Add(specialArtistName);
}
artist.AlternateNames = alt.ToDelimitedList();
artist.ArtistType = model.ArtistType;
artist.AmgId = model.AmgId;
artist.BeginDate = model.BeginDate;

View file

@ -170,12 +170,17 @@ namespace Roadie.Api.Services
}
var result = (from c in collections
where (request.FilterValue.Length == 0 || (request.FilterValue.Length > 0 && c.Name.Contains(request.Filter)))
where (request.FilterToStatusValue == Statuses.Ok || (c.Status == request.FilterToStatusValue))
select CollectionList.FromDataCollection(c, (from crc in this.DbContext.CollectionReleases
where crc.CollectionId == c.Id
select crc.Id).Count(), this.MakeCollectionThumbnailImage(c.RoadieId)));
var sortBy = string.IsNullOrEmpty(request.Sort) ? request.OrderValue(new Dictionary<string, string> { { "Collection.Text", "ASC" } }) : request.OrderValue(null);
var rowCount = result.Count();
var rows = result.OrderBy(sortBy).Skip(request.SkipValue).Take(request.LimitValue).ToArray();
if (request.FilterToStatusValue == Statuses.Incomplete)
{
rows = rows.OrderByDescending(x => x.PercentComplete).ThenBy(x => x.SortName).ToArray();
}
sw.Stop();
return Task.FromResult(new Library.Models.Pagination.PagedResult<CollectionList>
{

View file

@ -27,11 +27,11 @@ namespace Roadie.Api.Services
Task<OperationResult<Dictionary<string, List<string>>>> MissingCollectionReleases(ApplicationUser user);
Task<OperationResult<bool>> ScanAllCollections(ApplicationUser user, bool isReadOnly = false, bool doPurgeFirst = true);
Task<OperationResult<bool>> ScanAllCollections(ApplicationUser user, bool isReadOnly = false, bool doPurgeFirst = false);
Task<OperationResult<bool>> ScanArtist(ApplicationUser user, Guid artistId, bool isReadOnly = false);
Task<OperationResult<bool>> ScanCollection(ApplicationUser user, Guid collectionId, bool isReadOnly = false, bool doPurgeFirst = true);
Task<OperationResult<bool>> ScanCollection(ApplicationUser user, Guid collectionId, bool isReadOnly = false, bool doPurgeFirst = false, bool doUpdateRanks = true);
Task<OperationResult<bool>> ScanInboundFolder(ApplicationUser user, bool isReadOnly = false);

View file

@ -169,7 +169,7 @@ namespace Roadie.Api.Services
var sw = new Stopwatch();
sw.Start();
IEnumerable<int> collectionReleaseIds = null;
IQueryable<int> collectionReleaseIds = null;
if (request.FilterToCollectionId.HasValue)
{
collectionReleaseIds = (from cr in this.DbContext.CollectionReleases
@ -177,9 +177,9 @@ namespace Roadie.Api.Services
join r in this.DbContext.Releases on cr.ReleaseId equals r.Id
where c.RoadieId == request.FilterToCollectionId.Value
orderby cr.ListNumber
select r.Id).ToArray();
select r.Id);
}
int[] favoriteReleaseIds = new int[0];
IQueryable<int> favoriteReleaseIds = null;
if (request.FilterFavoriteOnly)
{
favoriteReleaseIds = (from a in this.DbContext.Releases
@ -187,9 +187,9 @@ namespace Roadie.Api.Services
where ur.IsFavorite ?? false
where (roadieUser == null || ur.UserId == roadieUser.Id)
select a.Id
).ToArray();
);
}
int[] genreReleaseIds = new int[0];
IQueryable<int> genreReleaseIds = null;
var isFilteredToGenre = false;
if (!string.IsNullOrEmpty(request.FilterByGenre) || (!string.IsNullOrEmpty(request.Filter) && request.Filter.StartsWith(":genre", StringComparison.OrdinalIgnoreCase)))
{
@ -198,8 +198,7 @@ namespace Roadie.Api.Services
join g in this.DbContext.Genres on rg.GenreId equals g.Id
where g.Name.Contains(genreFilter)
select rg.ReleaseId)
.Distinct()
.ToArray();
.Distinct();
request.Filter = null;
isFilteredToGenre = true;
}
@ -384,6 +383,7 @@ namespace Roadie.Api.Services
{
Text = par.Release
},
Status = Statuses.Missing,
CssClass = "missing",
ArtistThumbnail = new Library.Models.Image($"{this.HttpContext.ImageBaseUrl }/unknown.jpg"),
Thumbnail = new Library.Models.Image($"{this.HttpContext.ImageBaseUrl }/unknown.jpg"),
@ -392,6 +392,10 @@ namespace Roadie.Api.Services
}
}
// Resort the list for the collection by listNumber
if (request.FilterToStatusValue != Statuses.Ok)
{
newRows = newRows.Where(x => x.Status == request.FilterToStatusValue).ToList();
}
rows = newRows.OrderBy(x => x.ListNumber).Skip(request.SkipValue).Take(request.LimitValue).ToArray();
rowCount = collection.CollectionCount;
}
@ -614,7 +618,13 @@ namespace Roadie.Api.Services
release.IsVirtual = model.IsVirtual;
release.Status = SafeParser.ToEnum<Statuses>(model.Status);
release.Title = model.Title;
release.AlternateNames = model.AlternateNamesList.ToDelimitedList();
var specialReleaseTitle = model.Title.ToAlphanumericName();
var alt = new List<string>(model.AlternateNamesList);
if (!model.AlternateNamesList.Contains(specialReleaseTitle, StringComparer.OrdinalIgnoreCase))
{
alt.Add(specialReleaseTitle);
}
release.AlternateNames = alt.ToDelimitedList();
release.ReleaseDate = model.ReleaseDate;
release.Rating = model.Rating;
release.TrackCount = model.TrackCount;
@ -682,7 +692,6 @@ namespace Roadie.Api.Services
}
}
if (model.Genres != null && model.Genres.Any())
{
// Remove existing Genres not in model list

View file

@ -10,7 +10,7 @@
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="2.2.5" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="5.4.0" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.0.16" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.0.17" />
</ItemGroup>
<ItemGroup>

View file

@ -203,7 +203,7 @@ namespace Roadie.Api.Services
select t.Id).Skip(request.SkipValue).Take(request.LimitValue).ToArray();
}
int[] topTrackids = new int[0];
IQueryable<int> topTrackids = null;
if (request.FilterTopPlayedOnly)
{
// Get request number of top played songs for artist
@ -215,7 +215,7 @@ namespace Roadie.Api.Services
where a.RoadieId == request.FilterToArtistId
orderby ut.PlayedCount descending
select t.Id
).Skip(request.SkipValue).Take(request.LimitValue).ToArray();
).Skip(request.SkipValue).Take(request.LimitValue);
}
int[] randomTrackIds = null;
if (doRandomize ?? false)

View file

@ -12,7 +12,6 @@ namespace Roadie.Api
{
public static void Main(string[] args)
{
var dbFilename = Path.Combine("logs", "errors.db");
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(Configuration)
.CreateLogger();

View file

@ -3,7 +3,7 @@
"Roadie.Api": {
"commandName": "Project",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
"ASPNETCORE_ENVIRONMENT": "Production"
},
"applicationUrl": "http://localhost:5123/"
}

View file

@ -29,7 +29,7 @@
<PackageReference Include="Microsoft.AspNetCore.OData" Version="7.1.0" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.2.3" />
<PackageReference Include="Serilog.AspNetCore" Version="2.1.1" />
<PackageReference Include="Serilog.Exceptions" Version="5.0.0" />
<PackageReference Include="Serilog.Exceptions" Version="5.2.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="3.1.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
<PackageReference Include="Serilog.Sinks.RollingFileAlternate" Version="2.0.9" />