diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..dd2b550 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,4 @@ +[*.cs] + +# Default severity for analyzer diagnostics with category 'Style' +dotnet_analyzer_diagnostic.category-Style.severity = none diff --git a/Inspector/Program.cs b/Inspector/Program.cs index f8f617a..cf86c3b 100644 --- a/Inspector/Program.cs +++ b/Inspector/Program.cs @@ -29,7 +29,13 @@ namespace Inspector public static int Main(string[] args) => CommandLineApplication.Execute(args); +#pragma warning disable RCS1213 // Remove unused member declaration. +#pragma warning disable IDE0051 // Remove unused private members +#pragma warning disable CRR0026 private void OnExecute() +#pragma warning restore IDE0051 // Remove unused private members +#pragma warning restore RCS1213 // Remove unused member declaration. +#pragma warning restore CRR0026 { var inspector = new Roadie.Library.Inspect.Inspector(); inspector.Inspect(DoCopy, IsReadOnly, Folder, Destination ?? Folder, DontAppendSubFolder, IsReadOnly ? true : DontDeleteEmptyFolders, DontRunPreScript); diff --git a/Roadie.Api.Hubs/PlayActivityHub.cs b/Roadie.Api.Hubs/PlayActivityHub.cs index a40f6a0..0040752 100644 --- a/Roadie.Api.Hubs/PlayActivityHub.cs +++ b/Roadie.Api.Hubs/PlayActivityHub.cs @@ -6,9 +6,9 @@ namespace Roadie.Api.Hubs { public class PlayActivityHub : Hub { - public async Task SendActivity(PlayActivityList playActivity) + public Task SendActivityAsync(PlayActivityList playActivity, System.Threading.CancellationToken cancellationToken) { - await Clients.All.SendAsync("PlayActivity", playActivity); + return Clients.All.SendAsync("PlayActivity", playActivity, cancellationToken); } } } \ No newline at end of file diff --git a/Roadie.Api.Hubs/ScanActivityHub.cs b/Roadie.Api.Hubs/ScanActivityHub.cs index 5f2f40f..2081d91 100644 --- a/Roadie.Api.Hubs/ScanActivityHub.cs +++ b/Roadie.Api.Hubs/ScanActivityHub.cs @@ -5,9 +5,9 @@ namespace Roadie.Api.Hubs { public class ScanActivityHub : Hub { - public async Task SendSystemActivity(string scanActivity) + public Task SendSystemActivityAsync(string scanActivity, System.Threading.CancellationToken cancellationToken) { - await Clients.All.SendAsync("SendSystemActivity", scanActivity); + return Clients.All.SendAsync("SendSystemActivity", scanActivity, cancellationToken); } } } \ No newline at end of file diff --git a/Roadie.Api.Library.Tests/ArtistLookupEngineTests.cs b/Roadie.Api.Library.Tests/ArtistLookupEngineTests.cs index f51da99..fe72721 100644 --- a/Roadie.Api.Library.Tests/ArtistLookupEngineTests.cs +++ b/Roadie.Api.Library.Tests/ArtistLookupEngineTests.cs @@ -26,7 +26,7 @@ namespace Roadie.Library.Tests { get { - return this.MessageLogger as ILogger; + return MessageLogger as ILogger; } } @@ -38,17 +38,17 @@ namespace Roadie.Library.Tests public ArtistLookupEngineTests() { - this.MessageLogger = new EventMessageLogger(); - this.MessageLogger.Messages += MessageLogger_Messages; + MessageLogger = new EventMessageLogger(); + MessageLogger.Messages += MessageLogger_Messages; var settings = new RoadieSettings(); IConfigurationBuilder configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddJsonFile("appsettings.test.json"); IConfiguration configuration = configurationBuilder.Build(); configuration.GetSection("RoadieSettings").Bind(settings); - this.Configuration = settings; - this.CacheManager = new DictionaryCacheManager(this.Logger, new CachePolicy(TimeSpan.FromHours(4))); - this.HttpEncoder = new Encoding.DummyHttpEncoder(); + Configuration = settings; + CacheManager = new DictionaryCacheManager(Logger, new CachePolicy(TimeSpan.FromHours(4))); + HttpEncoder = new Encoding.DummyHttpEncoder(); } private void MessageLogger_Messages(object sender, EventMessage e) diff --git a/Roadie.Api.Library.Tests/ConfigurationTests.cs b/Roadie.Api.Library.Tests/ConfigurationTests.cs index df5c61b..9550e70 100644 --- a/Roadie.Api.Library.Tests/ConfigurationTests.cs +++ b/Roadie.Api.Library.Tests/ConfigurationTests.cs @@ -18,37 +18,30 @@ namespace Roadie.Library.Tests return config; } - private readonly IRoadieSettings _settings = null; + private readonly IRoadieSettings _settings; private readonly IConfiguration _configuration; - private IConfiguration Configuration - { - get - { - return this._configuration; - } - } private IRoadieSettings Settings { get { - return this._settings; + return _settings; } } public ConfigurationTests() { - this._configuration = InitConfiguration(); - this._settings = new RoadieSettings(); - this._configuration.GetSection("RoadieSettings").Bind(this._settings); + _configuration = InitConfiguration(); + _settings = new RoadieSettings(); + _configuration.GetSection("RoadieSettings").Bind(_settings); } [Fact] public void LoadRootLevelConfiguration() { var inboundFolder = @"C:\roadie_dev_root\inbound"; - var configInboundFolder = this.Settings.InboundFolder; + var configInboundFolder = Settings.InboundFolder; Assert.Equal(inboundFolder, configInboundFolder); } diff --git a/Roadie.Api.Library.Tests/ExtensionTests.cs b/Roadie.Api.Library.Tests/ExtensionTests.cs index 6ea71a6..15ac01e 100644 --- a/Roadie.Api.Library.Tests/ExtensionTests.cs +++ b/Roadie.Api.Library.Tests/ExtensionTests.cs @@ -220,8 +220,8 @@ namespace Roadie.Library.Tests var lastTrack = shuffledTracks.First(); foreach(var track in shuffledTracks.Skip(1)) { - Assert.False(track.Artist.Artist.Text == lastTrack.Artist.Artist.Text && - track.Release.Release.Text == lastTrack.Release.Release.Text); + Assert.False(string.Equals(track.Artist.Artist.Text, lastTrack.Artist.Artist.Text, StringComparison.Ordinal) && + string.Equals(track.Release.Release.Text, lastTrack.Release.Release.Text, StringComparison.Ordinal)); lastTrack = track; } diff --git a/Roadie.Api.Library.Tests/FolderPathHelperTests.cs b/Roadie.Api.Library.Tests/FolderPathHelperTests.cs index 7eb5042..d4dadf8 100644 --- a/Roadie.Api.Library.Tests/FolderPathHelperTests.cs +++ b/Roadie.Api.Library.Tests/FolderPathHelperTests.cs @@ -17,7 +17,7 @@ namespace Roadie.Library.Tests configurationBuilder.AddJsonFile("appsettings.test.json"); IConfiguration configuration = configurationBuilder.Build(); configuration.GetSection("RoadieSettings").Bind(settings); - this.Configuration = settings; + Configuration = settings; } [Theory] diff --git a/Roadie.Api.Library.Tests/ID3TagsHelperTests.cs b/Roadie.Api.Library.Tests/ID3TagsHelperTests.cs index 0193f86..6ea698c 100644 --- a/Roadie.Api.Library.Tests/ID3TagsHelperTests.cs +++ b/Roadie.Api.Library.Tests/ID3TagsHelperTests.cs @@ -21,7 +21,7 @@ namespace Roadie.Library.Tests { get { - return this.MessageLogger as ILogger; + return MessageLogger as ILogger; } } @@ -32,8 +32,8 @@ namespace Roadie.Library.Tests public ID3TagsHelperTests() { - this.MessageLogger = new EventMessageLogger(); - this.MessageLogger.Messages += MessageLoggerMessages; + MessageLogger = new EventMessageLogger(); + MessageLogger.Messages += MessageLoggerMessages; var settings = new RoadieSettings(); IConfigurationBuilder configurationBuilder = new ConfigurationBuilder(); @@ -41,11 +41,11 @@ namespace Roadie.Library.Tests IConfiguration configuration = configurationBuilder.Build(); configuration.GetSection("RoadieSettings").Bind(settings); settings.ConnectionString = configuration.GetConnectionString("RoadieDatabaseConnection"); - this.Configuration = settings; - this.CacheManager = new DictionaryCacheManager(this.Logger, new CachePolicy(TimeSpan.FromHours(4))); + Configuration = settings; + CacheManager = new DictionaryCacheManager(Logger, new CachePolicy(TimeSpan.FromHours(4))); var tagHelperLooper = new EventMessageLogger(); tagHelperLooper.Messages += MessageLoggerMessages; - this.TagsHelper = new ID3TagsHelper(this.Configuration, this.CacheManager, tagHelperLooper); + TagsHelper = new ID3TagsHelper(Configuration, CacheManager, tagHelperLooper); } private void MessageLoggerMessages(object sender, EventMessage e) @@ -384,7 +384,7 @@ namespace Roadie.Library.Tests var file = new FileInfo(@"C:\roadie_dev_root\mp3_tests\01 O.P.D. (Obsessive Personality Disorder).mp3"); if (file.Exists) { - var tagLib = this.TagsHelper.MetaDataForFile(file.FullName, true); + var tagLib = TagsHelper.MetaDataForFile(file.FullName, true); Assert.True(tagLib.IsSuccess); var metaData = tagLib.Data; Assert.NotNull(metaData.Artist); @@ -410,7 +410,7 @@ namespace Roadie.Library.Tests var file = new FileInfo(@"C:\roadie_dev_root\library\Dream Theater\[2016] The Astonishing\01 2285 Entr acte.mp3"); if (file.Exists) { - var tagLib = this.TagsHelper.MetaDataForFile(file.FullName); + var tagLib = TagsHelper.MetaDataForFile(file.FullName); Assert.True(tagLib.IsSuccess); var metaData = tagLib.Data; Assert.NotNull(metaData.Artist); @@ -436,7 +436,7 @@ namespace Roadie.Library.Tests var file = new FileInfo(@"E:\Roadie_Test_Files\13-anna_kendrick-true_colors-a57e270d\01-justin_timberlake-hair_up-ef53c026.mp3"); if (file.Exists) { - var tagLib = this.TagsHelper.MetaDataForFile(file.FullName); + var tagLib = TagsHelper.MetaDataForFile(file.FullName); Assert.True(tagLib.IsSuccess); var metaData = tagLib.Data; Assert.NotNull(metaData.Artist); @@ -473,7 +473,7 @@ namespace Roadie.Library.Tests var folderFiles = Directory.GetFiles(folderName, "*.mp3", SearchOption.AllDirectories); foreach(var file in folderFiles) { - var tagLib = this.TagsHelper.MetaDataForFile(file); + var tagLib = TagsHelper.MetaDataForFile(file); Assert.True(tagLib.IsSuccess); var metaData = tagLib.Data; Assert.NotNull(metaData.Artist); @@ -493,7 +493,7 @@ namespace Roadie.Library.Tests var file = new FileInfo(@"E:\Roadie_Test_Files\01 01 Angel Of Death.mp3"); if (file.Exists) { - var tagLib = this.TagsHelper.MetaDataForFile(file.FullName); + var tagLib = TagsHelper.MetaDataForFile(file.FullName); Assert.True(tagLib.IsSuccess); var metaData = tagLib.Data; Assert.NotNull(metaData.Artist); @@ -519,7 +519,7 @@ namespace Roadie.Library.Tests var file = new FileInfo(@"E:\Roadie_Test_Files\06 You'Re Sensational.mp3"); if (file.Exists) { - var tagLib = this.TagsHelper.MetaDataForFile(file.FullName); + var tagLib = TagsHelper.MetaDataForFile(file.FullName); Assert.True(tagLib.IsSuccess); var metaData = tagLib.Data; Assert.NotNull(metaData.Artist); @@ -545,7 +545,7 @@ namespace Roadie.Library.Tests var file = new FileInfo(@"M:\inbound\MEGAPACK ---METAL-DEATH-BLACK---\ebony_tears-evil_as_hell-2001-ss\01-deviation-ss.mp3"); if (file.Exists) { - var tagLib = this.TagsHelper.MetaDataForFile(file.FullName); + var tagLib = TagsHelper.MetaDataForFile(file.FullName); Assert.True(tagLib.IsSuccess); var metaData = tagLib.Data; Assert.NotNull(metaData.Artist); @@ -571,7 +571,7 @@ namespace Roadie.Library.Tests var file = new FileInfo(@"M:\unknown\2eec19bd-3575-4b7f-84dd-db2a0ec3e2f3~[2009] Dolly - Disc 1 Of 4~06 Nobody But You (Previously Unissued).mp3"); if (file.Exists) { - var tagLib = this.TagsHelper.MetaDataForFile(file.FullName); + var tagLib = TagsHelper.MetaDataForFile(file.FullName); Assert.True(tagLib.IsSuccess); var metaData = tagLib.Data; Assert.NotNull(metaData.Artist); @@ -598,7 +598,7 @@ namespace Roadie.Library.Tests var file = new FileInfo(@"M:\library_old\Perverse\[2014] Champion Dub\01 Champion Dub (Original Mix).mp3"); if (file.Exists) { - var tagLib = this.TagsHelper.MetaDataForFile(file.FullName); + var tagLib = TagsHelper.MetaDataForFile(file.FullName); Assert.True(tagLib.IsSuccess); var metaData = tagLib.Data; Assert.NotNull(metaData.Artist); @@ -625,7 +625,7 @@ namespace Roadie.Library.Tests var file = new FileInfo(@"C:\roadie_dev_root\inbound\Dreadful Fate - Vengeance (2018)\01-dreadful_fate-vengeance.mp3"); if (file.Exists) { - var tagLib = this.TagsHelper.MetaDataForFile(file.FullName); + var tagLib = TagsHelper.MetaDataForFile(file.FullName); Assert.True(tagLib.IsSuccess); var metaData = tagLib.Data; Assert.NotNull(metaData.Artist); @@ -653,7 +653,7 @@ namespace Roadie.Library.Tests var file = new FileInfo(@"M:\library\Blind Melon\[1992] Blind Melon\01. Blind Melon - Soak The Sin.mp3"); if (file.Exists) { - var tagLib = this.TagsHelper.MetaDataForFile(file.FullName); + var tagLib = TagsHelper.MetaDataForFile(file.FullName); Assert.True(tagLib.IsSuccess); var metaData = tagLib.Data; Assert.NotNull(metaData.Artist); @@ -680,7 +680,7 @@ namespace Roadie.Library.Tests var file = new FileInfo(@"E:\Roadie_Test_Files\Test.mp3"); if (file.Exists) { - var tagLib = this.TagsHelper.MetaDataForFile(file.FullName); + var tagLib = TagsHelper.MetaDataForFile(file.FullName); Assert.True(tagLib.IsSuccess); var metaData = tagLib.Data; Assert.NotNull(metaData.Artist); @@ -714,14 +714,14 @@ namespace Roadie.Library.Tests short trackNumber = 15; var numberOfTracks = 25; - var tagLib = this.TagsHelper.MetaDataForFile(file.FullName); + var tagLib = TagsHelper.MetaDataForFile(file.FullName); Assert.True(tagLib.IsSuccess); var metaData = tagLib.Data; metaData.TrackNumber = trackNumber; metaData.TotalTrackNumbers = numberOfTracks; - this.TagsHelper.WriteTags(metaData, file.FullName); + TagsHelper.WriteTags(metaData, file.FullName); - var tagLibAfterWrite = this.TagsHelper.MetaDataForFile(file.FullName); + var tagLibAfterWrite = TagsHelper.MetaDataForFile(file.FullName); Assert.True(tagLib.IsSuccess); Assert.Equal(metaData.Artist, tagLibAfterWrite.Data.Artist); Assert.Equal(metaData.Release, tagLibAfterWrite.Data.Release); @@ -745,7 +745,7 @@ namespace Roadie.Library.Tests var file = new FileInfo(@"M:\library\Denver, John\[1972] Aerie\10 Readjustment Blues.mp3"); if (file.Exists) { - var tagLib = this.TagsHelper.MetaDataForFile(file.FullName); + var tagLib = TagsHelper.MetaDataForFile(file.FullName); Assert.True(tagLib.IsSuccess); var metaData = tagLib.Data; Assert.True(metaData.IsValid); @@ -772,7 +772,7 @@ namespace Roadie.Library.Tests var file = new FileInfo(@"E:\Roadie_Test_Files\01. What's Yesterday.mp3"); if (file.Exists) { - var tagLib = this.TagsHelper.MetaDataForFile(file.FullName); + var tagLib = TagsHelper.MetaDataForFile(file.FullName); Assert.True(tagLib.IsSuccess); var metaData = tagLib.Data; Assert.NotNull(metaData.Artist); @@ -799,7 +799,7 @@ namespace Roadie.Library.Tests var file = new FileInfo(@"M:\library\Ac Dc\[1975] T.N.T\01 It'S A Long Way To The Top (If You Wanna Rock 'N' Roll).mp3"); if (file.Exists) { - var tagLib = this.TagsHelper.MetaDataForFile(file.FullName); + var tagLib = TagsHelper.MetaDataForFile(file.FullName); Assert.True(tagLib.IsSuccess); var metaData = tagLib.Data; Assert.NotNull(metaData.Artist); @@ -825,7 +825,7 @@ namespace Roadie.Library.Tests var file = new FileInfo(@"E:\Roadie_Test_Files\01 Martian.mp3"); if (file.Exists) { - var tagLib = this.TagsHelper.MetaDataForFile(file.FullName); + var tagLib = TagsHelper.MetaDataForFile(file.FullName); Assert.True(tagLib.IsSuccess); var metaData = tagLib.Data; Assert.NotNull(metaData.Artist); @@ -851,7 +851,7 @@ namespace Roadie.Library.Tests var file = new FileInfo(@"C:\roadie_dev_root\inbound\[2016] Invention Of Knowledge\01 Invention.mp3"); if (file.Exists) { - var tagLib = this.TagsHelper.MetaDataForFile(file.FullName); + var tagLib = TagsHelper.MetaDataForFile(file.FullName); Assert.True(tagLib.IsSuccess); var metaData = tagLib.Data; Assert.NotNull(metaData.Artist); @@ -877,7 +877,7 @@ namespace Roadie.Library.Tests var file = new FileInfo(@"C:\roadie_dev_root\1985 - Sacred Heart\Dio - Sacred Heart (1).mp3"); if (file.Exists) { - var tagLib = this.TagsHelper.MetaDataForFile(file.FullName); + var tagLib = TagsHelper.MetaDataForFile(file.FullName); Assert.True(tagLib.IsSuccess); var metaData = tagLib.Data; Assert.NotNull(metaData.Artist); @@ -903,7 +903,7 @@ namespace Roadie.Library.Tests var file = new FileInfo(@"C:\roadie_dev_root\Grift\2017 Arvet\01 - Flyktfast.mp3"); if (file.Exists) { - var tagLib = this.TagsHelper.MetaDataForFile(file.FullName); + var tagLib = TagsHelper.MetaDataForFile(file.FullName); Assert.True(tagLib.IsSuccess); var metaData = tagLib.Data; Assert.NotNull(metaData.Artist); @@ -929,7 +929,7 @@ namespace Roadie.Library.Tests var file = new FileInfo(@"C:\roadie_dev_root\Distorted Harmony - A Way Out - 2018\kWlZr0N_o72dwo0_CD001_0001.mp3"); if (file.Exists) { - var tagLib = this.TagsHelper.MetaDataForFile(file.FullName); + var tagLib = TagsHelper.MetaDataForFile(file.FullName); Assert.True(tagLib.IsSuccess); var metaData = tagLib.Data; Assert.NotNull(metaData.Artist); diff --git a/Roadie.Api.Library.Tests/ImageHelperTests.cs b/Roadie.Api.Library.Tests/ImageHelperTests.cs index a98b1e4..8b2cfbf 100644 --- a/Roadie.Api.Library.Tests/ImageHelperTests.cs +++ b/Roadie.Api.Library.Tests/ImageHelperTests.cs @@ -1,12 +1,5 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; -using Roadie.Library.Configuration; -using Roadie.Library.Data; -using Roadie.Library.Data.Context; -using Roadie.Library.FilePlugins; -using Roadie.Library.Imaging; +using Roadie.Library.Imaging; using System; -using System.Diagnostics; using System.IO; using System.Linq; using Xunit; @@ -38,6 +31,9 @@ namespace Roadie.Library.Tests [InlineData("logo.png")] [InlineData("Logo.Jpg")] [InlineData("logo.gif")] + [InlineData("band_logo.gif")] + [InlineData("134logo.gif")] + [InlineData("Dream_Theater_Logo.gif")] [InlineData("artist_logo.jpg")] [InlineData("Artist_logo.jpg")] [InlineData("ARTIST_LOGO.JPG")] @@ -61,7 +57,7 @@ namespace Roadie.Library.Tests [InlineData("cover.png")] [InlineData("Cover.Jpg")] [InlineData("Cover.JPG")] - [InlineData("Cover.PNG")] + [InlineData("Cover.PNG")] [InlineData("CvR.Jpg")] [InlineData("Release.JPG")] [InlineData("folder.JPG")] @@ -79,7 +75,7 @@ namespace Roadie.Library.Tests [InlineData("f.jpg")] [InlineData("F1.jpg")] [InlineData("F 1.jpg")] - [InlineData("F-1.jpg")] + [InlineData("F-1.jpg")] [InlineData("front_.jpg")] [InlineData("BIG.JPg")] [InlineData("bigart.JPg")] @@ -185,9 +181,9 @@ namespace Roadie.Library.Tests [InlineData("Booklet-1.jpg")] [InlineData("Booklet-10.jpg")] - [InlineData("Booklet_1.jpg")] - [InlineData("Booklet 3.jpg")] - [InlineData("Digipack (01).jpg")] + [InlineData("Booklet_1.jpg")] + [InlineData("Booklet 3.jpg")] + [InlineData("Digipack (01).jpg")] [InlineData("Eagles - Long Road Out Of Eden - Booklet-6.jpg")] [InlineData("Long Road Out Of Eden - Booklet-6.jpg")] [InlineData("Long Road Out Of Eden Booklet-6.jpg")] @@ -204,10 +200,10 @@ namespace Roadie.Library.Tests [InlineData("Back.jpg")] [InlineData("BAcK.JPg")] [InlineData("Cd.jpg")] - [InlineData("CD.JPG")] + [InlineData("CD.JPG")] [InlineData("Cd1.jpg")] [InlineData("CD (01).jpg")] - [InlineData("CD (02).jpg")] + [InlineData("CD (02).jpg")] [InlineData("CD-1.jpg")] [InlineData("CD 1.jpg")] [InlineData("CD_1.jpg")] @@ -242,16 +238,16 @@ namespace Roadie.Library.Tests [InlineData("release 3.jpg")] [InlineData("release 10.jpg")] [InlineData("Dixieland-Label-Side 1.JPG")] - [InlineData("Dixieland-Label-Side 2.JPG")] + [InlineData("Dixieland-Label-Side 2.JPG")] [InlineData("Hearing Is Believing-Inside 1.jpg")] - [InlineData("Booklet (2-3).jpg")] - [InlineData("Booklet (14-15).jpg")] - [InlineData("Booklet#2.jpg")] - [InlineData("traycard.png")] + [InlineData("Booklet (2-3).jpg")] + [InlineData("Booklet (14-15).jpg")] + [InlineData("Booklet#2.jpg")] + [InlineData("traycard.png")] [InlineData("Jewel Case.jpg")] [InlineData("Matrix-1.jpg")] [InlineData("Matrix 1.jpg")] - [InlineData("IMG_20160921_0004.jpg")] + [InlineData("IMG_20160921_0004.jpg")] public void TestShouldBeReleaseSecondaryImages(string input) { Assert.True(ImageHelper.IsReleaseSecondaryImage(new FileInfo(input))); @@ -292,7 +288,7 @@ namespace Roadie.Library.Tests public void GetReleaseImageInFolder() { var folder = new DirectoryInfo(@"C:\roadie_dev_root\image_tests"); - if(!folder.Exists) + if (!folder.Exists) { Assert.True(true); return; @@ -318,6 +314,6 @@ namespace Roadie.Library.Tests Assert.Single(artist); Assert.Equal("artist.jpg", artist.First().Name); } - + } } \ No newline at end of file diff --git a/Roadie.Api.Library.Tests/InspectorTests.cs b/Roadie.Api.Library.Tests/InspectorTests.cs index 013a0b7..ecf91f6 100644 --- a/Roadie.Api.Library.Tests/InspectorTests.cs +++ b/Roadie.Api.Library.Tests/InspectorTests.cs @@ -23,7 +23,7 @@ namespace Roadie.Library.Tests { get { - return this.MessageLogger as ILogger; + return MessageLogger as ILogger; } } @@ -35,8 +35,8 @@ namespace Roadie.Library.Tests public InspectorTests() { - this.MessageLogger = new EventMessageLogger(); - this.MessageLogger.Messages += MessageLoggerMessages; + MessageLogger = new EventMessageLogger(); + MessageLogger.Messages += MessageLoggerMessages; var settings = new configuration.RoadieSettings(); IConfigurationBuilder configurationBuilder = new ConfigurationBuilder(); @@ -44,11 +44,11 @@ namespace Roadie.Library.Tests IConfiguration configuration = configurationBuilder.Build(); configuration.GetSection("RoadieSettings").Bind(settings); settings.ConnectionString = configuration.GetConnectionString("RoadieDatabaseConnection"); - this.Configuration = settings; - this.CacheManager = new DictionaryCacheManager(this.Logger, new CachePolicy(TimeSpan.FromHours(4))); + Configuration = settings; + CacheManager = new DictionaryCacheManager(Logger, new CachePolicy(TimeSpan.FromHours(4))); var tagHelperLooper = new EventMessageLogger(); tagHelperLooper.Messages += MessageLoggerMessages; - this.TagsHelper = new ID3TagsHelper(this.Configuration, this.CacheManager, tagHelperLooper); + TagsHelper = new ID3TagsHelper(Configuration, CacheManager, tagHelperLooper); } private void MessageLoggerMessages(object sender, EventMessage e) diff --git a/Roadie.Api.Library.Tests/SearchEngineTests.cs b/Roadie.Api.Library.Tests/SearchEngineTests.cs index f5666d7..0a17146 100644 --- a/Roadie.Api.Library.Tests/SearchEngineTests.cs +++ b/Roadie.Api.Library.Tests/SearchEngineTests.cs @@ -6,10 +6,8 @@ using Roadie.Library.MetaData.MusicBrainz; using Roadie.Library.Processors; using Roadie.Library.SearchEngines.MetaData.Discogs; using System; -using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using System.Text; using System.Threading.Tasks; using Xunit; @@ -50,7 +48,7 @@ namespace Roadie.Library.Tests [Fact] public async Task DiscogsHelperReleaseSearch() { - if(!Configuration.Integrations.DiscogsProviderEnabled) + if (!Configuration.Integrations.DiscogsProviderEnabled) { return; } @@ -62,7 +60,7 @@ namespace Roadie.Library.Tests var artistName = "With The Dead"; var title = "Love From With The Dead"; - var result = await engine.PerformReleaseSearch(artistName, title, 1); + var result = await engine.PerformReleaseSearch(artistName, title, 1).ConfigureAwait(false); Assert.NotNull(result); Assert.NotEmpty(result.Data); @@ -84,10 +82,9 @@ namespace Roadie.Library.Tests var sw = Stopwatch.StartNew(); - var result = await mb.PerformArtistSearch(artistName, 1); + var result = await mb.PerformArtistSearchAsync(artistName, 1).ConfigureAwait(false); sw.Stop(); - var elapsedTime = sw.ElapsedMilliseconds; Assert.NotNull(result); Assert.NotNull(result.Data); @@ -113,10 +110,9 @@ namespace Roadie.Library.Tests var title = "Piano Man"; var sw = Stopwatch.StartNew(); - var result = await mb.PerformReleaseSearch(artistName, title, 1); + var result = await mb.PerformReleaseSearch(artistName, title, 1).ConfigureAwait(false); sw.Stop(); - var elapsedTime = sw.ElapsedMilliseconds; Assert.NotNull(result); Assert.NotNull(result.Data); diff --git a/Roadie.Api.Library.Tests/StringExtensionTests.cs b/Roadie.Api.Library.Tests/StringExtensionTests.cs index 6886e7f..66a1dc6 100644 --- a/Roadie.Api.Library.Tests/StringExtensionTests.cs +++ b/Roadie.Api.Library.Tests/StringExtensionTests.cs @@ -12,21 +12,21 @@ namespace Roadie.Library.Tests { private readonly IConfiguration _configuration; - private readonly IRoadieSettings _settings = null; + private readonly IRoadieSettings _settings; private IRoadieSettings Configuration { get { - return this._settings; + return _settings; } } public StringExtensionTests() { - this._configuration = InitConfiguration(); - this._settings = new RoadieSettings(); - this._configuration.GetSection("RoadieSettings").Bind(this._settings); + _configuration = InitConfiguration(); + _settings = new RoadieSettings(); + _configuration.GetSection("RoadieSettings").Bind(_settings); } public static IConfiguration InitConfiguration() @@ -102,7 +102,7 @@ namespace Roadie.Library.Tests public void CleanStringReleaseShouldBeAngie(string input) { var r = @"(\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)+(]|\)*))"; - var cleaned = input.CleanString(this.Configuration, r); + var cleaned = input.CleanString(Configuration, r); Assert.Equal("Angie", cleaned); } diff --git a/Roadie.Api.Library.Tests/appsettings.test.json b/Roadie.Api.Library.Tests/appsettings.test.json index a5d2930..200c9fb 100644 --- a/Roadie.Api.Library.Tests/appsettings.test.json +++ b/Roadie.Api.Library.Tests/appsettings.test.json @@ -2,6 +2,7 @@ "RoadieSettings": { "InboundFolder": "C:\\roadie_dev_root\\inbound", "LibraryFolder": "C:\\\\roadie_dev_root\\\\library", + "SearchEngineReposFolder": "C:\\\\roadie_dev_root\\\\repos", "Processing": { "RemoveStringsRegex": "\\b[0-9]+\\s#\\s\\b", "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)+(]|\\\\)*))", diff --git a/Roadie.Api.Library/Caching/DictionaryCacheManager.cs b/Roadie.Api.Library/Caching/DictionaryCacheManager.cs index 6c46223..88a0cbf 100644 --- a/Roadie.Api.Library/Caching/DictionaryCacheManager.cs +++ b/Roadie.Api.Library/Caching/DictionaryCacheManager.cs @@ -113,7 +113,7 @@ namespace Roadie.Library.Caching var r = Get(key, region); if (r == null) { - r = await getItem(); + r = await getItem().ConfigureAwait(false); Add(key, r, region); Logger.LogTrace($"-+> Cache Miss for Key [{key}], Region [{region}]"); } diff --git a/Roadie.Api.Library/Caching/MemoryCacheManager.cs b/Roadie.Api.Library/Caching/MemoryCacheManager.cs index 972a891..78570ce 100644 --- a/Roadie.Api.Library/Caching/MemoryCacheManager.cs +++ b/Roadie.Api.Library/Caching/MemoryCacheManager.cs @@ -95,7 +95,7 @@ namespace Roadie.Library.Caching var r = Get(key, region); if (r == null) { - r = await getItem(); + r = await getItem().ConfigureAwait(false); Add(key, r, region); Logger.LogTrace($"-+> Cache Miss for Key [{key}], Region [{region}]"); } diff --git a/Roadie.Api.Library/Data/Context/IRoadieDbRandomizer.cs b/Roadie.Api.Library/Data/Context/IRoadieDbRandomizer.cs index 74492de..965ef8f 100644 --- a/Roadie.Api.Library/Data/Context/IRoadieDbRandomizer.cs +++ b/Roadie.Api.Library/Data/Context/IRoadieDbRandomizer.cs @@ -5,14 +5,14 @@ namespace Roadie.Library.Data.Context { public interface IRoadieDbRandomizer { - Task> RandomArtistIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false); + Task> RandomArtistIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false); - Task> RandomGenreIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false); + Task> RandomGenreIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false); - Task> RandomLabelIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false); + Task> RandomLabelIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false); - Task> RandomReleaseIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false); + Task> RandomReleaseIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false); - Task> RandomTrackIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false); + Task> RandomTrackIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false); } } \ No newline at end of file diff --git a/Roadie.Api.Library/Data/Context/Implementation/LinqDbContextBase.cs b/Roadie.Api.Library/Data/Context/Implementation/LinqDbContextBase.cs index d00ed2c..c8ed342 100644 --- a/Roadie.Api.Library/Data/Context/Implementation/LinqDbContextBase.cs +++ b/Roadie.Api.Library/Data/Context/Implementation/LinqDbContextBase.cs @@ -16,21 +16,21 @@ namespace Roadie.Library.Data.Context.Implementation { } - public override async Task LastPlayedArtist(int userId) + public override Task LastPlayedArtist(int userId) { - return await (from ut in UserTracks - join t in Tracks on ut.TrackId equals t.Id - join rm in ReleaseMedias on t.ReleaseMediaId equals rm.Id - join r in Releases on rm.ReleaseId equals r.Id - join a in Artists on r.ArtistId equals a.Id - where ut.UserId == userId - orderby ut.LastPlayed descending - select a).FirstOrDefaultAsync(); + return (from ut in UserTracks + join t in Tracks on ut.TrackId equals t.Id + join rm in ReleaseMedias on t.ReleaseMediaId equals rm.Id + join r in Releases on rm.ReleaseId equals r.Id + join a in Artists on r.ArtistId equals a.Id + where ut.UserId == userId + orderby ut.LastPlayed descending + select a).FirstOrDefaultAsync(); } - public override async Task LastPlayedRelease(int userId) + public override Task LastPlayedRelease(int userId) { - return await (from ut in UserTracks + return (from ut in UserTracks join t in Tracks on ut.TrackId equals t.Id join rm in ReleaseMedias on t.ReleaseMediaId equals rm.Id join r in Releases on rm.ReleaseId equals r.Id @@ -39,9 +39,9 @@ namespace Roadie.Library.Data.Context.Implementation select r).FirstOrDefaultAsync(); } - public override async Task LastPlayedTrack(int userId) + public override Task LastPlayedTrack(int userId) { - return await (from ut in UserTracks + return (from ut in UserTracks join t in Tracks on ut.TrackId equals t.Id where ut.UserId == userId orderby ut.LastPlayed descending @@ -50,7 +50,7 @@ namespace Roadie.Library.Data.Context.Implementation public override async Task MostPlayedArtist(int userId) { - var mostPlayedTrack = await MostPlayedTrack(userId); + var mostPlayedTrack = await MostPlayedTrack(userId).ConfigureAwait(false); if (mostPlayedTrack != null) { return await (from t in Tracks @@ -58,35 +58,35 @@ namespace Roadie.Library.Data.Context.Implementation join r in Releases on rm.ReleaseId equals r.Id join a in Artists on r.ArtistId equals a.Id where t.Id == mostPlayedTrack.Id - select a).FirstOrDefaultAsync(); + select a).FirstOrDefaultAsync().ConfigureAwait(false); } return null; } public override async Task MostPlayedRelease(int userId) { - var mostPlayedTrack = await MostPlayedTrack(userId); + var mostPlayedTrack = await MostPlayedTrack(userId).ConfigureAwait(false); if (mostPlayedTrack != null) { return await (from t in Tracks join rm in ReleaseMedias on t.ReleaseMediaId equals rm.Id join r in Releases on rm.ReleaseId equals r.Id where t.Id == mostPlayedTrack.Id - select r).FirstOrDefaultAsync(); + select r).FirstOrDefaultAsync().ConfigureAwait(false); } return null; } - public override async Task MostPlayedTrack(int userId) + public override Task MostPlayedTrack(int userId) { - return await (from ut in UserTracks + return (from ut in UserTracks join t in Tracks on ut.TrackId equals t.Id where ut.UserId == userId orderby ut.PlayedCount descending select t).FirstOrDefaultAsync(); } - public override async Task> RandomArtistIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false) + public override async Task> RandomArtistIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false) { List randomArtists = null; if (doOnlyFavorites) @@ -98,7 +98,7 @@ namespace Roadie.Library.Data.Context.Implementation select a ).OrderBy(x => Guid.NewGuid()) .Take(randomLimit) - .ToListAsync(); + .ToListAsync().ConfigureAwait(false); } else if (doOnlyRated) { @@ -109,7 +109,7 @@ namespace Roadie.Library.Data.Context.Implementation select a ).OrderBy(x => Guid.NewGuid()) .Take(randomLimit) - .ToListAsync(); + .ToListAsync().ConfigureAwait(false); } else { @@ -120,29 +120,29 @@ namespace Roadie.Library.Data.Context.Implementation select a) .OrderBy(x => Guid.NewGuid()) .Take(randomLimit) - .ToListAsync(); + .ToListAsync().ConfigureAwait(false); } var dict = randomArtists.Select((x, i) => new { key = i, value = x.Id }).Take(randomLimit).ToDictionary(x => x.key, x => x.value); return new SortedDictionary(dict); } - public override async Task> RandomGenreIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false) + public override async Task> RandomGenreIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false) { var randomGenres = await Genres.OrderBy(x => Guid.NewGuid()) .Take(randomLimit) - .ToListAsync(); + .ToListAsync().ConfigureAwait(false); var dict = randomGenres.Select((x, i) => new { key = i, value = x.Id }).Take(randomLimit).ToDictionary(x => x.key, x => x.value); return new SortedDictionary(dict); } - public override async Task> RandomLabelIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false) + public override async Task> RandomLabelIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false) { - var randomLabels = await Labels.OrderBy(x => Guid.NewGuid()).Take(randomLimit).ToListAsync(); + var randomLabels = await Labels.OrderBy(x => Guid.NewGuid()).Take(randomLimit).ToListAsync().ConfigureAwait(false); var dict = randomLabels.Select((x, i) => new { key = i, value = x.Id }).Take(randomLimit).ToDictionary(x => x.key, x => x.value); return new SortedDictionary(dict); } - public override async Task> RandomReleaseIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false) + public override async Task> RandomReleaseIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false) { List randomReleases = null; if (doOnlyFavorites) @@ -154,7 +154,7 @@ namespace Roadie.Library.Data.Context.Implementation select r ).OrderBy(x => Guid.NewGuid()) .Take(randomLimit) - .ToListAsync(); + .ToListAsync().ConfigureAwait(false); } else if (doOnlyRated) { @@ -165,7 +165,7 @@ namespace Roadie.Library.Data.Context.Implementation select r ).OrderBy(x => Guid.NewGuid()) .Take(randomLimit) - .ToListAsync(); + .ToListAsync().ConfigureAwait(false); } else { @@ -176,13 +176,13 @@ namespace Roadie.Library.Data.Context.Implementation select r) .OrderBy(x => Guid.NewGuid()) .Take(randomLimit) - .ToListAsync(); + .ToListAsync().ConfigureAwait(false); } var dict = randomReleases.Select((x, i) => new { key = i, value = x.Id }).Take(randomLimit).ToDictionary(x => x.key, x => x.value); return new SortedDictionary(dict); } - public override async Task> RandomTrackIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false) + public override async Task> RandomTrackIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false) { List randomTracks = null; if (doOnlyFavorites) @@ -194,7 +194,7 @@ namespace Roadie.Library.Data.Context.Implementation select t ).OrderBy(x => Guid.NewGuid()) .Take(randomLimit) - .ToListAsync(); + .ToListAsync().ConfigureAwait(false); } else if (doOnlyRated) { @@ -205,7 +205,7 @@ namespace Roadie.Library.Data.Context.Implementation select t ).OrderBy(x => Guid.NewGuid()) .Take(randomLimit) - .ToListAsync(); + .ToListAsync().ConfigureAwait(false); } else { @@ -216,7 +216,7 @@ namespace Roadie.Library.Data.Context.Implementation select t) .OrderBy(x => Guid.NewGuid()) .Take(randomLimit) - .ToListAsync(); + .ToListAsync().ConfigureAwait(false); } var dict = randomTracks.Select((x, i) => new { key = i, value = x.Id }).Take(randomLimit).ToDictionary(x => x.key, x => x.value); return new SortedDictionary(dict); diff --git a/Roadie.Api.Library/Data/Context/Implementation/MysqlRoadieDbContext.cs b/Roadie.Api.Library/Data/Context/Implementation/MysqlRoadieDbContext.cs index c2c8cee..25ad0d6 100644 --- a/Roadie.Api.Library/Data/Context/Implementation/MysqlRoadieDbContext.cs +++ b/Roadie.Api.Library/Data/Context/Implementation/MysqlRoadieDbContext.cs @@ -26,7 +26,7 @@ namespace Roadie.Library.Data.Context.Implementation where ut.userId = {0} ORDER by ut.lastPlayed desc LIMIT 1"; - return await Artists.FromSqlRaw(sql, userId).FirstOrDefaultAsync(); + return await Artists.FromSqlRaw(sql, userId).FirstOrDefaultAsync().ConfigureAwait(false); } public override async Task LastPlayedRelease(int userId) @@ -41,7 +41,8 @@ namespace Roadie.Library.Data.Context.Implementation LIMIT 1"; return await Releases.FromSqlRaw(sql, userId) .Include(x => x.Artist) - .FirstOrDefaultAsync(); + .FirstOrDefaultAsync() + .ConfigureAwait(false); } public override async Task LastPlayedTrack(int userId) @@ -57,7 +58,8 @@ namespace Roadie.Library.Data.Context.Implementation .Include(x => x.ReleaseMedia) .Include("ReleaseMedia.Release") .Include("ReleaseMedia.Release.Artist") - .FirstOrDefaultAsync(); + .FirstOrDefaultAsync() + .ConfigureAwait(false); } public override async Task MostPlayedArtist(int userId) @@ -72,7 +74,7 @@ namespace Roadie.Library.Data.Context.Implementation group by r.id order by SUM(ut.playedCount) desc LIMIT 1"; - return await Artists.FromSqlRaw(sql, userId).FirstOrDefaultAsync(); + return await Artists.FromSqlRaw(sql, userId).FirstOrDefaultAsync().ConfigureAwait(false); } public override async Task MostPlayedRelease(int userId) @@ -88,7 +90,8 @@ namespace Roadie.Library.Data.Context.Implementation LIMIT 1"; return await Releases.FromSqlRaw(sql, userId) .Include(x => x.Artist) - .FirstOrDefaultAsync(); + .FirstOrDefaultAsync() + .ConfigureAwait(false); } public override async Task MostPlayedTrack(int userId) @@ -105,10 +108,11 @@ namespace Roadie.Library.Data.Context.Implementation .Include(x => x.ReleaseMedia) .Include("ReleaseMedia.Release") .Include("ReleaseMedia.Release.Artist") - .FirstOrDefaultAsync(); + .FirstOrDefaultAsync() + .ConfigureAwait(false); } - public override async Task> RandomArtistIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false) + public override async Task> RandomArtistIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false) { var sql = @"SELECT a.id FROM `artist` a @@ -117,34 +121,34 @@ namespace Roadie.Library.Data.Context.Implementation AND {2} = 1) order BY RIGHT(HEX((1 << 24) * (1 + RAND())), 6) LIMIT 0, {0}"; - var ids = await Artists.FromSqlRaw(sql, randomLimit, userId, doOnlyFavorites ? "1" : "0").Select(x => x.Id).ToListAsync(); + var ids = await Artists.FromSqlRaw(sql, randomLimit, userId, doOnlyFavorites ? "1" : "0").Select(x => x.Id).ToListAsync().ConfigureAwait(false); var dict = ids.Select((id, i) => new { key = i, value = id }).ToDictionary(x => x.key, x => x.value); return new SortedDictionary(dict); } - public override async Task> RandomGenreIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false) + public override async Task> RandomGenreIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false) { var sql = @"SELECT g.id FROM `genre` g ORDER BY RIGHT( HEX( (1<<24) * (1+RAND()) ), 6) LIMIT 0, {0}"; - var ids = await Genres.FromSqlRaw(sql, randomLimit).Select(x => x.Id).ToListAsync(); + var ids = await Genres.FromSqlRaw(sql, randomLimit).Select(x => x.Id).ToListAsync().ConfigureAwait(false); var dict = ids.Select((id, i) => new { key = i, value = id }).ToDictionary(x => x.key, x => x.value); return new SortedDictionary(dict); } - public override async Task> RandomLabelIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false) + public override async Task> RandomLabelIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false) { var sql = @"SELECT l.id FROM `label` l ORDER BY RIGHT( HEX( (1<<24) * (1+RAND()) ), 6) LIMIT 0, {0}"; - var ids = await Labels.FromSqlRaw(sql, randomLimit).Select(x => x.Id).ToListAsync(); + var ids = await Labels.FromSqlRaw(sql, randomLimit).Select(x => x.Id).ToListAsync().ConfigureAwait(false); var dict = ids.Select((id, i) => new { key = i, value = id }).ToDictionary(x => x.key, x => x.value); return new SortedDictionary(dict); } - public override async Task> RandomReleaseIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false) + public override async Task> RandomReleaseIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false) { var sql = @"SELECT r.id FROM `release` r @@ -153,12 +157,12 @@ namespace Roadie.Library.Data.Context.Implementation AND {2} = 1) ORDER BY RIGHT( HEX( (1<<24) * (1+RAND()) ), 6) LIMIT 0, {0}"; - var ids = await Releases.FromSqlRaw(sql, randomLimit, userId, doOnlyFavorites ? "1" : "0").Select(x => x.Id).ToListAsync(); + var ids = await Releases.FromSqlRaw(sql, randomLimit, userId, doOnlyFavorites ? "1" : "0").Select(x => x.Id).ToListAsync().ConfigureAwait(false); var dict = ids.Select((id, i) => new { key = i, value = id }).ToDictionary(x => x.key, x => x.value); return new SortedDictionary(dict); } - public override async Task> RandomTrackIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false) + public override async Task> RandomTrackIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false) { var sql = @"SELECT t.id FROM `track` t @@ -194,7 +198,7 @@ namespace Roadie.Library.Data.Context.Implementation WHERE ut.userId = {1} AND ut.isFavorite = 1) AND {2} = 1) OR {2} = 0) order BY RIGHT( HEX( (1<<24) * (1+RAND()) ), 6) LIMIT 0, {0}"; - var ids = await Tracks.FromSqlRaw(sql, randomLimit, userId, doOnlyFavorites ? "1" : "0", doOnlyRated ? "1" : "0").Select(x => x.Id).ToListAsync(); + var ids = await Tracks.FromSqlRaw(sql, randomLimit, userId, doOnlyFavorites ? "1" : "0", doOnlyRated ? "1" : "0").Select(x => x.Id).ToListAsync().ConfigureAwait(false); var dict = ids.Select((id, i) => new { key = i, value = id }).ToDictionary(x => x.key, x => x.value); return new SortedDictionary(dict); } diff --git a/Roadie.Api.Library/Data/Context/Implementation/SQLiteRoadieDbContext.cs b/Roadie.Api.Library/Data/Context/Implementation/SQLiteRoadieDbContext.cs index cc33f04..423451e 100644 --- a/Roadie.Api.Library/Data/Context/Implementation/SQLiteRoadieDbContext.cs +++ b/Roadie.Api.Library/Data/Context/Implementation/SQLiteRoadieDbContext.cs @@ -22,7 +22,7 @@ namespace Roadie.Library.Data.Context.Implementation } } - public override async Task> RandomArtistIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false) + public override async Task> RandomArtistIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false) { // TODO Rating? var sql = @"SELECT a.id @@ -32,34 +32,34 @@ namespace Roadie.Library.Data.Context.Implementation AND {2} = 1) order by random() LIMIT 0, {0}"; - var ids = await Artists.FromSqlRaw(sql, randomLimit, userId, doOnlyFavorites ? "1" : "0").Select(x => x.Id).ToListAsync(); + var ids = await Artists.FromSqlRaw(sql, randomLimit, userId, doOnlyFavorites ? "1" : "0").Select(x => x.Id).ToListAsync().ConfigureAwait(false); var dict = ids.Select((id, i) => new { key = i, value = id }).ToDictionary(x => x.key, x => x.value); return new SortedDictionary(dict); } - public override async Task> RandomGenreIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false) + public override async Task> RandomGenreIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false) { var sql = @"SELECT g.id FROM genre g order by random() LIMIT 0, {0}"; - var ids = await Genres.FromSqlRaw(sql, randomLimit).Select(x => x.Id).ToListAsync(); + var ids = await Genres.FromSqlRaw(sql, randomLimit).Select(x => x.Id).ToListAsync().ConfigureAwait(false); var dict = ids.Select((id, i) => new { key = i, value = id }).ToDictionary(x => x.key, x => x.value); return new SortedDictionary(dict); } - public override async Task> RandomLabelIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false) + public override async Task> RandomLabelIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false) { var sql = @"SELECT l.id FROM label l order by random() LIMIT 0, {0}"; - var ids = await Labels.FromSqlRaw(sql, randomLimit).Select(x => x.Id).ToListAsync(); + var ids = await Labels.FromSqlRaw(sql, randomLimit).Select(x => x.Id).ToListAsync().ConfigureAwait(false); var dict = ids.Select((id, i) => new { key = i, value = id }).ToDictionary(x => x.key, x => x.value); return new SortedDictionary(dict); } - public override async Task> RandomReleaseIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false) + public override async Task> RandomReleaseIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false) { // TODO Rating? var sql = @"SELECT r.id @@ -69,12 +69,12 @@ namespace Roadie.Library.Data.Context.Implementation AND {2} = 1) order by random() LIMIT 0, {0}"; - var ids = await Releases.FromSqlRaw(sql, randomLimit, userId, doOnlyFavorites ? "1" : "0").Select(x => x.Id).ToListAsync(); + var ids = await Releases.FromSqlRaw(sql, randomLimit, userId, doOnlyFavorites ? "1" : "0").Select(x => x.Id).ToListAsync().ConfigureAwait(false); var dict = ids.Select((id, i) => new { key = i, value = id }).ToDictionary(x => x.key, x => x.value); return new SortedDictionary(dict); } - public override async Task> RandomTrackIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false) + public override async Task> RandomTrackIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false) { // When using the regular 'FromSqlRaw' with parameters SQLite returns no records. @@ -103,7 +103,7 @@ namespace Roadie.Library.Data.Context.Implementation WHERE ut.userId = {userId} AND ut.isFavorite = 1) AND {df} = 1) OR {df} = 0) order by random() LIMIT 0, {randomLimit}"; - var ids = await Tracks.FromSqlRaw(sql, randomLimit).Select(x => x.Id).ToListAsync(); + var ids = await Tracks.FromSqlRaw(sql, randomLimit).Select(x => x.Id).ToListAsync().ConfigureAwait(false); var dict = ids.Select((id, i) => new { key = i, value = id }).ToDictionary(x => x.key, x => x.value); return new SortedDictionary(dict); } diff --git a/Roadie.Api.Library/Data/Context/RoadieDbContextPartial.cs b/Roadie.Api.Library/Data/Context/RoadieDbContextPartial.cs index 76d2e4c..2d7652c 100644 --- a/Roadie.Api.Library/Data/Context/RoadieDbContextPartial.cs +++ b/Roadie.Api.Library/Data/Context/RoadieDbContextPartial.cs @@ -6,15 +6,15 @@ namespace Roadie.Library.Data.Context { public abstract partial class RoadieDbContext : DbContext, IRoadieDbContext { - public abstract Task> RandomArtistIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false); + public abstract Task> RandomArtistIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false); - public abstract Task> RandomGenreIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false); + public abstract Task> RandomGenreIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false); - public abstract Task> RandomLabelIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false); + public abstract Task> RandomLabelIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false); - public abstract Task> RandomReleaseIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false); + public abstract Task> RandomReleaseIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false); - public abstract Task> RandomTrackIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false); + public abstract Task> RandomTrackIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false); public abstract Task MostPlayedArtist(int userId); diff --git a/Roadie.Api.Library/Engines/ArtistLookupEngine.cs b/Roadie.Api.Library/Engines/ArtistLookupEngine.cs index 17389ef..4d9df46 100644 --- a/Roadie.Api.Library/Engines/ArtistLookupEngine.cs +++ b/Roadie.Api.Library/Engines/ArtistLookupEngine.cs @@ -349,7 +349,7 @@ namespace Roadie.Library.Engines if (ITunesArtistSearchEngine.IsEnabled) { var sw2 = Stopwatch.StartNew(); - var iTunesResult = await ITunesArtistSearchEngine.PerformArtistSearch(artistName, 1).ConfigureAwait(false); + var iTunesResult = await ITunesArtistSearchEngine.PerformArtistSearchAsync(artistName, 1).ConfigureAwait(false); if (iTunesResult.IsSuccess) { var i = iTunesResult.Data.First(); @@ -406,7 +406,7 @@ namespace Roadie.Library.Engines if (MusicBrainzArtistSearchEngine.IsEnabled) { var sw2 = Stopwatch.StartNew(); - var mbResult = await MusicBrainzArtistSearchEngine.PerformArtistSearch(result.Name, 1).ConfigureAwait(false); + var mbResult = await MusicBrainzArtistSearchEngine.PerformArtistSearchAsync(result.Name, 1).ConfigureAwait(false); if (mbResult.IsSuccess) { var mb = mbResult.Data.First(); @@ -471,7 +471,7 @@ namespace Roadie.Library.Engines if (LastFmArtistSearchEngine.IsEnabled) { var sw2 = Stopwatch.StartNew(); - var lastFmResult = await LastFmArtistSearchEngine.PerformArtistSearch(result.Name, 1).ConfigureAwait(false); + var lastFmResult = await LastFmArtistSearchEngine.PerformArtistSearchAsync(result.Name, 1).ConfigureAwait(false); if (lastFmResult.IsSuccess) { var l = lastFmResult.Data.First(); @@ -515,7 +515,7 @@ namespace Roadie.Library.Engines if (SpotifyArtistSearchEngine.IsEnabled) { var sw2 = Stopwatch.StartNew(); - var spotifyResult = await SpotifyArtistSearchEngine.PerformArtistSearch(result.Name, 1).ConfigureAwait(false); + var spotifyResult = await SpotifyArtistSearchEngine.PerformArtistSearchAsync(result.Name, 1).ConfigureAwait(false); if (spotifyResult.IsSuccess) { var s = spotifyResult.Data.First(); @@ -557,7 +557,7 @@ namespace Roadie.Library.Engines if (DiscogsArtistSearchEngine.IsEnabled) { var sw2 = Stopwatch.StartNew(); - var discogsResult = await DiscogsArtistSearchEngine.PerformArtistSearch(result.Name, 1).ConfigureAwait(false); + var discogsResult = await DiscogsArtistSearchEngine.PerformArtistSearchAsync(result.Name, 1).ConfigureAwait(false); if (discogsResult.IsSuccess) { var d = discogsResult?.Data?.FirstOrDefault(); @@ -614,7 +614,7 @@ namespace Roadie.Library.Engines { wikiName += " band"; } - var wikipediaResult = await WikipediaArtistSearchEngine.PerformArtistSearch(wikiName, 1).ConfigureAwait(false); + var wikipediaResult = await WikipediaArtistSearchEngine.PerformArtistSearchAsync(wikiName, 1).ConfigureAwait(false); if (wikipediaResult?.Data != null) { if (wikipediaResult.IsSuccess) diff --git a/Roadie.Api.Library/Engines/ReleaseLookupEngine.cs b/Roadie.Api.Library/Engines/ReleaseLookupEngine.cs index 333ca18..1d1e1d5 100644 --- a/Roadie.Api.Library/Engines/ReleaseLookupEngine.cs +++ b/Roadie.Api.Library/Engines/ReleaseLookupEngine.cs @@ -455,7 +455,7 @@ namespace Roadie.Library.Engines { var sw2 = Stopwatch.StartNew(); Logger.LogTrace("ITunesReleaseSearchEngine Release Search for ArtistName [{0}], ReleaseTitle [{1}]", metaData.Artist, result.Title); - var iTunesResult = await ITunesReleaseSearchEngine.PerformReleaseSearch(metaData.Artist, result.Title, 1); + var iTunesResult = await ITunesReleaseSearchEngine.PerformReleaseSearch(metaData.Artist, result.Title, 1).ConfigureAwait(false); if (iTunesResult.IsSuccess) { var i = iTunesResult.Data.First(); diff --git a/Roadie.Api.Library/Imaging/ImageHelper.cs b/Roadie.Api.Library/Imaging/ImageHelper.cs index 5f0ceda..4e7347c 100644 --- a/Roadie.Api.Library/Imaging/ImageHelper.cs +++ b/Roadie.Api.Library/Imaging/ImageHelper.cs @@ -26,24 +26,23 @@ namespace Roadie.Library.Imaging public static byte[] ConvertToJpegFormat(byte[] imageBytes) { - if (imageBytes == null || !imageBytes.Any()) + if(imageBytes?.Any() != true) { return null; } try { - using (var outStream = new MemoryStream()) + using(var outStream = new MemoryStream()) { IImageFormat imageFormat = null; - using (var image = SixLabors.ImageSharp.Image.Load(imageBytes, out imageFormat)) + using(var image = SixLabors.ImageSharp.Image.Load(imageBytes, out imageFormat)) { image.SaveAsJpeg(outStream); } return outStream.ToArray(); } - } - catch (Exception ex) + } catch(Exception ex) { Trace.WriteLine($"Error Converting Image to Jpg [{ ex.Message }]", "Warning"); } @@ -55,44 +54,48 @@ namespace Roadie.Library.Imaging /// public static byte[] ConvertToGifFormat(byte[] imageBytes) { - if (imageBytes == null || !imageBytes.Any()) + if(imageBytes?.Any() != true) { return null; } try { - using (var outStream = new MemoryStream()) + using(var outStream = new MemoryStream()) { IImageFormat imageFormat = null; - using (var image = SixLabors.ImageSharp.Image.Load(imageBytes, out imageFormat)) + using(var image = SixLabors.ImageSharp.Image.Load(imageBytes, out imageFormat)) { image.SaveAsGif(outStream); } return outStream.ToArray(); } - } - catch (Exception ex) + } catch(Exception ex) { Trace.WriteLine($"Error Converting Image to Gif [{ ex.Message }]", "Warning"); } return null; } - public static IEnumerable FindImagesByName(DirectoryInfo directory, string name, SearchOption folderSearchOptions = SearchOption.AllDirectories) + public static IEnumerable FindImagesByName(DirectoryInfo directory, + string name, + SearchOption folderSearchOptions = SearchOption.AllDirectories) { var result = new List(); - if (directory == null || !directory.Exists || string.IsNullOrEmpty(name)) return result; + if(directory?.Exists != true || string.IsNullOrEmpty(name)) + return result; var imageFilesInFolder = ImageFilesInFolder(directory.FullName, folderSearchOptions); - if (imageFilesInFolder == null || !imageFilesInFolder.Any()) return result; - if (imageFilesInFolder.Any()) + if(imageFilesInFolder?.Any() != true) + return result; + if(imageFilesInFolder.Length > 0) { name = name.ToAlphanumericName(); - foreach (var imageFileInFolder in imageFilesInFolder) + foreach(var imageFileInFolder in imageFilesInFolder) { var image = new FileInfo(imageFileInFolder); var filenameWithoutExtension = Path.GetFileName(imageFileInFolder).ToAlphanumericName(); var imageName = image.Name.ToAlphanumericName(); - if (imageName.Equals(name) || filenameWithoutExtension.Equals(name)) result.Add(image); + if(imageName.Equals(name) || filenameWithoutExtension.Equals(name)) + result.Add(image); } } @@ -107,77 +110,102 @@ namespace Roadie.Library.Imaging } try { - if (string.IsNullOrEmpty(image1) || !File.Exists(image1)) + if(string.IsNullOrEmpty(image1) || !File.Exists(image1)) { return File.Exists(compareToImage); } - using (var imageComparing = SixLabors.ImageSharp.Image.Load(image1)) + using(var imageComparing = SixLabors.ImageSharp.Image.Load(image1)) { - using (var imageToCompare = SixLabors.ImageSharp.Image.Load(compareToImage)) + using(var imageToCompare = SixLabors.ImageSharp.Image.Load(compareToImage)) { // Generally a larger image is the preferred image - var isBigger = imageToCompare.Height > imageComparing.Height && imageToCompare.Width > imageComparing.Width; - if (isBigger) + var isBigger = imageToCompare.Height > imageComparing.Height && + imageToCompare.Width > imageComparing.Width; + if(isBigger) { return true; } } } - } - catch (Exception ex) + } catch(Exception ex) { - Trace.WriteLine($"Error IsImageBetterQuality Image Comparing [{ image1 }] to [{ compareToImage }], Error [{ ex }]", "Warning"); + Trace.WriteLine($"Error IsImageBetterQuality Image Comparing [{ image1 }] to [{ compareToImage }], Error [{ ex }]", + "Warning"); } return false; } - public static IEnumerable FindImageTypeInDirectory(DirectoryInfo directory, ImageType type, SearchOption folderSearchOptions = SearchOption.AllDirectories) + public static IEnumerable FindImageTypeInDirectory(DirectoryInfo directory, + ImageType type, + SearchOption folderSearchOptions = SearchOption.AllDirectories) { var result = new List(); - if (directory == null || !directory.Exists) return result; + if (directory?.Exists != true) + { + return result; + } var imageFilesInFolder = ImageFilesInFolder(directory.FullName, folderSearchOptions); - if (imageFilesInFolder == null || !imageFilesInFolder.Any()) return result; - foreach (var imageFile in imageFilesInFolder) + if (imageFilesInFolder?.Any() != true) + { + return result; + } + foreach(var imageFile in imageFilesInFolder) { var image = new FileInfo(imageFile); - switch (type) + switch(type) { case ImageType.Artist: - if (IsArtistImage(image)) result.Add(image); + if (IsArtistImage(image)) + { + result.Add(image); + } break; case ImageType.ArtistSecondary: - if (IsArtistSecondaryImage(image)) result.Add(image); + if (IsArtistSecondaryImage(image)) + { + result.Add(image); + } break; case ImageType.Release: - if (IsReleaseImage(image)) result.Add(image); + if (IsReleaseImage(image)) + { + result.Add(image); + } break; case ImageType.ReleaseSecondary: - if (IsReleaseSecondaryImage(image)) result.Add(image); + if (IsReleaseSecondaryImage(image)) + { + result.Add(image); + } break; case ImageType.Label: - if (IsLabelImage(image)) result.Add(image); + if (IsLabelImage(image)) + { + result.Add(image); + } break; } } - return result.OrderBy(x => x.Name); } - public static string[] GetFiles(string path, string[] patterns = null, SearchOption options = SearchOption.TopDirectoryOnly) + public static string[] GetFiles(string path, + string[] patterns = null, + SearchOption options = SearchOption.TopDirectoryOnly) { - if (!Directory.Exists(path)) + if(!Directory.Exists(path)) { return new string[0]; } - if (patterns == null || patterns.Length == 0) + if(patterns == null || patterns.Length == 0) { return Directory.GetFiles(path, "*", options); } - if (patterns.Length == 1) + if(patterns.Length == 1) { return Directory.GetFiles(path, patterns[0], options); } @@ -186,9 +214,10 @@ namespace Roadie.Library.Imaging public static byte[] ImageDataFromUrl(string imageUrl) { - if (!string.IsNullOrEmpty(imageUrl) && !imageUrl.StartsWith("http", StringComparison.OrdinalIgnoreCase)) + if(!string.IsNullOrEmpty(imageUrl) && !imageUrl.StartsWith("http", StringComparison.OrdinalIgnoreCase)) { - var dataString = imageUrl.Trim().Replace('-', '+') + var dataString = imageUrl.Trim() + .Replace('-', '+') .Replace("data:image/jpeg;base64,", "") .Replace("data:image/bmp;base64,", "") .Replace("data:image/gif;base64,", "") @@ -200,27 +229,22 @@ namespace Roadie.Library.Imaging } public static string[] ImageExtensions() - { - return new string[6] { "*.bmp", "*.jpeg", "*.jpe", "*.jpg", "*.png", "*.gif" }; - } + { return new string[6] { "*.bmp", "*.jpeg", "*.jpe", "*.jpg", "*.png", "*.gif" }; } public static string[] ImageFilesInFolder(string folder, SearchOption searchOption) - { - return GetFiles(folder, ImageExtensions(), searchOption); - } + { return GetFiles(folder, ImageExtensions(), searchOption); } public static string[] ImageMimeTypes() - { - return new string[4] { "image/bmp", "image/jpeg", "image/png", "image/gif" }; - } + { return new string[4] { "image/bmp", "image/jpeg", "image/png", "image/gif" }; } public static ImageSearchResult ImageSearchResultForImageUrl(string imageUrl) { - if (!WebHelper.IsStringUrl(imageUrl)) return null; + if(!WebHelper.IsStringUrl(imageUrl)) + return null; var result = new ImageSearchResult(); var imageBytes = WebHelper.BytesForImageUrl(imageUrl); IImageFormat imageFormat = null; - using (var image = SixLabors.ImageSharp.Image.Load(imageBytes, out imageFormat)) + using(var image = SixLabors.ImageSharp.Image.Load(imageBytes, out imageFormat)) { result.Height = image.Height.ToString(); result.Width = image.Width.ToString(); @@ -232,43 +256,54 @@ namespace Roadie.Library.Imaging public static bool IsArtistImage(FileInfo fileinfo) { - if (fileinfo == null) return false; - return Regex.IsMatch(fileinfo.Name, @"(band|artist|group|photo)\.(jpg|jpeg|png|bmp|gif)", - RegexOptions.IgnoreCase); + if(fileinfo == null) + return false; + return Regex.IsMatch(fileinfo.Name, + @"(band|artist|group|photo)\.(jpg|jpeg|png|bmp|gif)", + RegexOptions.IgnoreCase); } public static bool IsArtistSecondaryImage(FileInfo fileinfo) { - if (fileinfo == null) return false; + if(fileinfo == null) + return false; return Regex.IsMatch(fileinfo.Name, - @"(artist_logo|logo|photo[-_\s]*[0-9]+|(artist[\s_-]+[0-9]+)|(band[\s_-]+[0-9]+))\.(jpg|jpeg|png|bmp|gif)", - RegexOptions.IgnoreCase); + @"(artist_logo|logo|photo[-_\s]*[0-9]+|(artist[\s_-]+[0-9]+)|(band[\s_-]+[0-9]+))\.(jpg|jpeg|png|bmp|gif)", + RegexOptions.IgnoreCase); } public static bool IsLabelImage(FileInfo fileinfo) { - if (fileinfo == null) return false; - return Regex.IsMatch(fileinfo.Name, @"(label|recordlabel|record_label)\.(jpg|jpeg|png|bmp|gif)", - RegexOptions.IgnoreCase); + if(fileinfo == null) + return false; + return Regex.IsMatch(fileinfo.Name, + @"(label|recordlabel|record_label)\.(jpg|jpeg|png|bmp|gif)", + RegexOptions.IgnoreCase); } public static bool IsReleaseImage(FileInfo fileinfo, string releaseName = null) { - if (fileinfo == null) return false; + if(fileinfo == null) + return false; return Regex.IsMatch(fileinfo.Name, - @"((f[-_\s]*[0-9]*)|00|art|big[art]*|cover|cvr|folder|release|front[-_\s]*)\.(jpg|jpeg|png|bmp|gif)", - RegexOptions.IgnoreCase); + @"((f[-_\s]*[0-9]*)|00|art|big[art]*|cover|cvr|folder|release|front[-_\s]*)\.(jpg|jpeg|png|bmp|gif)", + RegexOptions.IgnoreCase); } public static bool IsReleaseSecondaryImage(FileInfo fileinfo) { - if (fileinfo == null) return false; + if(fileinfo == null) + 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]*[)]*)|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]*|digipack.?\[?\(?([0-9]*)\]?\)?|cd.?\[?\(?([0-9]*)\]?\)?|(release[\s_-]+[0-9]+))\.(jpg|jpeg|png|bmp|gif)", - RegexOptions.IgnoreCase); + @"((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]*|digipack.?\[?\(?([0-9]*)\]?\)?|cd.?\[?\(?([0-9]*)\]?\)?|(release[\s_-]+[0-9]+))\.(jpg|jpeg|png|bmp|gif)", + RegexOptions.IgnoreCase); } - public static byte[] ResizeImage(byte[] imageBytes, int width, int height) => ImageHelper.ResizeImage(imageBytes, width, height, false).Item2; + public static byte[] ResizeImage(byte[] imageBytes, int width, int height) => ImageHelper.ResizeImage(imageBytes, + width, + height, + false) + .Item2; /// /// Resize a given image to given dimensions if needed @@ -278,38 +313,39 @@ namespace Roadie.Library.Imaging /// Resize to height /// Force resize /// Tuple with bool for did resize and byte array of image - public static Tuple ResizeImage(byte[] imageBytes, int width, int height, bool? forceResize = false) + public static Tuple ResizeImage(byte[] imageBytes, + int width, + int height, + bool? forceResize = false) { - if (imageBytes == null) + if(imageBytes == null) { return null; } try { - using (var outStream = new MemoryStream()) + using(var outStream = new MemoryStream()) { var resized = false; IImageFormat imageFormat = null; - using (var image = SixLabors.ImageSharp.Image.Load(imageBytes, out imageFormat)) + using(var image = SixLabors.ImageSharp.Image.Load(imageBytes, out imageFormat)) { var doForce = forceResize ?? false; - if (doForce || image.Width > width || image.Height > height) + if(doForce || image.Width > width || image.Height > height) { int newWidth, newHeight; - if (doForce) + if(doForce) { newWidth = width; newHeight = height; - } - else + } else { float aspect = image.Width / (float)image.Height; - if (aspect < 1) + if(aspect < 1) { newWidth = (int)(width * aspect); newHeight = (int)(newWidth / aspect); - } - else + } else { newHeight = (int)(height / aspect); newWidth = (int)(newHeight * aspect); @@ -322,8 +358,7 @@ namespace Roadie.Library.Imaging } return new Tuple(resized, outStream.ToArray()); } - } - catch (Exception ex) + } catch(Exception ex) { Trace.WriteLine($"Error Resizing Image [{ex}]", "Warning"); } @@ -331,12 +366,13 @@ namespace Roadie.Library.Imaging } /// - /// Get image data from all sources for either fileanme or MetaData + /// Get image data from all sources for either fileanme or MetaData /// /// Name of the File (ie a CUE file) /// Populated MetaData - /// - public static AudioMetaDataImage GetPictureForMetaData(IRoadieSettings configuration, string filename, AudioMetaData metaData) + public static AudioMetaDataImage GetPictureForMetaData(IRoadieSettings configuration, + string filename, + AudioMetaData metaData) { SimpleContract.Requires(!string.IsNullOrEmpty(filename), "Invalid Filename"); SimpleContract.Requires(metaData != null, "Invalid MetaData"); @@ -345,7 +381,7 @@ namespace Roadie.Library.Imaging } /// - /// Does image exist with the same filename + /// Does image exist with the same filename /// /// Name of the File (ie a CUE file) /// Null if not found else populated image @@ -353,7 +389,7 @@ namespace Roadie.Library.Imaging { AudioMetaDataImage imageMetaData = null; - if (string.IsNullOrEmpty(filename)) + if(string.IsNullOrEmpty(filename)) { return imageMetaData; } @@ -361,9 +397,9 @@ namespace Roadie.Library.Imaging { var fileInfo = new FileInfo(filename); var ReleaseCover = Path.ChangeExtension(filename, "jpg"); - if (File.Exists(ReleaseCover)) + if(File.Exists(ReleaseCover)) { - using (var processor = new ImageProcessor(configuration)) + using(var processor = new ImageProcessor(configuration)) { imageMetaData = new AudioMetaDataImage { @@ -372,24 +408,23 @@ namespace Roadie.Library.Imaging MimeType = Library.Processors.FileProcessor.DetermineFileType(fileInfo) }; } - } - else + } else { // Is there a picture in filename folder (for the Release) var pictures = fileInfo.Directory.GetFiles("*.jpg"); var tagImages = new List(); - if (pictures != null && pictures.Any()) + if(pictures?.Any() == true) { - FileInfo picture = null; // See if there is a "cover" or "front" jpg file if so use it - picture = pictures.FirstOrDefault(x => - x.Name.Equals("cover", StringComparison.OrdinalIgnoreCase)); - if (picture == null) - picture = pictures.FirstOrDefault(x => - x.Name.Equals("front", StringComparison.OrdinalIgnoreCase)); - if (picture == null) picture = pictures.First(); - if (picture != null) - using (var processor = new ImageProcessor(configuration)) + FileInfo picture = Array.Find(pictures, + x => x.Name.Equals("cover", StringComparison.OrdinalIgnoreCase)); + if(picture == null) + picture = Array.Find(pictures, + x => x.Name.Equals("front", StringComparison.OrdinalIgnoreCase)); + if(picture == null) + picture = pictures[0]; + if(picture != null) + using(var processor = new ImageProcessor(configuration)) { imageMetaData = new AudioMetaDataImage { @@ -400,11 +435,9 @@ namespace Roadie.Library.Imaging } } } - } - catch (FileNotFoundException) + } catch(FileNotFoundException) { - } - catch (Exception ex) + } catch(Exception ex) { Trace.WriteLine(ex, ex.Serialize()); } @@ -412,97 +445,138 @@ namespace Roadie.Library.Imaging return imageMetaData; } - public static models.Image MakeThumbnailImage(IRoadieSettings configuration, IHttpContext httpContext, Guid id, string type, int? width = null, int? height = null, bool includeCachebuster = false) + public static models.Image MakeThumbnailImage(IRoadieSettings configuration, + IHttpContext httpContext, + Guid id, + string type, + int? width = null, + int? height = null, + bool includeCachebuster = false) { - return MakeImage(configuration, httpContext, id, type, width ?? configuration.ThumbnailImageSize.Width, height ?? configuration.ThumbnailImageSize.Height, null, includeCachebuster); + return MakeImage(configuration, + httpContext, + id, + type, + width ?? configuration.ThumbnailImageSize.Width, + height ?? configuration.ThumbnailImageSize.Height, + null, + includeCachebuster); } public static models.Image MakeNewImage(IHttpContext httpContext, string type) - { - return new models.Image($"{httpContext.ImageBaseUrl}/{type}.jpg", null, null); - } + { return new models.Image($"{httpContext.ImageBaseUrl}/{type}.jpg", null, null); } - public static models.Image MakePlaylistThumbnailImage(IRoadieSettings configuration, IHttpContext httpContext, Guid id) - { - return MakeThumbnailImage(configuration, httpContext, id, "playlist"); - } + public static models.Image MakePlaylistThumbnailImage(IRoadieSettings configuration, + IHttpContext httpContext, + Guid id) + { return MakeThumbnailImage(configuration, httpContext, id, "playlist"); } - public static models.Image MakeReleaseThumbnailImage(IRoadieSettings configuration, IHttpContext httpContext, Guid id) - { - return MakeThumbnailImage(configuration, httpContext, id, "release"); - } + public static models.Image MakeReleaseThumbnailImage(IRoadieSettings configuration, + IHttpContext httpContext, + Guid id) + { return MakeThumbnailImage(configuration, httpContext, id, "release"); } - public static models.Image MakeTrackThumbnailImage(IRoadieSettings configuration, IHttpContext httpContext, Guid id) - { - return MakeThumbnailImage(configuration, httpContext, id, "track"); - } + public static models.Image MakeTrackThumbnailImage(IRoadieSettings configuration, + IHttpContext httpContext, + Guid id) + { return MakeThumbnailImage(configuration, httpContext, id, "track"); } - public static models.Image MakeUserThumbnailImage(IRoadieSettings configuration, IHttpContext httpContext, Guid id) - { - return MakeThumbnailImage(configuration, httpContext, id, "user"); - } + public static models.Image MakeUserThumbnailImage(IRoadieSettings configuration, + IHttpContext httpContext, + Guid id) + { return MakeThumbnailImage(configuration, httpContext, id, "user"); } - public static models.Image MakeLabelThumbnailImage(IRoadieSettings configuration, IHttpContext httpContext, Guid id) - { - return MakeThumbnailImage(configuration, httpContext, id, "label"); - } + public static models.Image MakeLabelThumbnailImage(IRoadieSettings configuration, + IHttpContext httpContext, + Guid id) + { return MakeThumbnailImage(configuration, httpContext, id, "label"); } - public static models.Image MakeArtistThumbnailImage(IRoadieSettings configuration, IHttpContext httpContext, Guid? id) + public static models.Image MakeArtistThumbnailImage(IRoadieSettings configuration, + IHttpContext httpContext, + Guid? id) { - if (!id.HasValue) return null; + if(!id.HasValue) + return null; return MakeThumbnailImage(configuration, httpContext, id.Value, "artist"); } - public static models.Image MakeCollectionThumbnailImage(IRoadieSettings configuration, IHttpContext httpContext, Guid id) + public static models.Image MakeCollectionThumbnailImage(IRoadieSettings configuration, + IHttpContext httpContext, + Guid id) + { return MakeThumbnailImage(configuration, httpContext, id, "collection"); } + + public static models.Image MakeFullsizeImage(IRoadieSettings configuration, + IHttpContext httpContext, + Guid id, + string caption = null) { - return MakeThumbnailImage(configuration, httpContext, id, "collection"); + return new models.Image($"{httpContext.ImageBaseUrl}/{id}", + caption, + $"{httpContext.ImageBaseUrl}/{id}/{configuration.SmallImageSize.Width}/{configuration.SmallImageSize.Height}"); } - public static models.Image MakeFullsizeImage(IRoadieSettings configuration, IHttpContext httpContext, Guid id, string caption = null) + public static models.Image MakeFullsizeSecondaryImage(IRoadieSettings configuration, + IHttpContext httpContext, + Guid id, + ImageType type, + int imageId, + string caption = null) { - return new models.Image($"{httpContext.ImageBaseUrl}/{id}", caption, - $"{httpContext.ImageBaseUrl}/{id}/{configuration.SmallImageSize.Width}/{configuration.SmallImageSize.Height}"); - } - - public static models.Image MakeFullsizeSecondaryImage(IRoadieSettings configuration, IHttpContext httpContext, Guid id, ImageType type, int imageId, string caption = null) - { - if (type == ImageType.ArtistSecondary) + if(type == ImageType.ArtistSecondary) { - return new models.Image($"{httpContext.ImageBaseUrl}/artist-secondary/{id}/{imageId}", caption, $"{httpContext.ImageBaseUrl}/artist-secondary/{id}/{imageId}/{configuration.SmallImageSize.Width}/{configuration.SmallImageSize.Height}"); + return new models.Image($"{httpContext.ImageBaseUrl}/artist-secondary/{id}/{imageId}", + caption, + $"{httpContext.ImageBaseUrl}/artist-secondary/{id}/{imageId}/{configuration.SmallImageSize.Width}/{configuration.SmallImageSize.Height}"); } - return new models.Image($"{httpContext.ImageBaseUrl}/release-secondary/{id}/{imageId}", caption, $"{httpContext.ImageBaseUrl}/release-secondary/{id}/{imageId}/{configuration.SmallImageSize.Width}/{configuration.SmallImageSize.Height}"); + return new models.Image($"{httpContext.ImageBaseUrl}/release-secondary/{id}/{imageId}", + caption, + $"{httpContext.ImageBaseUrl}/release-secondary/{id}/{imageId}/{configuration.SmallImageSize.Width}/{configuration.SmallImageSize.Height}"); } - public static models.Image MakeGenreThumbnailImage(IRoadieSettings configuration, IHttpContext httpContext, Guid id) - { - return MakeThumbnailImage(configuration, httpContext, id, "genre"); - } + public static models.Image MakeGenreThumbnailImage(IRoadieSettings configuration, + IHttpContext httpContext, + Guid id) + { return MakeThumbnailImage(configuration, httpContext, id, "genre"); } public static models.Image MakeUnknownImage(IHttpContext httpContext, string unknownType = "unknown") - { - return new models.Image($"{httpContext.ImageBaseUrl}/{ unknownType }.jpg"); - } + { return new models.Image($"{httpContext.ImageBaseUrl}/{ unknownType }.jpg"); } - public static models.Image MakeImage(IRoadieSettings configuration, IHttpContext httpContext, Guid id, int width = 200, int height = 200, string caption = null, bool includeCachebuster = false) + public static models.Image MakeImage(IRoadieSettings configuration, + IHttpContext httpContext, + Guid id, + int width = 200, + int height = 200, + string caption = null, + bool includeCachebuster = false) { return new models.Image($"{httpContext.ImageBaseUrl}/{id}/{width}/{height}/{(includeCachebuster ? DateTime.UtcNow.Ticks.ToString() : string.Empty)}", - caption, - $"{httpContext.ImageBaseUrl}/{id}/{configuration.SmallImageSize.Width}/{configuration.SmallImageSize.Height}"); + caption, + $"{httpContext.ImageBaseUrl}/{id}/{configuration.SmallImageSize.Width}/{configuration.SmallImageSize.Height}"); } - public static models.Image MakeImage(IRoadieSettings configuration, IHttpContext httpContext, Guid id, string type, IImageSize imageSize) - { - return MakeImage(configuration, httpContext, id, type, imageSize.Width, imageSize.Height); - } + public static models.Image MakeImage(IRoadieSettings configuration, + IHttpContext httpContext, + Guid id, + string type, + IImageSize imageSize) + { return MakeImage(configuration, httpContext, id, type, imageSize.Width, imageSize.Height); } - public static models.Image MakeImage(IRoadieSettings configuration, IHttpContext httpContext, Guid id, string type, int? width, int? height, string caption = null, bool includeCachebuster = false) + public static models.Image MakeImage(IRoadieSettings configuration, + IHttpContext httpContext, + Guid id, + string type, + int? width, + int? height, + string caption = null, + bool includeCachebuster = false) { - if (width.HasValue && height.HasValue && (width.Value != configuration.ThumbnailImageSize.Width || - height.Value != configuration.ThumbnailImageSize.Height)) - return new models.Image( - $"{httpContext.ImageBaseUrl}/{type}/{id}/{width}/{height}/{(includeCachebuster ? DateTime.UtcNow.Ticks.ToString() : string.Empty)}", - caption, - $"{httpContext.ImageBaseUrl}/{type}/{id}/{configuration.ThumbnailImageSize.Width}/{configuration.ThumbnailImageSize.Height}"); + if(width.HasValue && + height.HasValue && + (width.Value != configuration.ThumbnailImageSize.Width || + height.Value != configuration.ThumbnailImageSize.Height)) + return new models.Image($"{httpContext.ImageBaseUrl}/{type}/{id}/{width}/{height}/{(includeCachebuster ? DateTime.UtcNow.Ticks.ToString() : string.Empty)}", + caption, + $"{httpContext.ImageBaseUrl}/{type}/{id}/{configuration.ThumbnailImageSize.Width}/{configuration.ThumbnailImageSize.Height}"); return new models.Image($"{httpContext.ImageBaseUrl}/{type}/{id}", caption, null); } } diff --git a/Roadie.Api.Library/Inspect/Inspector.cs b/Roadie.Api.Library/Inspect/Inspector.cs index e3c90f1..dcce8da 100644 --- a/Roadie.Api.Library/Inspect/Inspector.cs +++ b/Roadie.Api.Library/Inspect/Inspector.cs @@ -26,7 +26,7 @@ namespace Roadie.Library.Inspect { public class Inspector { - private static readonly string Salt = "6856F2EE-5965-4345-884B-2CCA457AAF59"; + private const string Salt = "6856F2EE-5965-4345-884B-2CCA457AAF59"; private IEnumerable _directoryPlugins; private IEnumerable _filePlugins; @@ -49,13 +49,15 @@ namespace Roadie.Library.Inspect foreach (var t in types) if (t.GetInterface("IInspectorDirectoryPlugin") != null && !t.IsAbstract && !t.IsInterface) { - var plugin = - Activator.CreateInstance(t, Configuration, CacheManager, Logger, TagsHelper) as - IInspectorDirectoryPlugin; + var plugin = Activator.CreateInstance(t, Configuration, CacheManager, Logger, TagsHelper) as IInspectorDirectoryPlugin; if (plugin.IsEnabled) + { plugins.Add(plugin); + } else + { Console.WriteLine($"╠╣ Not Loading Disabled Plugin [{plugin.Description}]"); + } } } catch (Exception ex) @@ -84,17 +86,20 @@ namespace Roadie.Library.Inspect .SelectMany(s => s.GetTypes()) .Where(p => type.IsAssignableFrom(p)); foreach (var t in types) + { if (t.GetInterface("IInspectorFilePlugin") != null && !t.IsAbstract && !t.IsInterface) { - var plugin = - Activator.CreateInstance(t, Configuration, CacheManager, Logger, TagsHelper) as - IInspectorFilePlugin; - + var plugin = Activator.CreateInstance(t, Configuration, CacheManager, Logger, TagsHelper) as IInspectorFilePlugin; if (plugin.IsEnabled) + { plugins.Add(plugin); + } else + { Console.WriteLine($"╠╣ Not Loading Disabled Plugin [{plugin.Description}]"); + } } + } } catch (Exception ex) { @@ -147,10 +152,12 @@ namespace Roadie.Library.Inspect var numbers = 0; var bytes = System.Text.Encoding.ASCII.GetBytes(input); var looper = bytes.Length / 4; - for (var i = 0; i < looper; i++) numbers += BitConverter.ToInt32(bytes, i * 4); + for (var i = 0; i < looper; i++) + { + numbers += BitConverter.ToInt32(bytes, i * 4); + } if (numbers < 0) numbers *= -1; - var token = hashids.Encode(numbers); - return token; + return hashids.Encode(numbers); } public void Inspect(bool doCopy, bool isReadOnly, string directoryToInspect, string destination, bool dontAppendSubFolder, bool dontDeleteEmptyFolders, bool dontRunPreScripts) @@ -171,15 +178,15 @@ namespace Roadie.Library.Inspect string scriptResult = null; // Run PreInspect script - if(dontRunPreScripts) + if (dontRunPreScripts) { Console.BackgroundColor = ConsoleColor.Blue; Console.ForegroundColor = ConsoleColor.White; - Console.WriteLine($"Skipping PreInspectScript."); + Console.WriteLine("Skipping PreInspectScript."); Console.ResetColor(); } else - { + { scriptResult = RunScript(Configuration.Processing.PreInspectScript, doCopy, isReadOnly, directoryToInspect, destination); if (!string.IsNullOrEmpty(scriptResult)) { @@ -217,7 +224,7 @@ namespace Roadie.Library.Inspect // Get all the MP3 files in 'directory' var files = Directory.GetFiles(directory, "*.mp3", SearchOption.TopDirectoryOnly); - if (files != null && files.Any()) + if (files?.Any() == true) { if (!isReadOnly && !createdDestinationFolder && !Directory.Exists(dest)) { @@ -226,20 +233,19 @@ namespace Roadie.Library.Inspect } // Run directory plugins against current directory - foreach (var plugin in DirectoryPlugins.Where(x => !x.IsPostProcessingPlugin) - .OrderBy(x => x.Order)) + 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) { - Console.WriteLine( - $"📛 Plugin Failed: Error [{JsonConvert.SerializeObject(pluginResult)}]"); + Console.WriteLine($"📛 Plugin Failed: Error [{JsonConvert.SerializeObject(pluginResult)}]"); return; } - if (!string.IsNullOrEmpty(pluginResult.Data)) + { Console.WriteLine($"╠╣ Directory Plugin Message: {pluginResult.Data}"); + } } Console.WriteLine("╠╝"); @@ -263,8 +269,7 @@ namespace Roadie.Library.Inspect if (!originalMetaData.IsValid) { Console.ForegroundColor = ConsoleColor.DarkYellow; - Console.WriteLine( - $"╟ ❗ INVALID: Missing: {ID3TagsHelper.DetermineMissingRequiredMetaData(originalMetaData)}"); + Console.WriteLine($"╟ ❗ INVALID: Missing: {ID3TagsHelper.DetermineMissingRequiredMetaData(originalMetaData)}"); Console.WriteLine($"╟ [{JsonConvert.SerializeObject(tagLib, Newtonsoft.Json.Formatting.Indented)}]"); Console.ResetColor(); } @@ -274,13 +279,11 @@ namespace Roadie.Library.Inspect foreach (var plugin in FilePlugins.OrderBy(x => x.Order)) { Console.WriteLine($"╟┤ Running File Plugin {plugin.Description}"); - OperationResult pluginResult = null; - pluginResult = plugin.Process(pluginMetaData); + OperationResult pluginResult = plugin.Process(pluginMetaData); if (!pluginResult.IsSuccess) { Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine( - $"📛 Plugin Failed: Error [{JsonConvert.SerializeObject(pluginResult)}]"); + Console.WriteLine($"📛 Plugin Failed: Error [{JsonConvert.SerializeObject(pluginResult)}]"); Console.ResetColor(); return; } @@ -291,8 +294,7 @@ namespace Roadie.Library.Inspect if (!pluginMetaData.IsValid) { Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine( - $"╟ ❗ INVALID: Missing: {ID3TagsHelper.DetermineMissingRequiredMetaData(pluginMetaData)}"); + Console.WriteLine($"╟ ❗ INVALID: Missing: {ID3TagsHelper.DetermineMissingRequiredMetaData(pluginMetaData)}"); Console.ResetColor(); return; } @@ -301,16 +303,17 @@ namespace Roadie.Library.Inspect if (originalMetaData != null && pluginMetaData != null) { var differences = Comparer.Compare(originalMetaData, pluginMetaData); - if (differences.Any()) + if (differences.Count > 0) { - var skipDifferences = new List - {"AudioMetaDataWeights", "FileInfo", "Images", "TrackArtists"}; + var skipDifferences = new List { "AudioMetaDataWeights", "FileInfo", "Images", "TrackArtists" }; var differencesDescription = $"{Environment.NewLine}"; foreach (var difference in differences) { - if (skipDifferences.Contains(difference.Name)) continue; - differencesDescription += - $"╟ || {difference.Name} : Was [{difference.OldValue}] Now [{difference.NewValue}]{Environment.NewLine}"; + if (skipDifferences.Contains(difference.Name)) + { + continue; + } + differencesDescription += $"╟ || {difference.Name} : Was [{difference.OldValue}] Now [{difference.NewValue}]{Environment.NewLine}"; } Console.Write($"╟ ≡ != ID3 Tag Modified: {differencesDescription}"); @@ -339,15 +342,13 @@ namespace Roadie.Library.Inspect { var oBad = originalMetaData == null; var pBad = pluginMetaData == null; - Console.WriteLine( - $"╟ !! MetaData comparison skipped. {(oBad ? "Pre MetaData is Invalid" : "")} {(pBad ? "Post MetaData is Invalid" : "")}"); + Console.WriteLine($"╟ !! MetaData comparison skipped. {(oBad ? "Pre MetaData is Invalid" : string.Empty)} {(pBad ? "Post MetaData is Invalid" : string.Empty)}"); } if (!pluginMetaData.IsValid) { Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine( - $"╟ ❗ INVALID: Missing: {ID3TagsHelper.DetermineMissingRequiredMetaData(pluginMetaData)}"); + Console.WriteLine($"╟ ❗ INVALID: Missing: {ID3TagsHelper.DetermineMissingRequiredMetaData(pluginMetaData)}"); Console.ResetColor(); } else @@ -357,11 +358,16 @@ namespace Roadie.Library.Inspect Console.ResetColor(); var artistToken = ArtistInspectorToken(tagLib.Data); - if (!artistsFound.Contains(artistToken)) artistsFound.Add(artistToken); + 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"; + 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 @@ -371,40 +377,35 @@ namespace Roadie.Library.Inspect ? 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(); - 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, ImageType.Artist, SearchOption.TopDirectoryOnly)); - foundArtistImages.AddRange(ImageHelper.FindImageTypeInDirectory( - directoryInfo.Parent, ImageType.ArtistSecondary, - SearchOption.TopDirectoryOnly)); - foundArtistImages.AddRange(ImageHelper.FindImageTypeInDirectory(directoryInfo, - ImageType.Artist, SearchOption.TopDirectoryOnly)); - foundArtistImages.AddRange(ImageHelper.FindImageTypeInDirectory(directoryInfo, - ImageType.ArtistSecondary, SearchOption.TopDirectoryOnly)); + 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, ImageType.Artist, SearchOption.TopDirectoryOnly)); + foundArtistImages.AddRange(ImageHelper.FindImageTypeInDirectory(directoryInfo.Parent, ImageType.ArtistSecondary, SearchOption.TopDirectoryOnly)); + foundArtistImages.AddRange(ImageHelper.FindImageTypeInDirectory(directoryInfo, ImageType.Artist, SearchOption.TopDirectoryOnly)); + foundArtistImages.AddRange(ImageHelper.FindImageTypeInDirectory(directoryInfo, 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(); - foundReleaseImages.AddRange( - ImageHelper.FindImagesByName(directoryInfo, tagLib.Data.Release)); - foundReleaseImages.AddRange( - ImageHelper.FindImageTypeInDirectory(directoryInfo, ImageType.Release)); - foundReleaseImages.AddRange( - ImageHelper.FindImageTypeInDirectory(directoryInfo, - ImageType.ReleaseSecondary)); + foundReleaseImages.AddRange(ImageHelper.FindImagesByName(directoryInfo, tagLib.Data.Release)); + foundReleaseImages.AddRange(ImageHelper.FindImageTypeInDirectory(directoryInfo, ImageType.Release)); + foundReleaseImages.AddRange(ImageHelper.FindImageTypeInDirectory(directoryInfo, ImageType.ReleaseSecondary)); foreach (var foundReleaseImage in foundReleaseImages) + { InspectImage(isReadOnly, doCopy, dest, subDirectory, foundReleaseImage); + } inspectedImagesInDirectories.Add(directoryInfo.FullName); } @@ -412,8 +413,7 @@ namespace Roadie.Library.Inspect var newPath = Path.Combine(dest, subDirectory, newFileName.ToFileNameFriendly()); if (isReadOnly) { - Console.WriteLine( - $"╟ 🔒 Read Only Mode: File would be [{(doCopy ? "Copied" : "Moved")}] to [{newPath}]"); + Console.WriteLine($"╟ 🔒 Read Only Mode: File would be [{(doCopy ? "Copied" : "Moved")}] to [{newPath}]"); } else { @@ -464,8 +464,7 @@ namespace Roadie.Library.Inspect Console.WriteLine("╠╝"); sw.Stop(); - Console.WriteLine( - $"╚═ Elapsed Time {sw.ElapsedMilliseconds.ToString("0000000")}, Artists {artistsFound.Count()}, Releases {releasesFound.Count()}, MP3s {mp3FilesFoundCount} ═╝"); + Console.WriteLine($"╚═ Elapsed Time {sw.ElapsedMilliseconds.ToString("0000000")}, Artists {artistsFound.Count}, Releases {releasesFound.Count}, MP3s {mp3FilesFoundCount} ═╝"); } catch (Exception ex) { @@ -492,8 +491,7 @@ namespace Roadie.Library.Inspect { Console.BackgroundColor = ConsoleColor.Blue; Console.ForegroundColor = ConsoleColor.White; - Console.WriteLine( - $"PostInspectScript Results: {Environment.NewLine + scriptResult + Environment.NewLine}"); + Console.WriteLine($"PostInspectScript Results: {Environment.NewLine + scriptResult + Environment.NewLine}"); Console.ResetColor(); } } @@ -521,17 +519,22 @@ namespace Roadie.Library.Inspect looper++; newImagePath = Path.Combine(dest, subdirectory, looper.ToString("00"), image.Name); } - 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 + { try { if (!doCopy) + { image.MoveTo(newImagePath); + } else + { image.CopyTo(newImagePath, true); + } Console.ForegroundColor = ConsoleColor.DarkYellow; Console.WriteLine($"╠═ 🚛 {(doCopy ? "Copied" : "Moved")} Image File to [{newImagePath}]"); } @@ -539,9 +542,9 @@ namespace Roadie.Library.Inspect { Logger.LogError(ex); Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine( - $"📛 Error file [{image.FullName}], newImagePath [{newImagePath}], Exception: [{ex}]"); + Console.WriteLine($"📛 Error file [{image.FullName}], newImagePath [{newImagePath}], Exception: [{ex}]"); } + } } Console.ResetColor(); @@ -588,9 +591,17 @@ namespace Roadie.Library.Inspect 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 + Environment.NewLine; + var r = string.Empty; + var results = ps.AddScript(script) + .AddParameter("DoCopy", doCopy) + .AddParameter("IsReadOnly", isReadOnly) + .AddParameter("DirectoryToInspect", directoryToInspect) + .AddParameter("Dest", dest) + .Invoke(); + foreach (var result in results) + { + r += result + Environment.NewLine; + } return r; } } @@ -604,7 +615,6 @@ namespace Roadie.Library.Inspect public class LoggingTraceListener : TraceListener { - public override void Write(string message) { Console.WriteLine($"╠╬═ { message }"); @@ -614,7 +624,5 @@ namespace Roadie.Library.Inspect { Console.WriteLine($"╠╬═ { message }"); } - } - } \ No newline at end of file diff --git a/Roadie.Api.Library/Scrobble/LastFMScrobbler.cs b/Roadie.Api.Library/Scrobble/LastFMScrobbler.cs index e47f59f..dca205d 100644 --- a/Roadie.Api.Library/Scrobble/LastFMScrobbler.cs +++ b/Roadie.Api.Library/Scrobble/LastFMScrobbler.cs @@ -33,7 +33,7 @@ namespace Roadie.Library.Scrobble /// indication of what music player they're using. /// /// - public override async Task> NowPlaying(User roadieUser, ScrobbleInfo scrobble) => await LastFmHelper.NowPlaying(roadieUser, scrobble); + public override async Task> NowPlaying(User roadieUser, ScrobbleInfo scrobble) => await LastFmHelper.NowPlaying(roadieUser, scrobble).ConfigureAwait(false); /// /// Send a Scrobble Request @@ -42,7 +42,7 @@ namespace Roadie.Library.Scrobble /// listening history and generate personalised charts and recommendations (and more). /// /// - public override async Task> Scrobble(User roadieUser, ScrobbleInfo scrobble) => await LastFmHelper.Scrobble(roadieUser, scrobble); + public override async Task> Scrobble(User roadieUser, ScrobbleInfo scrobble) => await LastFmHelper.Scrobble(roadieUser, scrobble).ConfigureAwait(false); } } \ No newline at end of file diff --git a/Roadie.Api.Library/Scrobble/RoadieScrobbler.cs b/Roadie.Api.Library/Scrobble/RoadieScrobbler.cs index 65a5cb7..b06962b 100644 --- a/Roadie.Api.Library/Scrobble/RoadieScrobbler.cs +++ b/Roadie.Api.Library/Scrobble/RoadieScrobbler.cs @@ -37,7 +37,7 @@ namespace Roadie.Library.Scrobble { Data = true, IsSuccess = true - }); + }).ConfigureAwait(false); } /// @@ -59,12 +59,13 @@ namespace Roadie.Library.Scrobble }; } var sw = Stopwatch.StartNew(); - var track = DbContext.Tracks + var track = await DbContext.Tracks .Include(x => x.ReleaseMedia) .Include(x => x.ReleaseMedia.Release) .Include(x => x.ReleaseMedia.Release.Artist) .Include(x => x.TrackArtist) - .FirstOrDefault(x => x.RoadieId == scrobble.TrackId); + .FirstOrDefaultAsync(x => x.RoadieId == scrobble.TrackId) + .ConfigureAwait(false); if (track == null) { return new OperationResult($"Scrobble: Unable To Find Track [{scrobble.TrackId}]"); @@ -80,8 +81,8 @@ namespace Roadie.Library.Scrobble { if (roadieUser != null) { - var user = DbContext.Users.FirstOrDefault(x => x.RoadieId == roadieUser.UserId); - userTrack = DbContext.UserTracks.FirstOrDefault(x => x.UserId == user.Id && x.TrackId == track.Id); + var user = await DbContext.Users.FirstOrDefaultAsync(x => x.RoadieId == roadieUser.UserId).ConfigureAwait(false); + userTrack = await DbContext.UserTracks.FirstOrDefaultAsync(x => x.UserId == user.Id && x.TrackId == track.Id).ConfigureAwait(false); if (userTrack == null) { userTrack = new data.UserTrack(now) @@ -89,7 +90,7 @@ namespace Roadie.Library.Scrobble UserId = user.Id, TrackId = track.Id }; - DbContext.UserTracks.Add(userTrack); + await DbContext.UserTracks.AddAsync(userTrack).ConfigureAwait(false); } userTrack.LastPlayed = now; @@ -101,20 +102,21 @@ namespace Roadie.Library.Scrobble track.PlayedCount = (track.PlayedCount ?? 0) + 1; track.LastPlayed = now; - var release = DbContext.Releases + var release = await DbContext.Releases .Include(x => x.Artist) - .FirstOrDefault(x => x.RoadieId == track.ReleaseMedia.Release.RoadieId); + .FirstOrDefaultAsync(x => x.RoadieId == track.ReleaseMedia.Release.RoadieId) + .ConfigureAwait(false); release.LastPlayed = now; release.PlayedCount = (release.PlayedCount ?? 0) + 1; - var artist = DbContext.Artists.FirstOrDefault(x => x.RoadieId == release.Artist.RoadieId); + var artist = await DbContext.Artists.FirstOrDefaultAsync(x => x.RoadieId == release.Artist.RoadieId).ConfigureAwait(false); artist.LastPlayed = now; artist.PlayedCount = (artist.PlayedCount ?? 0) + 1; data.Artist trackArtist = null; if (track.ArtistId.HasValue) { - trackArtist = DbContext.Artists.FirstOrDefault(x => x.Id == track.ArtistId); + trackArtist = await DbContext.Artists.FirstOrDefaultAsync(x => x.Id == track.ArtistId).ConfigureAwait(false); if (trackArtist != null) { trackArtist.LastPlayed = now; @@ -127,7 +129,7 @@ namespace Roadie.Library.Scrobble } } - await DbContext.SaveChangesAsync(); + await DbContext.SaveChangesAsync().ConfigureAwait(false); CacheManager.ClearRegion(track.CacheRegion); CacheManager.ClearRegion(track.ReleaseMedia.Release.CacheRegion); diff --git a/Roadie.Api.Library/Scrobble/ScrobbleHandler.cs b/Roadie.Api.Library/Scrobble/ScrobbleHandler.cs index 553095d..e70ef6d 100644 --- a/Roadie.Api.Library/Scrobble/ScrobbleHandler.cs +++ b/Roadie.Api.Library/Scrobble/ScrobbleHandler.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.Logging; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; using Roadie.Library.Caching; using Roadie.Library.Configuration; using Roadie.Library.Data.Context; @@ -10,7 +11,6 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using data = Roadie.Library.Data; namespace Roadie.Library.Scrobble { @@ -31,6 +31,18 @@ namespace Roadie.Library.Scrobble private IEnumerable Scrobblers { get; } + public ScrobbleHandler(IRoadieSettings configuration, ILogger logger, IRoadieDbContext dbContext, ICacheManager cacheManager, RoadieScrobbler roadieScrobbler) + { + Logger = logger; + Configuration = configuration; + DbContext = dbContext; + var scrobblers = new List + { + roadieScrobbler + }; + Scrobblers = scrobblers; + } + public ScrobbleHandler(IRoadieSettings configuration, ILogger logger, IRoadieDbContext dbContext, ICacheManager cacheManager, IHttpEncoder httpEncoder, IHttpContext httpContext, ILastFmHelper lastFmHelper, IRoadieScrobbler roadieScrobbler, ILastFMScrobbler lastFMScrobbler) @@ -51,16 +63,28 @@ namespace Roadie.Library.Scrobble Scrobblers = scrobblers; } - public ScrobbleHandler(IRoadieSettings configuration, ILogger logger, IRoadieDbContext dbContext, ICacheManager cacheManager, RoadieScrobbler roadieScrobbler) + private async Task GetScrobbleInfoDetailsAsync(ScrobbleInfo scrobble) { - Logger = logger; - Configuration = configuration; - DbContext = dbContext; - var scrobblers = new List - { - roadieScrobbler - }; - Scrobblers = scrobblers; + var scrobbleInfo = await (from t in DbContext.Tracks + join rm in DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id + join r in DbContext.Releases on rm.ReleaseId equals r.Id + join a in DbContext.Artists on r.ArtistId equals a.Id + where t.RoadieId == scrobble.TrackId + select new + { + ArtistName = a.Name, + ReleaseTitle = r.Title, + TrackTitle = t.Title, + t.TrackNumber, + t.Duration + }).FirstOrDefaultAsync().ConfigureAwait(false); + + scrobble.ArtistName = scrobbleInfo.ArtistName; + scrobble.ReleaseTitle = scrobbleInfo.ReleaseTitle; + scrobble.TrackTitle = scrobbleInfo.TrackTitle; + scrobble.TrackNumber = scrobbleInfo.TrackNumber.ToString(); + scrobble.TrackDuration = TimeSpan.FromMilliseconds(scrobbleInfo.Duration ?? 0); + return scrobble; } /// @@ -68,8 +92,11 @@ namespace Roadie.Library.Scrobble /// public async Task> NowPlaying(User user, ScrobbleInfo scrobble) { - var s = GetScrobbleInfoDetails(scrobble); - foreach (var scrobbler in Scrobblers) await Task.Run(async () => await scrobbler.NowPlaying(user, s)); + var s = await GetScrobbleInfoDetailsAsync(scrobble).ConfigureAwait(false); + foreach (var scrobbler in Scrobblers) + { + await scrobbler.NowPlaying(user, s).ConfigureAwait(false); + } return new OperationResult { Data = true, @@ -82,10 +109,10 @@ namespace Roadie.Library.Scrobble /// public async Task> Scrobble(User user, ScrobbleInfo scrobble) { - var s = GetScrobbleInfoDetails(scrobble); + var s = await GetScrobbleInfoDetailsAsync(scrobble).ConfigureAwait(false); foreach (var scrobbler in Scrobblers) { - await Task.Run(async () => await scrobbler.Scrobble(user, s)); + await scrobbler.Scrobble(user, s).ConfigureAwait(false); } return new OperationResult { @@ -93,29 +120,5 @@ namespace Roadie.Library.Scrobble IsSuccess = true }; } - - private ScrobbleInfo GetScrobbleInfoDetails(ScrobbleInfo scrobble) - { - var scrobbleInfo = (from t in DbContext.Tracks - join rm in DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id - join r in DbContext.Releases on rm.ReleaseId equals r.Id - join a in DbContext.Artists on r.ArtistId equals a.Id - where t.RoadieId == scrobble.TrackId - select new - { - ArtistName = a.Name, - ReleaseTitle = r.Title, - TrackTitle = t.Title, - t.TrackNumber, - t.Duration - }).FirstOrDefault(); - - scrobble.ArtistName = scrobbleInfo.ArtistName; - scrobble.ReleaseTitle = scrobbleInfo.ReleaseTitle; - scrobble.TrackTitle = scrobbleInfo.TrackTitle; - scrobble.TrackNumber = scrobbleInfo.TrackNumber.ToString(); - scrobble.TrackDuration = TimeSpan.FromMilliseconds(scrobbleInfo.Duration ?? 0); - return scrobble; - } } } \ No newline at end of file diff --git a/Roadie.Api.Library/SearchEngines/Imaging/BingImageSearchEngine.cs b/Roadie.Api.Library/SearchEngines/Imaging/BingImageSearchEngine.cs index 9fc7be5..c1c2230 100644 --- a/Roadie.Api.Library/SearchEngines/Imaging/BingImageSearchEngine.cs +++ b/Roadie.Api.Library/SearchEngines/Imaging/BingImageSearchEngine.cs @@ -44,11 +44,11 @@ namespace Roadie.Library.SearchEngines.Imaging return request; } - public override async Task> PerformImageSearch(string query, int resultsCount) + public override async Task> PerformImageSearchAsync(string query, int resultsCount) { var request = BuildRequest(query, resultsCount); - var response = await _client.ExecuteAsync(request); + var response = await _client.ExecuteAsync(request).ConfigureAwait(false); if (response.StatusCode == HttpStatusCode.Unauthorized) throw new AuthenticationException("Api Key is not correct"); diff --git a/Roadie.Api.Library/SearchEngines/Imaging/IImageSearchEngine.cs b/Roadie.Api.Library/SearchEngines/Imaging/IImageSearchEngine.cs index 3bf24a2..1c7b3f7 100644 --- a/Roadie.Api.Library/SearchEngines/Imaging/IImageSearchEngine.cs +++ b/Roadie.Api.Library/SearchEngines/Imaging/IImageSearchEngine.cs @@ -10,6 +10,6 @@ namespace Roadie.Library.SearchEngines.Imaging RestRequest BuildRequest(string query, int resultsCount); - Task> PerformImageSearch(string query, int resultsCount); + Task> PerformImageSearchAsync(string query, int resultsCount); } } \ No newline at end of file diff --git a/Roadie.Api.Library/SearchEngines/Imaging/ITunesSearchEngine.cs b/Roadie.Api.Library/SearchEngines/Imaging/ITunesSearchEngine.cs index 42a50b2..20be588 100644 --- a/Roadie.Api.Library/SearchEngines/Imaging/ITunesSearchEngine.cs +++ b/Roadie.Api.Library/SearchEngines/Imaging/ITunesSearchEngine.cs @@ -25,14 +25,14 @@ namespace Roadie.Library.SearchEngines.Imaging public override bool IsEnabled => Configuration.Integrations.ITunesProviderEnabled; - public async Task>> PerformArtistSearch(string query, int resultsCount) + public async Task>> PerformArtistSearchAsync(string query, int resultsCount) { ArtistSearchResult data = null; try { var request = BuildRequest(query, 1, "musicArtist"); - var response = await _client.ExecuteAsync(request); + var response = await _client.ExecuteAsync(request).ConfigureAwait(false); if (response.ResponseStatus == ResponseStatus.Error) { if (response.StatusCode == HttpStatusCode.Unauthorized) @@ -79,7 +79,7 @@ namespace Roadie.Library.SearchEngines.Imaging #pragma warning disable CS1998 - public override async Task> PerformImageSearch(string query, int resultsCount) + public override async Task> PerformImageSearchAsync(string query, int resultsCount) { var request = BuildRequest(query, resultsCount); ImageSearchResult[] result = null; @@ -116,7 +116,7 @@ namespace Roadie.Library.SearchEngines.Imaging public async Task>> PerformReleaseSearch(string artistName, string query, int resultsCount) { var request = BuildRequest(query, 1, "album"); - var response = await _client.ExecuteAsync(request); + var response = await _client.ExecuteAsync(request).ConfigureAwait(false); if (response.ResponseStatus == ResponseStatus.Error) { if (response.StatusCode == HttpStatusCode.Unauthorized) diff --git a/Roadie.Api.Library/SearchEngines/Imaging/ImageSearchEngineBase.cs b/Roadie.Api.Library/SearchEngines/Imaging/ImageSearchEngineBase.cs index f53627b..278b061 100644 --- a/Roadie.Api.Library/SearchEngines/Imaging/ImageSearchEngineBase.cs +++ b/Roadie.Api.Library/SearchEngines/Imaging/ImageSearchEngineBase.cs @@ -50,7 +50,7 @@ namespace Roadie.Library.SearchEngines.Imaging public abstract RestRequest BuildRequest(string query, int resultsCount); - public virtual Task> PerformImageSearch(string query, int resultsCount) + public virtual Task> PerformImageSearchAsync(string query, int resultsCount) { throw new NotImplementedException(); } diff --git a/Roadie.Api.Library/SearchEngines/Imaging/ImageSearchManager.cs b/Roadie.Api.Library/SearchEngines/Imaging/ImageSearchManager.cs index 1f81f75..977446a 100644 --- a/Roadie.Api.Library/SearchEngines/Imaging/ImageSearchManager.cs +++ b/Roadie.Api.Library/SearchEngines/Imaging/ImageSearchManager.cs @@ -37,7 +37,7 @@ namespace Roadie.Library.SearchEngines.Imaging } if (Configuration.Integrations.BingImageSearchEngineEnabled) { - var bingResults = await _bingSearchEngine.PerformImageSearch(query, count); + var bingResults = await _bingSearchEngine.PerformImageSearchAsync(query, count).ConfigureAwait(false); if (bingResults != null) { result.AddRange(bingResults); @@ -45,7 +45,7 @@ namespace Roadie.Library.SearchEngines.Imaging } if (Configuration.Integrations.ITunesProviderEnabled) { - var iTunesResults = await _itunesSearchEngine.PerformImageSearch(query, count); + var iTunesResults = await _itunesSearchEngine.PerformImageSearchAsync(query, count).ConfigureAwait(false); if (iTunesResults != null) { result.AddRange(iTunesResults); diff --git a/Roadie.Api.Library/SearchEngines/MetaData/Audio/AudioMetaDataHelper.cs b/Roadie.Api.Library/SearchEngines/MetaData/Audio/AudioMetaDataHelper.cs index f6d550a..6822228 100644 --- a/Roadie.Api.Library/SearchEngines/MetaData/Audio/AudioMetaDataHelper.cs +++ b/Roadie.Api.Library/SearchEngines/MetaData/Audio/AudioMetaDataHelper.cs @@ -103,11 +103,11 @@ namespace Roadie.Library.MetaData.Audio if (!result.IsValid) { tagSources.Add("MusicBrainz"); - result = await ParseFromMusicBrainz(result); + result = await ParseFromMusicBrainzAsync(result).ConfigureAwait(false); if (!result.IsValid) { tagSources.Add("LastFm"); - result = await GetFromLastFmIntegration(result); + result = await GetFromLastFmIntegrationAsync(result).ConfigureAwait(false); } } @@ -183,15 +183,18 @@ namespace Roadie.Library.MetaData.Audio return result; } - private async Task GetFromLastFmIntegration(AudioMetaData metaData) + private async Task GetFromLastFmIntegrationAsync(AudioMetaData metaData) { var artistName = metaData.Artist; var ReleaseName = metaData.Release; if (DoParseFromLastFM) { - if (string.IsNullOrEmpty(artistName) && string.IsNullOrEmpty(ReleaseName)) return metaData; - var lastFmReleaseTracks = await LastFmHelper.TracksForRelease(artistName, ReleaseName); + if (string.IsNullOrEmpty(artistName) && string.IsNullOrEmpty(ReleaseName)) + { + return metaData; + } + var lastFmReleaseTracks = await LastFmHelper.TracksForReleaseAsync(artistName, ReleaseName).ConfigureAwait(false); if (lastFmReleaseTracks != null) { var lastFmReleaseTrack = lastFmReleaseTracks.FirstOrDefault(x => @@ -224,12 +227,11 @@ namespace Roadie.Library.MetaData.Audio return metaData; } - private async Task ParseFromMusicBrainz(AudioMetaData metaData) + private async Task ParseFromMusicBrainzAsync(AudioMetaData metaData) { if (DoParseFromMusicBrainz) { - var musicBrainzReleaseTracks = - await MusicBrainzProvider.MusicBrainzReleaseTracks(metaData.Artist, metaData.Release); + var musicBrainzReleaseTracks = await MusicBrainzProvider.MusicBrainzReleaseTracksAsync(metaData.Artist, metaData.Release).ConfigureAwait(false); if (musicBrainzReleaseTracks != null) { var musicBrainzReleaseTrack = musicBrainzReleaseTracks.FirstOrDefault(x => diff --git a/Roadie.Api.Library/SearchEngines/MetaData/Discogs/DiscogsHelper.cs b/Roadie.Api.Library/SearchEngines/MetaData/Discogs/DiscogsHelper.cs index 9d6d832..db9f808 100644 --- a/Roadie.Api.Library/SearchEngines/MetaData/Discogs/DiscogsHelper.cs +++ b/Roadie.Api.Library/SearchEngines/MetaData/Discogs/DiscogsHelper.cs @@ -24,7 +24,7 @@ namespace Roadie.Library.SearchEngines.MetaData.Discogs _apiKey = configuration.Integrations.ApiKeys.FirstOrDefault(x => x.ApiName == "DiscogsConsumerKey") ?? new ApiKey(); } - public async Task>> PerformArtistSearch(string query, int resultsCount) + public async Task>> PerformArtistSearchAsync(string query, int resultsCount) { ArtistSearchResult data = null; try @@ -40,7 +40,7 @@ namespace Roadie.Library.SearchEngines.MetaData.Discogs UserAgent = WebHelper.UserAgent }; - var response = await client.ExecuteAsync(request); + var response = await client.ExecuteAsync(request).ConfigureAwait(false); if (response.ResponseStatus == ResponseStatus.Error) { @@ -61,7 +61,7 @@ namespace Roadie.Library.SearchEngines.MetaData.Discogs { UserAgent = WebHelper.UserAgent }; - var artistResponse = await c2.ExecuteTaskAsync(request); + var artistResponse = await c2.ExecuteTaskAsync(request).ConfigureAwait(false); var artist = artistResponse.Data; if (artist != null) { @@ -99,7 +99,7 @@ namespace Roadie.Library.SearchEngines.MetaData.Discogs } } return null; - }, "uri:metadata"); + }, "uri:metadata").ConfigureAwait(false); } catch (Exception ex) { @@ -126,7 +126,7 @@ namespace Roadie.Library.SearchEngines.MetaData.Discogs var client = new RestClient("https://api.discogs.com/database"); client.UserAgent = WebHelper.UserAgent; - var response = await client.ExecuteAsync(request); + var response = await client.ExecuteAsync(request).ConfigureAwait(false); if (response.ResponseStatus == ResponseStatus.Error) { @@ -144,7 +144,7 @@ namespace Roadie.Library.SearchEngines.MetaData.Discogs request = BuildLabelRequest(responseData.id); var c2 = new RestClient("https://api.discogs.com/"); c2.UserAgent = WebHelper.UserAgent; - var labelResponse = await c2.ExecuteTaskAsync(request); + var labelResponse = await c2.ExecuteTaskAsync(request).ConfigureAwait(false); var label = labelResponse.Data; if (label != null) { @@ -157,7 +157,7 @@ namespace Roadie.Library.SearchEngines.MetaData.Discogs if (label.images != null) { images.AddRange(label.images.Where(x => x.type != "primary").Select(x => x.uri)); - var primaryImage = label.images.FirstOrDefault(x => x.type == "primary"); + var primaryImage = label.images.Find(x => x.type == "primary"); if (primaryImage != null) labelThumbnailUrl = primaryImage.uri; if (string.IsNullOrEmpty(labelThumbnailUrl)) labelThumbnailUrl = label.images.First(x => !string.IsNullOrEmpty(x.uri)).uri; @@ -176,7 +176,7 @@ namespace Roadie.Library.SearchEngines.MetaData.Discogs } } return null; - }, "uri:metadata"); + }, "uri:metadata").ConfigureAwait(false); } catch (Exception ex) { @@ -207,7 +207,7 @@ namespace Roadie.Library.SearchEngines.MetaData.Discogs Timeout = SafeParser.ToNumber(Configuration.Integrations.DiscogsTimeout) }; - var response = await client.ExecuteAsync(request); + var response = await client.ExecuteAsync(request).ConfigureAwait(false); if (response?.ResponseStatus == null || response.ResponseStatus == ResponseStatus.Error) { if (response.StatusCode == HttpStatusCode.Unauthorized) @@ -225,7 +225,7 @@ namespace Roadie.Library.SearchEngines.MetaData.Discogs { UserAgent = WebHelper.UserAgent }; - var releaseResult = await c2.ExecuteTaskAsync(request); + var releaseResult = await c2.ExecuteTaskAsync(request).ConfigureAwait(false); var release = releaseResult?.Data; if (release != null) { @@ -296,7 +296,7 @@ namespace Roadie.Library.SearchEngines.MetaData.Discogs if (release.identifiers != null) { - var barcode = release.identifiers.FirstOrDefault(x => (x.type ?? string.Empty) == "Barcode"); + var barcode = release.identifiers.Find(x => (x.type ?? string.Empty) == "Barcode"); if (barcode?.value != null) { data.Tags = new[] { "barcode:" + barcode.value }; @@ -305,7 +305,7 @@ namespace Roadie.Library.SearchEngines.MetaData.Discogs } } return null; - }, "uri:metadata"); + }, "uri:metadata").ConfigureAwait(false); } catch (Exception ex) { diff --git a/Roadie.Api.Library/SearchEngines/MetaData/IArtistSearchEngine.cs b/Roadie.Api.Library/SearchEngines/MetaData/IArtistSearchEngine.cs index 46d35fa..e174f68 100644 --- a/Roadie.Api.Library/SearchEngines/MetaData/IArtistSearchEngine.cs +++ b/Roadie.Api.Library/SearchEngines/MetaData/IArtistSearchEngine.cs @@ -7,6 +7,6 @@ namespace Roadie.Library.SearchEngines.MetaData { bool IsEnabled { get; } - Task>> PerformArtistSearch(string query, int resultsCount); + Task>> PerformArtistSearchAsync(string query, int resultsCount); } } \ No newline at end of file diff --git a/Roadie.Api.Library/SearchEngines/MetaData/LastFm/ILastFmHelper.cs b/Roadie.Api.Library/SearchEngines/MetaData/LastFm/ILastFmHelper.cs index 9291efc..9e49810 100644 --- a/Roadie.Api.Library/SearchEngines/MetaData/LastFm/ILastFmHelper.cs +++ b/Roadie.Api.Library/SearchEngines/MetaData/LastFm/ILastFmHelper.cs @@ -10,6 +10,6 @@ namespace Roadie.Library.MetaData.LastFm { Task> GetSessionKeyForUserToken(string token); - Task> TracksForRelease(string artist, string Release); + Task> TracksForReleaseAsync(string artist, string Release); } } \ No newline at end of file diff --git a/Roadie.Api.Library/SearchEngines/MetaData/LastFm/LastFmHelper.cs b/Roadie.Api.Library/SearchEngines/MetaData/LastFm/LastFmHelper.cs index 4c7706a..a013a8b 100644 --- a/Roadie.Api.Library/SearchEngines/MetaData/LastFm/LastFmHelper.cs +++ b/Roadie.Api.Library/SearchEngines/MetaData/LastFm/LastFmHelper.cs @@ -86,7 +86,7 @@ namespace Roadie.Library.MetaData.LastFm }; var request = new RestRequest(Method.GET); var client = new RestClient(BuildUrl("auth.getSession", parameters)); - var responseXML = await client.ExecuteAsync(request); + var responseXML = await client.ExecuteAsync(request).ConfigureAwait(false); var doc = new XmlDocument(); doc.LoadXml(responseXML.Content); var sessionKey = doc.GetElementsByTagName("key")[0].InnerText; @@ -139,7 +139,7 @@ namespace Roadie.Library.MetaData.LastFm var xp = GetResponseAsXml(request); Logger.LogTrace($"LastFmHelper: RoadieUser `{roadieUser}` NowPlaying `{scrobble}` LastFmResult [{xp.InnerXml}]"); result = true; - }); + }).ConfigureAwait(false); } catch (Exception ex) { @@ -153,7 +153,7 @@ namespace Roadie.Library.MetaData.LastFm }; } - public async Task>> PerformArtistSearch(string query, int resultsCount) + public async Task>> PerformArtistSearchAsync(string query, int resultsCount) { try { @@ -163,7 +163,7 @@ namespace Roadie.Library.MetaData.LastFm Logger.LogTrace("LastFmHelper:PerformArtistSearch:{0}", query); var auth = new LastAuth(ApiKey.Key, ApiKey.KeySecret); var albumApi = new ArtistApi(auth); - var response = await albumApi.GetInfoAsync(query); + var response = await albumApi.GetInfoAsync(query).ConfigureAwait(false); if (!response.Success) { return null; @@ -183,7 +183,7 @@ namespace Roadie.Library.MetaData.LastFm result.Urls = new[] { lastFmArtist.Url.ToString() }; } return result; - }, "uri:metadata"); + }, "uri:metadata").ConfigureAwait(false); return new OperationResult> { IsSuccess = data != null, @@ -205,7 +205,7 @@ namespace Roadie.Library.MetaData.LastFm { var request = new RestRequest(Method.GET); var client = new RestClient(string.Format("http://ws.audioscrobbler.com/2.0/?method=album.getinfo&api_key={0}&artist={1}&album={2}&format=xml", ApiKey.Key, artistName, query)); - var responseData = await client.ExecuteAsync(request); + var responseData = await client.ExecuteAsync(request).ConfigureAwait(false); ReleaseSearchResult result = null; @@ -246,7 +246,7 @@ namespace Roadie.Library.MetaData.LastFm } } return result; - }, "uri:metadata"); + }, "uri:metadata").ConfigureAwait(false); return new OperationResult> { IsSuccess = data != null, @@ -269,7 +269,7 @@ namespace Roadie.Library.MetaData.LastFm // If less than half of duration then create a NowPlaying if (scrobble.ElapsedTimeOfTrackPlayed.TotalMinutes < 4 || scrobble.ElapsedTimeOfTrackPlayed.TotalSeconds < scrobble.TrackDuration.TotalSeconds / 2) - return await NowPlaying(roadieUser, scrobble); + return await NowPlaying(roadieUser, scrobble).ConfigureAwait(false); var user = DbContext.Users.FirstOrDefault(x => x.RoadieId == roadieUser.UserId); @@ -319,7 +319,7 @@ namespace Roadie.Library.MetaData.LastFm }; } - public async Task> TracksForRelease(string artist, string Release) + public async Task> TracksForReleaseAsync(string artist, string Release) { if (string.IsNullOrEmpty(artist) || string.IsNullOrEmpty(Release)) return null; var result = new List(); @@ -333,7 +333,7 @@ namespace Roadie.Library.MetaData.LastFm { var auth = new LastAuth(ApiKey.Key, ApiKey.KeySecret); var albumApi = new AlbumApi(auth); // this is an unauthenticated call to the API - var response = await albumApi.GetInfoAsync(artist, Release); + var response = await albumApi.GetInfoAsync(artist, Release).ConfigureAwait(false); releaseInfo = response.Content; if (releaseInfo != null) CacheManager.Add(responseCacheKey, releaseInfo); } diff --git a/Roadie.Api.Library/SearchEngines/MetaData/MusicBrainz/IMusicBrainzProvider.cs b/Roadie.Api.Library/SearchEngines/MetaData/MusicBrainz/IMusicBrainzProvider.cs index d29fc43..f95df03 100644 --- a/Roadie.Api.Library/SearchEngines/MetaData/MusicBrainz/IMusicBrainzProvider.cs +++ b/Roadie.Api.Library/SearchEngines/MetaData/MusicBrainz/IMusicBrainzProvider.cs @@ -7,6 +7,6 @@ namespace Roadie.Library.MetaData.MusicBrainz { public interface IMusicBrainzProvider : IArtistSearchEngine, IReleaseSearchEngine { - Task> MusicBrainzReleaseTracks(string artistName, string releaseTitle); + Task> MusicBrainzReleaseTracksAsync(string artistName, string releaseTitle); } } \ No newline at end of file diff --git a/Roadie.Api.Library/SearchEngines/MetaData/MusicBrainz/MusicBrainzProvider.cs b/Roadie.Api.Library/SearchEngines/MetaData/MusicBrainz/MusicBrainzProvider.cs index bab4f46..d7a106f 100644 --- a/Roadie.Api.Library/SearchEngines/MetaData/MusicBrainz/MusicBrainzProvider.cs +++ b/Roadie.Api.Library/SearchEngines/MetaData/MusicBrainz/MusicBrainzProvider.cs @@ -23,14 +23,14 @@ namespace Roadie.Library.MetaData.MusicBrainz Repository = new MusicBrainzRepository(configuration, logger); } - public async Task> MusicBrainzReleaseTracks(string artistName, string releaseTitle) + public async Task> MusicBrainzReleaseTracksAsync(string artistName, string releaseTitle) { try { if (string.IsNullOrEmpty(artistName) && string.IsNullOrEmpty(releaseTitle)) return null; // Find the Artist var artistCacheKey = string.Format("uri:musicbrainz:artist:{0}", artistName); - var artistSearch = await PerformArtistSearch(artistName, 1); + var artistSearch = await PerformArtistSearchAsync(artistName, 1).ConfigureAwait(false); if (!artistSearch.IsSuccess) return null; var artist = artistSearch.Data.First(); @@ -56,8 +56,7 @@ namespace Roadie.Library.MetaData.MusicBrainz } // Now get The Release Details - release = await MusicBrainzRequestHelper.GetAsync( - MusicBrainzRequestHelper.CreateLookupUrl("release", ReleaseResult.MusicBrainzId, "recordings")); + release = await MusicBrainzRequestHelper.GetAsync(MusicBrainzRequestHelper.CreateLookupUrl("release", ReleaseResult.MusicBrainzId, "recordings")).ConfigureAwait(false); if (release == null) return null; CacheManager.Add(ReleaseCacheKey, release); } @@ -113,12 +112,12 @@ namespace Roadie.Library.MetaData.MusicBrainz return null; } - public async Task>> PerformArtistSearch(string query, int resultsCount) + public async Task>> PerformArtistSearchAsync(string query, int resultsCount) { ArtistSearchResult result = null; Logger.LogTrace("MusicBrainzProvider:PerformArtistSearch:{0}", query); - var a = await Repository.ArtistByName(query, resultsCount); + var a = await Repository.ArtistByNameAsync(query, resultsCount).ConfigureAwait(false); if (a != null) { var imageUrls = a.relations?.Where(x => x.type.Equals("image", StringComparison.OrdinalIgnoreCase)).Select(x => x.url.resource).Distinct().ToArray(); @@ -174,7 +173,7 @@ namespace Roadie.Library.MetaData.MusicBrainz ReleaseSearchResult result = null; try { - var releaseInfosForArtist = await ReleasesForArtist(artistName); + var releaseInfosForArtist = await ReleasesForArtistAsync(artistName).ConfigureAwait(false); if (releaseInfosForArtist != null) { var r = releaseInfosForArtist.FirstOrDefault(x => x.title.Equals(query, StringComparison.OrdinalIgnoreCase)); @@ -208,7 +207,7 @@ namespace Roadie.Library.MetaData.MusicBrainz ReleaseGenres = r.releasegroup?.genres?.Select(x => x.name).Distinct().ToArray(), ReleaseType = r.releasegroup?.primarytype }; - var coverUrls = await CoverArtForMusicBrainzReleaseById(r.id); + var coverUrls = await CoverArtForMusicBrainzReleaseByIdAsync(r.id).ConfigureAwait(false); if (coverUrls != null) { var frontCover = coverUrls.images.FirstOrDefault(i => i.front); @@ -296,16 +295,13 @@ namespace Roadie.Library.MetaData.MusicBrainz }; } - private async Task CoverArtForMusicBrainzReleaseById(string musicBrainzId) - { - return await MusicBrainzRequestHelper.GetAsync(MusicBrainzRequestHelper.CreateCoverArtReleaseUrl(musicBrainzId)); - } + private Task CoverArtForMusicBrainzReleaseByIdAsync(string musicBrainzId) => MusicBrainzRequestHelper.GetAsync(MusicBrainzRequestHelper.CreateCoverArtReleaseUrl(musicBrainzId)); - private async Task> ReleasesForArtist(string artist, string artistMusicBrainzId = null) + private async Task> ReleasesForArtistAsync(string artist, string artistMusicBrainzId = null) { if (string.IsNullOrEmpty(artistMusicBrainzId)) { - var artistSearch = await PerformArtistSearch(artist, 1); + var artistSearch = await PerformArtistSearchAsync(artist, 1).ConfigureAwait(false); if (artistSearch == null || !artistSearch.IsSuccess) { return null; @@ -317,7 +313,7 @@ namespace Roadie.Library.MetaData.MusicBrainz } artistMusicBrainzId = mbArtist.MusicBrainzId; } - return await Repository.ReleasesForArtist(artistMusicBrainzId); + return await Repository.ReleasesForArtist(artistMusicBrainzId).ConfigureAwait(false); } } } \ No newline at end of file diff --git a/Roadie.Api.Library/SearchEngines/MetaData/MusicBrainz/MusicBrainzRepository.cs b/Roadie.Api.Library/SearchEngines/MetaData/MusicBrainz/MusicBrainzRepository.cs index 1cb5c25..be12566 100644 --- a/Roadie.Api.Library/SearchEngines/MetaData/MusicBrainz/MusicBrainzRepository.cs +++ b/Roadie.Api.Library/SearchEngines/MetaData/MusicBrainz/MusicBrainzRepository.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Http; -using System.Text; using System.Threading.Tasks; namespace Roadie.Library.MetaData.MusicBrainz @@ -21,7 +20,7 @@ namespace Roadie.Library.MetaData.MusicBrainz Logger = logger; var location = System.Reflection.Assembly.GetEntryAssembly().Location; var directory = configuration.SearchEngineReposFolder ?? Path.Combine(System.IO.Path.GetDirectoryName(location), "SearchEngineRepos"); - if(!Directory.Exists(directory)) + if (!Directory.Exists(directory)) { Directory.CreateDirectory(directory); } @@ -33,7 +32,7 @@ namespace Roadie.Library.MetaData.MusicBrainz /// /// Query name of Artist /// Maximum Number of Results - public async Task ArtistByName(string name, int? resultsCount = null) + public async Task ArtistByNameAsync(string name, int? resultsCount = null) { Artist result = null; @@ -48,14 +47,14 @@ namespace Roadie.Library.MetaData.MusicBrainz if (artist == null) { // Perform a query to get the MbId for the Name - var artistResult = await MusicBrainzRequestHelper.GetAsync(MusicBrainzRequestHelper.CreateSearchTemplate("artist", name, resultsCount ?? 1, 0)); + var artistResult = await MusicBrainzRequestHelper.GetAsync(MusicBrainzRequestHelper.CreateSearchTemplate("artist", name, resultsCount ?? 1, 0)).ConfigureAwait(false); if (artistResult == null || artistResult.artists == null || !artistResult.artists.Any() || artistResult.count < 1) { return null; } var mbId = artistResult.artists.First().id; // Now perform a detail request to get the details by the MbId - result = await MusicBrainzRequestHelper.GetAsync(MusicBrainzRequestHelper.CreateLookupUrl("artist", mbId, "aliases+tags+genres+url-rels")); + result = await MusicBrainzRequestHelper.GetAsync(MusicBrainzRequestHelper.CreateLookupUrl("artist", mbId, "aliases+tags+genres+url-rels")).ConfigureAwait(false); if (result != null) { col.Insert(new RepositoryArtist @@ -99,13 +98,13 @@ namespace Roadie.Library.MetaData.MusicBrainz col.EnsureIndex(x => x.ArtistMbId); col.EnsureIndex(x => x.Release.id); var releases = col.Find(x => x.ArtistMbId == artistMbId); - if(releases == null || !releases.Any()) + if (releases == null || !releases.Any()) { // Query to get collection of Releases for Artist var pageSize = 50; var page = 0; var url = MusicBrainzRequestHelper.CreateArtistBrowseTemplate(artistMbId, pageSize, 0); - var mbReleaseBrowseResult = await MusicBrainzRequestHelper.GetAsync(url); + var mbReleaseBrowseResult = await MusicBrainzRequestHelper.GetAsync(url).ConfigureAwait(false); var totalReleases = mbReleaseBrowseResult != null ? mbReleaseBrowseResult.releasecount : 0; var totalPages = Math.Ceiling((decimal)totalReleases / pageSize); var fetchResult = new List(); @@ -116,7 +115,7 @@ namespace Roadie.Library.MetaData.MusicBrainz fetchResult.AddRange(mbReleaseBrowseResult.releases.Where(x => !string.IsNullOrEmpty(x.date))); } page++; - mbReleaseBrowseResult = await MusicBrainzRequestHelper.GetAsync(MusicBrainzRequestHelper.CreateArtistBrowseTemplate(artistMbId, pageSize, pageSize * page)); + mbReleaseBrowseResult = await MusicBrainzRequestHelper.GetAsync(MusicBrainzRequestHelper.CreateArtistBrowseTemplate(artistMbId, pageSize, pageSize * page)).ConfigureAwait(false); } while (page < totalPages); col.InsertBulk(fetchResult.Select(x => new RepositoryRelease { @@ -129,7 +128,7 @@ namespace Roadie.Library.MetaData.MusicBrainz { results = releases.Select(x => x.Release).ToArray(); } - } + } } catch (HttpRequestException ex) { diff --git a/Roadie.Api.Library/SearchEngines/MetaData/MusicBrainz/MusicBrainzRequestHelper.cs b/Roadie.Api.Library/SearchEngines/MetaData/MusicBrainz/MusicBrainzRequestHelper.cs index 711f155..0b01eb1 100644 --- a/Roadie.Api.Library/SearchEngines/MetaData/MusicBrainz/MusicBrainzRequestHelper.cs +++ b/Roadie.Api.Library/SearchEngines/MetaData/MusicBrainz/MusicBrainzRequestHelper.cs @@ -55,7 +55,7 @@ namespace Roadie.Library.MetaData.MusicBrainz using (var webClient = new WebClient()) { webClient.Headers.Add("user-agent", WebHelper.UserAgent); - result = JsonConvert.DeserializeObject(await webClient.DownloadStringTaskAsync(new Uri(url))); + result = JsonConvert.DeserializeObject(await webClient.DownloadStringTaskAsync(new Uri(url)).ConfigureAwait(false)); } } catch (WebException ex) diff --git a/Roadie.Api.Library/SearchEngines/MetaData/Spotify/SpotifyHelper.cs b/Roadie.Api.Library/SearchEngines/MetaData/Spotify/SpotifyHelper.cs index 59abe23..d34e463 100644 --- a/Roadie.Api.Library/SearchEngines/MetaData/Spotify/SpotifyHelper.cs +++ b/Roadie.Api.Library/SearchEngines/MetaData/Spotify/SpotifyHelper.cs @@ -23,7 +23,7 @@ namespace Roadie.Library.SearchEngines.MetaData.Spotify { } - public async Task>> PerformArtistSearch(string query, + public async Task>> PerformArtistSearchAsync(string query, int resultsCount) { ArtistSearchResult data = null; @@ -37,7 +37,7 @@ namespace Roadie.Library.SearchEngines.MetaData.Spotify var client = new RestClient("http://api.spotify.com/v1"); client.UserAgent = WebHelper.UserAgent; - var response = await client.ExecuteAsync(request); + var response = await client.ExecuteAsync(request).ConfigureAwait(false); if (response.ResponseStatus == ResponseStatus.Error) { @@ -79,7 +79,7 @@ namespace Roadie.Library.SearchEngines.MetaData.Spotify public async Task>> PerformReleaseSearch(string artistName, string query, int resultsCount) { - var artistResult = await PerformArtistSearch(artistName, resultsCount); + var artistResult = await PerformArtistSearchAsync(artistName, resultsCount).ConfigureAwait(false); if (!artistResult.IsSuccess) return new OperationResult>(); try { @@ -88,7 +88,7 @@ namespace Roadie.Library.SearchEngines.MetaData.Spotify ReleaseSearchResult result = null; - var response = await AlbumsForArtist(artistResult.Data.First().SpotifyId); + var response = await AlbumsForArtistAsync(artistResult.Data.First().SpotifyId).ConfigureAwait(false); if (response != null && response.items != null) { string foundByAlternateName = null; @@ -104,7 +104,7 @@ namespace Roadie.Library.SearchEngines.MetaData.Spotify } var client = new RestClient(string.Format("http://api.spotify.com/v1/albums/{0}", spotifyAlbum.id)); - var albumResult = await client.ExecuteAsync(request); + var albumResult = await client.ExecuteAsync(request).ConfigureAwait(false); if (albumResult != null && albumResult.Data != null) { var sa = albumResult.Data; @@ -203,7 +203,7 @@ namespace Roadie.Library.SearchEngines.MetaData.Spotify return new OperationResult>(); } - private async Task AlbumsForArtist(string spotifyId) + private async Task AlbumsForArtistAsync(string spotifyId) { var cacheKey = string.Format("uri:spotify:AlbumsForArtist:{0}", spotifyId); var result = CacheManager.Get(cacheKey); @@ -213,7 +213,7 @@ namespace Roadie.Library.SearchEngines.MetaData.Spotify var client = new RestClient(string.Format( "http://api.spotify.com/v1/artists/{0}/albums?offset=0&limit=25&album_type=album&market=US", spotifyId)); - var artistAlbumsResponse = await client.ExecuteAsync(request); + var artistAlbumsResponse = await client.ExecuteAsync(request).ConfigureAwait(false); result = artistAlbumsResponse != null && artistAlbumsResponse.Data != null ? artistAlbumsResponse.Data : null; diff --git a/Roadie.Api.Library/SearchEngines/MetaData/Wikipedia/WikipediaHelper.cs b/Roadie.Api.Library/SearchEngines/MetaData/Wikipedia/WikipediaHelper.cs index beb4f33..ac7fae0 100644 --- a/Roadie.Api.Library/SearchEngines/MetaData/Wikipedia/WikipediaHelper.cs +++ b/Roadie.Api.Library/SearchEngines/MetaData/Wikipedia/WikipediaHelper.cs @@ -21,7 +21,7 @@ namespace Roadie.Library.SearchEngines.MetaData.Wikipedia HttpEncoder = httpEncoder; } - public Task>> PerformArtistSearch(string query, int resultsCount) + public Task>> PerformArtistSearchAsync(string query, int resultsCount) { if(string.IsNullOrEmpty(query) || resultsCount == 0) { diff --git a/Roadie.Api.Library/Utility/WebHelper.cs b/Roadie.Api.Library/Utility/WebHelper.cs index f652e85..02ac301 100644 --- a/Roadie.Api.Library/Utility/WebHelper.cs +++ b/Roadie.Api.Library/Utility/WebHelper.cs @@ -66,7 +66,7 @@ namespace Roadie.Library.Utility using (var webClient = new WebClient()) { webClient.Headers.Add("user-agent", UserAgent); - imageBytes = await webClient.DownloadDataTaskAsync(new Uri(url)); + imageBytes = await webClient.DownloadDataTaskAsync(new Uri(url)).ConfigureAwait(false); } } catch diff --git a/Roadie.Api.Services/AdminService.cs b/Roadie.Api.Services/AdminService.cs index 5a4a595..1094061 100644 --- a/Roadie.Api.Services/AdminService.cs +++ b/Roadie.Api.Services/AdminService.cs @@ -1,5 +1,4 @@ -using Mapster; -using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.SignalR; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; @@ -29,9 +28,10 @@ namespace Roadie.Api.Services { public class AdminService : ServiceBase, IAdminService { - protected IHubContext ScanActivityHub { get; } + private IArtistLookupEngine ArtistLookupEngine { get; } private IArtistService ArtistService { get; } + private IBookmarkService BookmarkService { get; } private IEventMessageLogger EventMessageLogger { get; } @@ -39,16 +39,16 @@ namespace Roadie.Api.Services private IGenreService GenreService { get; } private ILabelService LabelService { get; } - private IArtistLookupEngine ArtistLookupEngine { get; } private IReleaseLookupEngine ReleaseLookupEngine { get; } - private IBookmarkService BookmarkService { get; } private IReleaseService ReleaseService { get; } + protected IHubContext ScanActivityHub { get; } + public AdminService(IRoadieSettings configuration, IHttpEncoder httpEncoder, IHttpContext httpContext, IRoadieDbContext context, ICacheManager cacheManager, ILogger logger, IHubContext scanActivityHub, IFileDirectoryProcessorService fileDirectoryProcessorService, IArtistService artistService, - IReleaseService releaseService, IArtistLookupEngine artistLookupEngine, IReleaseLookupEngine releaseLookupEngine, + IReleaseService releaseService, IArtistLookupEngine artistLookupEngine, IReleaseLookupEngine releaseLookupEngine, ILabelService labelService, IGenreService genreService, IBookmarkService bookmarkService ) : base(configuration, httpEncoder, context, cacheManager, logger, httpContext) @@ -67,21 +67,94 @@ namespace Roadie.Api.Services BookmarkService = bookmarkService; } - public async Task> DeleteArtist(User user, Guid artistId, bool deleteFolder) + private void EventMessageLogger_Messages(object sender, EventMessage e) => Task.WaitAll(LogAndPublishAsync(e.Message, e.Level)); + + private async Task LogAndPublishAsync(string message, LogLevel level = LogLevel.Trace) + { + switch (level) + { + case LogLevel.Trace: + Logger.LogTrace(message); + break; + + case LogLevel.Debug: + Logger.LogDebug(message); + break; + + case LogLevel.Information: + Logger.LogInformation(message); + break; + + case LogLevel.Warning: + Logger.LogWarning(message); + break; + + case LogLevel.Critical: + Logger.LogCritical(message); + break; + } + + await ScanActivityHub.Clients.All.SendAsync("SendSystemActivityAsync", message).ConfigureAwait(false); + } + + private async Task> ScanFolderAsync(User user, DirectoryInfo d, bool isReadOnly, bool doDeleteFiles = true) + { + var sw = new Stopwatch(); + sw.Start(); + + long processedFiles = 0; + await LogAndPublishAsync($"** Processing Folder: [{d.FullName}]").ConfigureAwait(false); + + long processedFolders = 0; + foreach (var folder in Directory.EnumerateDirectories(d.FullName).ToArray()) + { + var directoryProcessResult = await FileDirectoryProcessorService.ProcessAsync(user: user, + folder: new DirectoryInfo(folder), + doJustInfo: isReadOnly, + doDeleteFiles: doDeleteFiles).ConfigureAwait(false); + processedFolders++; + processedFiles += SafeParser.ToNumber(directoryProcessResult.AdditionalData["ProcessedFiles"]); + } + CacheManager.Clear(); + if (!isReadOnly) + { + Services.FileDirectoryProcessorService.DeleteEmptyFolders(d, Logger); + } + sw.Stop(); + var newScanHistory = new data.ScanHistory + { + UserId = user.Id, + NewArtists = FileDirectoryProcessorService.AddedArtistIds.Count(), + NewReleases = FileDirectoryProcessorService.AddedReleaseIds.Count(), + NewTracks = FileDirectoryProcessorService.AddedTrackIds.Count(), + TimeSpanInSeconds = (int)sw.Elapsed.TotalSeconds + }; + DbContext.ScanHistories.Add(newScanHistory); + await DbContext.SaveChangesAsync().ConfigureAwait(false); + await LogAndPublishAsync($"** Completed! Processed Folders [{processedFolders}], Processed Files [{processedFiles}], New Artists [{ newScanHistory.NewArtists }], New Releases [{ newScanHistory.NewReleases }], New Tracks [{ newScanHistory.NewTracks }] : Elapsed Time [{sw.Elapsed}]").ConfigureAwait(false); + return new OperationResult + { + Data = true, + IsSuccess = true, + OperationTime = sw.ElapsedMilliseconds + }; + } + + public async Task> DeleteArtistAsync(User user, Guid artistId, bool deleteFolder) { var sw = new Stopwatch(); sw.Start(); var errors = new List(); - var artist = DbContext.Artists.FirstOrDefault(x => x.RoadieId == artistId); + var artist = await DbContext.Artists.FirstOrDefaultAsync(x => x.RoadieId == artistId).ConfigureAwait(false); if (artist == null) { - await LogAndPublish($"DeleteArtist Unknown Artist [{artistId}]", LogLevel.Warning); + await LogAndPublishAsync($"DeleteArtist Unknown Artist [{artistId}]", LogLevel.Warning).ConfigureAwait(false); return new OperationResult(true, $"Artist Not Found [{artistId}]"); } try { - var result = await ArtistService.Delete(user, artist, deleteFolder); + var result = await ArtistService.DeleteAsync(user, artist, deleteFolder).ConfigureAwait(false); if (!result.IsSuccess) { return new OperationResult @@ -93,22 +166,22 @@ namespace Roadie.Api.Services catch (Exception ex) { Logger.LogError(ex); - await LogAndPublish("Error deleting artist."); + await LogAndPublishAsync("Error deleting artist.").ConfigureAwait(false); errors.Add(ex); } sw.Stop(); - await LogAndPublish($"DeleteArtist `{artist}`, By User `{user}`", LogLevel.Information); + await LogAndPublishAsync($"DeleteArtist `{artist}`, By User `{user}`", LogLevel.Information).ConfigureAwait(false); return new OperationResult { - IsSuccess = !errors.Any(), + IsSuccess = errors.Count == 0, Data = true, OperationTime = sw.ElapsedMilliseconds, Errors = errors }; } - public async Task> DeleteArtistReleases(User user, Guid artistId, bool doDeleteFiles = false) + public async Task> DeleteArtistReleasesAsync(User user, Guid artistId, bool doDeleteFiles = false) { var sw = new Stopwatch(); sw.Start(); @@ -116,34 +189,34 @@ namespace Roadie.Api.Services var artist = DbContext.Artists.FirstOrDefault(x => x.RoadieId == artistId); if (artist == null) { - await LogAndPublish($"DeleteArtistReleases Unknown Artist [{artistId}]", LogLevel.Warning); + await LogAndPublishAsync($"DeleteArtistReleases Unknown Artist [{artistId}]", LogLevel.Warning).ConfigureAwait(false); return new OperationResult(true, $"Artist Not Found [{artistId}]"); } try { - await ReleaseService.DeleteReleases(user, DbContext.Releases.Where(x => x.ArtistId == artist.Id).Select(x => x.RoadieId).ToArray(), doDeleteFiles); - await DbContext.SaveChangesAsync(); + await ReleaseService.DeleteReleasesAsync(user, DbContext.Releases.Where(x => x.ArtistId == artist.Id).Select(x => x.RoadieId).ToArray(), doDeleteFiles).ConfigureAwait(false); + await DbContext.SaveChangesAsync().ConfigureAwait(false); } catch (Exception ex) { Logger.LogError(ex); - await LogAndPublish("Error deleting artist."); + await LogAndPublishAsync("Error deleting artist.").ConfigureAwait(false); errors.Add(ex); } sw.Stop(); - await LogAndPublish($"DeleteArtistReleases `{artist}`, By User `{user}`", LogLevel.Information); + await LogAndPublishAsync($"DeleteArtistReleases `{artist}`, By User `{user}`", LogLevel.Information).ConfigureAwait(false); return new OperationResult { - IsSuccess = !errors.Any(), + IsSuccess = errors.Count == 0, Data = true, OperationTime = sw.ElapsedMilliseconds, Errors = errors }; } - public async Task> DeleteArtistSecondaryImage(User user, Guid artistId, int index) + public async Task> DeleteArtistSecondaryImageAsync(User user, Guid artistId, int index) { var sw = new Stopwatch(); sw.Start(); @@ -151,7 +224,7 @@ namespace Roadie.Api.Services var artist = DbContext.Artists.FirstOrDefault(x => x.RoadieId == artistId); if (artist == null) { - await LogAndPublish($"DeleteArtistSecondaryImage Unknown Artist [{artistId}]", LogLevel.Warning); + await LogAndPublishAsync($"DeleteArtistSecondaryImage Unknown Artist [{artistId}]", LogLevel.Warning).ConfigureAwait(false); return new OperationResult(true, $"Artist Not Found [{artistId}]"); } @@ -169,22 +242,22 @@ namespace Roadie.Api.Services catch (Exception ex) { Logger.LogError(ex); - await LogAndPublish("Error deleting artist secondary image."); + await LogAndPublishAsync("Error deleting artist secondary image.").ConfigureAwait(false); errors.Add(ex); } sw.Stop(); - await LogAndPublish($"DeleteArtistSecondaryImage `{artist}` Index [{index}], By User `{user}`", LogLevel.Information); + await LogAndPublishAsync($"DeleteArtistSecondaryImage `{artist}` Index [{index}], By User `{user}`", LogLevel.Information).ConfigureAwait(false); return new OperationResult { - IsSuccess = !errors.Any(), + IsSuccess = errors.Count == 0, Data = true, OperationTime = sw.ElapsedMilliseconds, Errors = errors }; } - public async Task> DeleteGenre(User user, Guid genreId) + public async Task> DeleteGenreAsync(User user, Guid genreId) { var sw = new Stopwatch(); sw.Start(); @@ -192,34 +265,34 @@ namespace Roadie.Api.Services var genre = DbContext.Genres.FirstOrDefault(x => x.RoadieId == genreId); if (genre == null) { - await LogAndPublish($"DeleteLabel Unknown Genre [{genreId}]", LogLevel.Warning); + await LogAndPublishAsync($"DeleteLabel Unknown Genre [{genreId}]", LogLevel.Warning).ConfigureAwait(false); return new OperationResult(true, $"Genre Not Found [{genreId}]"); } try { - await GenreService.Delete(user, genreId); + await GenreService.DeleteAsync(user, genreId).ConfigureAwait(false); CacheManager.ClearRegion(genre.CacheRegion); } catch (Exception ex) { Logger.LogError(ex); - await LogAndPublish("Error deleting Genre."); + await LogAndPublishAsync("Error deleting Genre.").ConfigureAwait(false); errors.Add(ex); } - sw.Stop(); - await LogAndPublish($"DeleteGenre `{genre}`, By User `{user}`", LogLevel.Information); + sw.Stop(); + await LogAndPublishAsync($"DeleteGenre `{genre}`, By User `{user}`", LogLevel.Information).ConfigureAwait(false); return new OperationResult { - IsSuccess = !errors.Any(), + IsSuccess = errors.Count == 0, Data = true, OperationTime = sw.ElapsedMilliseconds, Errors = errors }; } - public async Task> DeleteLabel(User user, Guid labelId) + public async Task> DeleteLabelAsync(User user, Guid labelId) { var sw = new Stopwatch(); sw.Start(); @@ -227,34 +300,34 @@ namespace Roadie.Api.Services var label = DbContext.Labels.FirstOrDefault(x => x.RoadieId == labelId); if (label == null) { - await LogAndPublish($"DeleteLabel Unknown Label [{labelId}]", LogLevel.Warning); + await LogAndPublishAsync($"DeleteLabel Unknown Label [{labelId}]", LogLevel.Warning).ConfigureAwait(false); return new OperationResult(true, $"Label Not Found [{labelId}]"); } try { - await LabelService.Delete(user, labelId); + await LabelService.DeleteAsync(user, labelId).ConfigureAwait(false); CacheManager.ClearRegion(label.CacheRegion); } catch (Exception ex) { Logger.LogError(ex); - await LogAndPublish("Error deleting Label."); + await LogAndPublishAsync("Error deleting Label.").ConfigureAwait(false); errors.Add(ex); } sw.Stop(); - await LogAndPublish($"DeleteLabel `{label}`, By User `{user}`", LogLevel.Information); + await LogAndPublishAsync($"DeleteLabel `{label}`, By User `{user}`", LogLevel.Information).ConfigureAwait(false); return new OperationResult { - IsSuccess = !errors.Any(), + IsSuccess = errors.Count == 0, Data = true, OperationTime = sw.ElapsedMilliseconds, Errors = errors }; } - public async Task> DeleteRelease(User user, Guid releaseId, bool? doDeleteFiles) + public async Task> DeleteReleaseAsync(User user, Guid releaseId, bool? doDeleteFiles) { var sw = new Stopwatch(); sw.Start(); @@ -266,31 +339,31 @@ namespace Roadie.Api.Services { if (release == null) { - await LogAndPublish($"DeleteRelease Unknown Release [{releaseId}]", LogLevel.Warning); + await LogAndPublishAsync($"DeleteRelease Unknown Release [{releaseId}]", LogLevel.Warning).ConfigureAwait(false); return new OperationResult(true, $"Release Not Found [{releaseId}]"); } - await ReleaseService.Delete(user, release, doDeleteFiles ?? false); + await ReleaseService.DeleteAsync(user, release, doDeleteFiles ?? false).ConfigureAwait(false); } catch (Exception ex) { Logger.LogError(ex); - await LogAndPublish("Error deleting release."); + await LogAndPublishAsync("Error deleting release.").ConfigureAwait(false); errors.Add(ex); } sw.Stop(); - await LogAndPublish($"DeleteRelease `{release}`, By User `{user}`", LogLevel.Information); + await LogAndPublishAsync($"DeleteRelease `{release}`, By User `{user}`", LogLevel.Information).ConfigureAwait(false); CacheManager.Clear(); return new OperationResult { - IsSuccess = !errors.Any(), + IsSuccess = errors.Count == 0, Data = true, OperationTime = sw.ElapsedMilliseconds, Errors = errors }; } - public async Task> DeleteReleaseSecondaryImage(User user, Guid releaseId, int index) + public async Task> DeleteReleaseSecondaryImageAsync(User user, Guid releaseId, int index) { var sw = new Stopwatch(); sw.Start(); @@ -298,7 +371,7 @@ namespace Roadie.Api.Services var release = DbContext.Releases.Include(x => x.Artist).FirstOrDefault(x => x.RoadieId == releaseId); if (release == null) { - await LogAndPublish($"DeleteReleaseSecondaryImage Unknown Release [{releaseId}]", LogLevel.Warning); + await LogAndPublishAsync($"DeleteReleaseSecondaryImage Unknown Release [{releaseId}]", LogLevel.Warning).ConfigureAwait(false); return new OperationResult(true, $"Release Not Found [{releaseId}]"); } @@ -316,22 +389,22 @@ namespace Roadie.Api.Services catch (Exception ex) { Logger.LogError(ex); - await LogAndPublish("Error deleting release secondary image."); + await LogAndPublishAsync("Error deleting release secondary image.").ConfigureAwait(false); errors.Add(ex); } sw.Stop(); - await LogAndPublish($"DeleteReleaseSecondaryImage `{release}` Index [{index}], By User `{user}`", LogLevel.Information); + await LogAndPublishAsync($"DeleteReleaseSecondaryImage `{release}` Index [{index}], By User `{user}`", LogLevel.Information).ConfigureAwait(false); return new OperationResult { - IsSuccess = !errors.Any(), + IsSuccess = errors.Count == 0, Data = true, OperationTime = sw.ElapsedMilliseconds, Errors = errors }; } - public async Task> DeleteTracks(User user, IEnumerable trackIds, bool? doDeleteFile) + public async Task> DeleteTracksAsync(User user, IEnumerable trackIds, bool? doDeleteFile) { var sw = new Stopwatch(); sw.Start(); @@ -348,12 +421,12 @@ namespace Roadie.Api.Services { if (track == null) { - await LogAndPublish($"DeleteTracks Unknown Track [{trackId}]", LogLevel.Warning); + await LogAndPublishAsync($"DeleteTracks Unknown Track [{trackId}]", LogLevel.Warning).ConfigureAwait(false); return new OperationResult(true, $"Track Not Found [{trackId}]"); } DbContext.Tracks.Remove(track); - await DbContext.SaveChangesAsync(); + await DbContext.SaveChangesAsync().ConfigureAwait(false); if (doDeleteFile ?? false) { string trackPath = null; @@ -375,33 +448,33 @@ namespace Roadie.Api.Services } catch (Exception ex) { - Logger.LogError(ex, string.Format("Error Deleting File [{0}] For Track [{1}] Exception [{2}]", trackPath, track.Id, ex.Serialize())); + Logger.LogError(ex, $"Error Deleting File [{trackPath}] For Track [{track.Id}] Exception [{ex.Serialize()}]"); } } - await ReleaseService.ScanReleaseFolder(user, track.ReleaseMedia.Release.RoadieId, false, track.ReleaseMedia.Release); - await BookmarkService.RemoveAllBookmarksForItem(BookmarkType.Track, track.Id); + await ReleaseService.ScanReleaseFolderAsync(user, track.ReleaseMedia.Release.RoadieId, false, track.ReleaseMedia.Release).ConfigureAwait(false); + await BookmarkService.RemoveAllBookmarksForItemAsync(BookmarkType.Track, track.Id).ConfigureAwait(false); } catch (Exception ex) { Logger.LogError(ex); - await LogAndPublish("Error deleting track."); + await LogAndPublishAsync("Error deleting track.").ConfigureAwait(false); errors.Add(ex); } sw.Stop(); - await LogAndPublish($"DeleteTracks `{track}`, By User `{user}`", LogLevel.Warning); + await LogAndPublishAsync($"DeleteTracks `{track}`, By User `{user}`", LogLevel.Warning).ConfigureAwait(false); } CacheManager.Clear(); return new OperationResult { - IsSuccess = !errors.Any(), + IsSuccess = errors.Count == 0, Data = true, OperationTime = sw.ElapsedMilliseconds, Errors = errors }; } - public async Task> DeleteUser(User applicationUser, Guid userId) + public async Task> DeleteUserAsync(User applicationUser, Guid userId) { var sw = new Stopwatch(); sw.Start(); @@ -412,7 +485,7 @@ namespace Roadie.Api.Services { var ex = new Exception("User cannot self."); Logger.LogError(ex); - await LogAndPublish("Error deleting user."); + await LogAndPublishAsync("Error deleting user.").ConfigureAwait(false); errors.Add(ex); } @@ -420,12 +493,12 @@ namespace Roadie.Api.Services { if (user == null) { - await LogAndPublish($"DeleteUser Unknown User [{userId}]", LogLevel.Warning); + await LogAndPublishAsync($"DeleteUser Unknown User [{userId}]", LogLevel.Warning).ConfigureAwait(false); return new OperationResult(true, $"User Not Found [{userId}]"); } DbContext.Users.Remove(user); - await DbContext.SaveChangesAsync(); + await DbContext.SaveChangesAsync().ConfigureAwait(false); var userImageFilename = user.PathToImage(Configuration); if (File.Exists(userImageFilename)) { @@ -435,16 +508,16 @@ namespace Roadie.Api.Services catch (Exception ex) { Logger.LogError(ex); - await LogAndPublish("Error deleting user."); + await LogAndPublishAsync("Error deleting user.").ConfigureAwait(false); errors.Add(ex); } sw.Stop(); - await LogAndPublish($"DeleteUser `{user}`, By User `{user}`", LogLevel.Warning); + await LogAndPublishAsync($"DeleteUser `{user}`, By User `{user}`", LogLevel.Warning).ConfigureAwait(false); CacheManager.Clear(); return new OperationResult { - IsSuccess = !errors.Any(), + IsSuccess = errors.Count == 0, Data = true, OperationTime = sw.ElapsedMilliseconds, Errors = errors @@ -454,7 +527,7 @@ namespace Roadie.Api.Services /// /// This is a very simple way to seed the database or setup configuration when the first (who becomes "Admin") user registers /// - public async Task> DoInitialSetup(User user, UserManager userManager) + public async Task> DoInitialSetupAsync(User user, UserManager userManager) { var sw = new Stopwatch(); sw.Start(); @@ -483,10 +556,10 @@ namespace Roadie.Api.Services Description = "Users who have Edit Permissions", NormalizedName = "EDITOR" }); - await DbContext.SaveChangesAsync(); + await DbContext.SaveChangesAsync().ConfigureAwait(false); // Add given user to Admin role - await userManager.AddToRoleAsync(user, "Admin"); + await userManager.AddToRoleAsync(user, "Admin").ConfigureAwait(false); // Create special system artists of 'Sound Tracks' and 'Various Artists' DbContext.Artists.Add(new data.Artist @@ -515,7 +588,7 @@ namespace Roadie.Api.Services Tags = "compilations|various", URLs = "https://en.wikipedia.org/wiki/Compilation_album" }); - await DbContext.SaveChangesAsync(); + await DbContext.SaveChangesAsync().ConfigureAwait(false); return new OperationResult { @@ -525,7 +598,7 @@ namespace Roadie.Api.Services }; } - public Task>>> MissingCollectionReleases(User user) + public Task>>> MissingCollectionReleasesAsync(User user) { var sw = Stopwatch.StartNew(); sw.Start(); @@ -567,7 +640,7 @@ namespace Roadie.Api.Services sw.Stop(); return Task.FromResult(new OperationResult>> { - Data = missingData.OrderBy(x => x.Value.Count()).ToDictionary(x => x.Key, x => x.Value), + Data = missingData.OrderBy(x => x.Value.Count).ToDictionary(x => x.Key, x => x.Value), IsSuccess = true, OperationTime = sw.ElapsedMilliseconds }); @@ -619,13 +692,10 @@ namespace Roadie.Api.Services Directory.CreateDirectory(Configuration.LabelImageFolder); Logger.LogInformation($"Created Label Image Folder [{Configuration.LabelImageFolder}]"); } - if (Configuration.DbContextToUse != DbContexts.MySQL) + if (Configuration.DbContextToUse != DbContexts.MySQL && !Directory.Exists(Configuration.FileDatabaseOptions.DatabaseFolder)) { - if (!Directory.Exists(Configuration.FileDatabaseOptions.DatabaseFolder)) - { - Directory.CreateDirectory(Configuration.FileDatabaseOptions.DatabaseFolder); - Logger.LogInformation($"Created File Database Folder [{Configuration.FileDatabaseOptions.DatabaseFolder}]"); - } + Directory.CreateDirectory(Configuration.FileDatabaseOptions.DatabaseFolder); + Logger.LogInformation($"Created File Database Folder [{Configuration.FileDatabaseOptions.DatabaseFolder}]"); } } catch (Exception ex) @@ -640,7 +710,7 @@ namespace Roadie.Api.Services Logger.LogInformation($"Administration startup tasks completed, elapsed time [{ sw.ElapsedMilliseconds }]"); } - public async Task> ScanAllCollections(User user, bool isReadOnly = false, bool doPurgeFirst = false) + public async Task> ScanAllCollectionsAsync(User user, bool isReadOnly = false, bool doPurgeFirst = false) { var sw = new Stopwatch(); sw.Start(); @@ -652,7 +722,7 @@ namespace Roadie.Api.Services { try { - var result = await ScanCollection(user, collection.RoadieId, isReadOnly, doPurgeFirst, false).ConfigureAwait(false); + var result = await ScanCollectionAsync(user, collection.RoadieId, isReadOnly, doPurgeFirst, false).ConfigureAwait(false); if (!result.IsSuccess) { errors.AddRange(result.Errors); @@ -661,7 +731,7 @@ namespace Roadie.Api.Services } catch (Exception ex) { - await LogAndPublish(ex.ToString(), LogLevel.Error).ConfigureAwait(false); + await LogAndPublishAsync(ex.ToString(), LogLevel.Error).ConfigureAwait(false); errors.Add(ex); } } @@ -671,7 +741,7 @@ namespace Roadie.Api.Services await UpdateReleaseRank(updatedReleaseId).ConfigureAwait(false); } sw.Stop(); - await LogAndPublish($"ScanAllCollections, By User `{user}`, Updated Release Count [{updatedReleaseIds.Distinct().Count()}], ElapsedTime [{sw.ElapsedMilliseconds}]", LogLevel.Warning).ConfigureAwait(false); + await LogAndPublishAsync($"ScanAllCollections, By User `{user}`, Updated Release Count [{updatedReleaseIds.Distinct().Count()}], ElapsedTime [{sw.ElapsedMilliseconds}]", LogLevel.Warning).ConfigureAwait(false); return new OperationResult { IsSuccess = errors.Count == 0, @@ -681,7 +751,7 @@ namespace Roadie.Api.Services }; } - public async Task> ScanArtist(User user, Guid artistId, bool isReadOnly = false) + public async Task> ScanArtistAsync(User user, Guid artistId, bool isReadOnly = false) { var sw = Stopwatch.StartNew(); @@ -689,18 +759,18 @@ namespace Roadie.Api.Services var artist = DbContext.Artists.FirstOrDefault(x => x.RoadieId == artistId); if (artist == null) { - await LogAndPublish($"ScanArtist Unknown Artist [{artistId}]", LogLevel.Warning); + await LogAndPublishAsync($"ScanArtist Unknown Artist [{artistId}]", LogLevel.Warning).ConfigureAwait(false); return new OperationResult(true, $"Artist Not Found [{artistId}]"); } try { - var result = await ArtistService.ScanArtistReleasesFolders(user, artist.RoadieId, Configuration.LibraryFolder, isReadOnly); + await ArtistService.ScanArtistReleasesFoldersAsync(user, artist.RoadieId, Configuration.LibraryFolder, isReadOnly).ConfigureAwait(false); CacheManager.ClearRegion(artist.CacheRegion); } catch (Exception ex) { - await LogAndPublish(ex.ToString(), LogLevel.Error); + await LogAndPublishAsync(ex.ToString(), LogLevel.Error).ConfigureAwait(false); errors.Add(ex); } @@ -713,56 +783,52 @@ namespace Roadie.Api.Services NewTracks = ReleaseService.AddedTrackIds.Count(), TimeSpanInSeconds = (int)sw.Elapsed.TotalSeconds }); - await DbContext.SaveChangesAsync(); - await UpdateArtistRank(artist.Id, true); + await DbContext.SaveChangesAsync().ConfigureAwait(false); + await UpdateArtistRank(artist.Id, true).ConfigureAwait(false); return new OperationResult { - IsSuccess = !errors.Any(), + IsSuccess = errors.Count == 0, AdditionalData = new Dictionary { { "artistAverage", artist.Rating } }, OperationTime = sw.ElapsedMilliseconds, Errors = errors }; } - public async Task> ScanArtists(User user, IEnumerable artistIds, bool isReadOnly = false) + public async Task> ScanArtistsAsync(User user, IEnumerable artistIds, bool isReadOnly = false) { var sw = Stopwatch.StartNew(); var errors = new List(); foreach (var artistId in artistIds) { - var result = await ScanArtist(user, artistId, isReadOnly); - if (!result.IsSuccess) + var result = await ScanArtistAsync(user, artistId, isReadOnly).ConfigureAwait(false); + if (!result.IsSuccess && (result.Errors?.Any() ?? false)) { - if (result.Errors?.Any() ?? false) - { - errors.AddRange(result.Errors); - } + errors.AddRange(result.Errors); } } sw.Stop(); - await LogAndPublish($"** Completed! ScanArtists: Artist Count [{ artistIds.Count() }], Elapsed Time [{sw.Elapsed}]"); + await LogAndPublishAsync($"** Completed! ScanArtists: Artist Count [{ artistIds.Count() }], Elapsed Time [{sw.Elapsed}]").ConfigureAwait(false); return new OperationResult { - IsSuccess = !errors.Any(), + IsSuccess = errors.Count == 0, OperationTime = sw.ElapsedMilliseconds, Errors = errors }; } - public async Task> ScanCollection(User user, Guid collectionId, bool isReadOnly = false, bool doPurgeFirst = false, bool doUpdateRanks = true) + public async Task> ScanCollectionAsync(User user, Guid collectionId, bool isReadOnly = false, bool doPurgeFirst = false, bool doUpdateRanks = true) { var sw = new Stopwatch(); sw.Start(); var releaseIdsInCollection = new List(); var updatedReleaseIds = new List(); - var result = new List(); var errors = new List(); var collection = await DbContext.Collections.FirstOrDefaultAsync(x => x.RoadieId == collectionId).ConfigureAwait(false); if (collection == null) { - await LogAndPublish($"ScanCollection Unknown Collection [{collectionId}]", LogLevel.Warning).ConfigureAwait(false); + await LogAndPublishAsync($"ScanCollection Unknown Collection [{collectionId}]", LogLevel.Warning).ConfigureAwait(false); return new OperationResult(true, $"Collection Not Found [{collectionId}]"); } @@ -770,7 +836,7 @@ namespace Roadie.Api.Services { if (doPurgeFirst) { - await LogAndPublish($"ScanCollection Purging Collection [{collectionId}]", LogLevel.Warning).ConfigureAwait(false); + await LogAndPublishAsync($"ScanCollection Purging Collection [{collectionId}]", LogLevel.Warning).ConfigureAwait(false); var crs = await DbContext.CollectionReleases.Where(x => x.CollectionId == collection.Id).ToArrayAsync().ConfigureAwait(false); DbContext.CollectionReleases.RemoveRange(crs); await DbContext.SaveChangesAsync().ConfigureAwait(false); @@ -790,8 +856,8 @@ namespace Roadie.Api.Services data.Release release = null; var isArtistNameDbKey = csvRelease.Artist.StartsWith(Roadie.Library.Data.Collection.DatabaseIdKey); - int? artistId = isArtistNameDbKey ? SafeParser.ToNumber(csvRelease.Artist.Replace(Roadie.Library.Data.Collection.DatabaseIdKey, "")) : null; - if(artistId.HasValue) + int? artistId = isArtistNameDbKey ? SafeParser.ToNumber(csvRelease.Artist.Replace(Roadie.Library.Data.Collection.DatabaseIdKey, string.Empty)) : null; + if (artistId.HasValue) { var artist = DbContext.Artists.FirstOrDefault(x => x.Id == artistId.Value); if (artist != null) @@ -802,9 +868,9 @@ namespace Roadie.Api.Services else { artistsMatchingName = await ArtistLookupEngine.DatabaseQueryForArtistName(csvRelease.Artist).ConfigureAwait(false); - if (artistsMatchingName == null || !artistsMatchingName.Any()) + if (artistsMatchingName?.Any() != true) { - await LogAndPublish($"CSV Position [{ csvRelease.Position }] Unable To Find Artist [{csvRelease.Artist}]", LogLevel.Warning).ConfigureAwait(false); + await LogAndPublishAsync($"CSV Position [{ csvRelease.Position }] Unable To Find Artist [{csvRelease.Artist}]", LogLevel.Warning).ConfigureAwait(false); csvRelease.Status = Statuses.Missing; await DbContext.CollectionMissings.AddAsync(new data.CollectionMissing { @@ -817,13 +883,13 @@ namespace Roadie.Api.Services } else if (artistsMatchingName.Count() > 1) { - await LogAndPublish($"CSV Position [{ csvRelease.Position }] Found [{ artistsMatchingName.Count() }] Artists by [{csvRelease.Artist}]", LogLevel.Information).ConfigureAwait(false); + await LogAndPublishAsync($"CSV Position [{ csvRelease.Position }] Found [{ artistsMatchingName.Count() }] Artists by [{csvRelease.Artist}]", LogLevel.Information).ConfigureAwait(false); } } foreach (var artist in artistsMatchingName) { var isReleaseNameDbKey = csvRelease.Release.StartsWith(Roadie.Library.Data.Collection.DatabaseIdKey); - int? releaseId = isReleaseNameDbKey ? SafeParser.ToNumber(csvRelease.Release.Replace(Roadie.Library.Data.Collection.DatabaseIdKey, "")) : null; + int? releaseId = isReleaseNameDbKey ? SafeParser.ToNumber(csvRelease.Release.Replace(Roadie.Library.Data.Collection.DatabaseIdKey, string.Empty)) : null; if (releaseId.HasValue) { release = await DbContext.Releases.FirstOrDefaultAsync(x => x.Id == releaseId.Value).ConfigureAwait(false); @@ -840,7 +906,7 @@ namespace Roadie.Api.Services if (release == null) { - await LogAndPublish($"CSV Position [{ csvRelease.Position }] Unable To Find Release [{csvRelease.Release}], for Artist [{csvRelease.Artist}]", LogLevel.Warning).ConfigureAwait(false); + await LogAndPublishAsync($"CSV Position [{ csvRelease.Position }] Unable To Find Release [{csvRelease.Release}], for Artist [{csvRelease.Artist}]", LogLevel.Warning).ConfigureAwait(false); csvRelease.Status = Statuses.Missing; await DbContext.CollectionMissings.AddAsync(new data.CollectionMissing { @@ -867,7 +933,7 @@ namespace Roadie.Api.Services CollectionId = collection.Id, ReleaseId = release.Id, ListNumber = csvRelease.Position - }); + }).ConfigureAwait(false); updated = true; } // If Item in Collection is at different List number update CollectionRelease @@ -906,16 +972,16 @@ namespace Roadie.Api.Services } var collectionReleasesToRemove = await (from cr in DbContext.CollectionReleases - where cr.CollectionId == collection.Id - where !releaseIdsInCollection.Contains(cr.ReleaseId) - select cr).ToArrayAsync().ConfigureAwait(false); - if (collectionReleasesToRemove.Any()) + where cr.CollectionId == collection.Id + where !releaseIdsInCollection.Contains(cr.ReleaseId) + select cr).ToArrayAsync().ConfigureAwait(false); + if (collectionReleasesToRemove.Length > 0) { - await LogAndPublish($"Removing [{collectionReleasesToRemove.Count()}] Stale Release Records from Collection.", LogLevel.Information); + await LogAndPublishAsync($"Removing [{collectionReleasesToRemove.Length}] Stale Release Records from Collection.", LogLevel.Information).ConfigureAwait(false); DbContext.CollectionReleases.RemoveRange(collectionReleasesToRemove); } - await DbContext.SaveChangesAsync(); + await DbContext.SaveChangesAsync().ConfigureAwait(false); if (doUpdateRanks) { foreach (var updatedReleaseId in updatedReleaseIds) @@ -933,31 +999,23 @@ namespace Roadie.Api.Services } sw.Stop(); - Logger.LogWarning(string.Format("RescanCollection `{0}`, By User `{1}`, ElapsedTime [{2}]", collection, user, sw.ElapsedMilliseconds)); + Logger.LogWarning($"RescanCollection `{collection}`, By User `{user}`, ElapsedTime [{sw.ElapsedMilliseconds}]"); return new OperationResult { AdditionalData = new Dictionary { { "updatedReleaseIds", updatedReleaseIds.ToArray() } }, - IsSuccess = !errors.Any(), + IsSuccess = errors.Count == 0, Data = true, OperationTime = sw.ElapsedMilliseconds, Errors = errors }; } - public async Task> ScanInboundFolder(User user, bool isReadOnly = false) - { - var d = new DirectoryInfo(Configuration.InboundFolder); - return await ScanFolder(user, d, isReadOnly); - } + public Task> ScanInboundFolderAsync(User user, bool isReadOnly = false) => ScanFolderAsync(user, new DirectoryInfo(Configuration.InboundFolder), isReadOnly); - public async Task> ScanLibraryFolder(User user, bool isReadOnly = false) - { - var d = new DirectoryInfo(Configuration.LibraryFolder); - return await ScanFolder(user, d, isReadOnly, false); - } + public Task> ScanLibraryFolderAsync(User user, bool isReadOnly = false) => ScanFolderAsync(user, new DirectoryInfo(Configuration.LibraryFolder), isReadOnly, false); - public async Task> ScanRelease(User user, Guid releaseId, bool isReadOnly = false, bool wasDoneForInvalidTrackPlay = false) + public async Task> ScanReleaseAsync(User user, Guid releaseId, bool isReadOnly = false, bool wasDoneForInvalidTrackPlay = false) { var sw = new Stopwatch(); sw.Start(); @@ -969,17 +1027,17 @@ namespace Roadie.Api.Services .FirstOrDefault(x => x.RoadieId == releaseId); if (release == null) { - await LogAndPublish($"ScanRelease Unknown Release [{releaseId}]", LogLevel.Warning); + await LogAndPublishAsync($"ScanRelease Unknown Release [{releaseId}]", LogLevel.Warning).ConfigureAwait(false); return new OperationResult(true, $"Release Not Found [{releaseId}]"); } try { - var result = await ReleaseService.ScanReleaseFolder(user, release.RoadieId, isReadOnly, release); + await ReleaseService.ScanReleaseFolderAsync(user, release.RoadieId, isReadOnly, release).ConfigureAwait(false); } catch (Exception ex) { - await LogAndPublish(ex.ToString(), LogLevel.Error); + await LogAndPublishAsync(ex.ToString(), LogLevel.Error).ConfigureAwait(false); errors.Add(ex); } @@ -992,43 +1050,40 @@ namespace Roadie.Api.Services NewTracks = ReleaseService.AddedTrackIds.Count(), TimeSpanInSeconds = (int)sw.Elapsed.TotalSeconds }); - await DbContext.SaveChangesAsync(); + await DbContext.SaveChangesAsync().ConfigureAwait(false); return new OperationResult { - IsSuccess = !errors.Any(), + IsSuccess = errors.Count == 0, Data = true, OperationTime = sw.ElapsedMilliseconds, Errors = errors }; } - public async Task> ScanReleases(User user, IEnumerable releaseIds, bool isReadOnly = false, bool wasDoneForInvalidTrackPlay = false) + public async Task> ScanReleasesAsync(User user, IEnumerable releaseIds, bool isReadOnly = false, bool wasDoneForInvalidTrackPlay = false) { var sw = Stopwatch.StartNew(); var errors = new List(); foreach (var releaseId in releaseIds) { - var result = await ScanRelease(user, releaseId, isReadOnly, wasDoneForInvalidTrackPlay); - if (!result.IsSuccess) + var result = await ScanReleaseAsync(user, releaseId, isReadOnly, wasDoneForInvalidTrackPlay).ConfigureAwait(false); + if (!result.IsSuccess && (result.Errors?.Any() ?? false)) { - if (result.Errors?.Any() ?? false) - { - errors.AddRange(result.Errors); - } + errors.AddRange(result.Errors); } } sw.Stop(); - await LogAndPublish($"** Completed! ScanReleases: Release Count [{ releaseIds.Count() }], Elapsed Time [{sw.Elapsed}]"); + await LogAndPublishAsync($"** Completed! ScanReleases: Release Count [{ releaseIds.Count() }], Elapsed Time [{sw.Elapsed}]").ConfigureAwait(false); return new OperationResult { - IsSuccess = !errors.Any(), + IsSuccess = errors.Count == 0, OperationTime = sw.ElapsedMilliseconds, Errors = errors }; } - public async Task> UpdateInviteTokenUsed(Guid? tokenId) + public async Task> UpdateInviteTokenUsedAsync(Guid? tokenId) { var sw = new Stopwatch(); sw.Start(); @@ -1044,17 +1099,17 @@ namespace Roadie.Api.Services } token.Status = Statuses.Complete; token.LastUpdated = DateTime.UtcNow; - await DbContext.SaveChangesAsync(); + await DbContext.SaveChangesAsync().ConfigureAwait(false); return new OperationResult { - IsSuccess = !errors.Any(), + IsSuccess = errors.Count == 0, Data = true, OperationTime = sw.ElapsedMilliseconds, Errors = errors }; } - public async Task> ValidateInviteToken(Guid? tokenId) + public async Task> ValidateInviteTokenAsync(Guid? tokenId) { var sw = new Stopwatch(); sw.Start(); @@ -1072,7 +1127,7 @@ namespace Roadie.Api.Services { token.Status = Statuses.Expired; token.LastUpdated = DateTime.UtcNow; - await DbContext.SaveChangesAsync(); + await DbContext.SaveChangesAsync().ConfigureAwait(false); return new OperationResult(true, $"Invite Token [{tokenId}] Expired [{ token.ExpiresDate }]"); } if (token.Status == Statuses.Complete) @@ -1081,84 +1136,11 @@ namespace Roadie.Api.Services } return new OperationResult { - IsSuccess = !errors.Any(), + IsSuccess = errors.Count == 0, Data = true, OperationTime = sw.ElapsedMilliseconds, Errors = errors }; } - - private void EventMessageLogger_Messages(object sender, EventMessage e) => Task.WaitAll(LogAndPublish(e.Message, e.Level)); - - private async Task LogAndPublish(string message, LogLevel level = LogLevel.Trace) - { - switch (level) - { - case LogLevel.Trace: - Logger.LogTrace(message); - break; - - case LogLevel.Debug: - Logger.LogDebug(message); - break; - - case LogLevel.Information: - Logger.LogInformation(message); - break; - - case LogLevel.Warning: - Logger.LogWarning(message); - break; - - case LogLevel.Critical: - Logger.LogCritical(message); - break; - } - - await ScanActivityHub.Clients.All.SendAsync("SendSystemActivity", message).ConfigureAwait(false); - } - - private async Task> ScanFolder(User user, DirectoryInfo d, bool isReadOnly, bool doDeleteFiles = true) - { - var sw = new Stopwatch(); - sw.Start(); - - long processedFiles = 0; - await LogAndPublish($"** Processing Folder: [{d.FullName}]"); - - long processedFolders = 0; - foreach (var folder in Directory.EnumerateDirectories(d.FullName).ToArray()) - { - var directoryProcessResult = await FileDirectoryProcessorService.Process(user:user, - folder: new DirectoryInfo(folder), - doJustInfo: isReadOnly, - doDeleteFiles: doDeleteFiles); - processedFolders++; - processedFiles += SafeParser.ToNumber(directoryProcessResult.AdditionalData["ProcessedFiles"]); - } - CacheManager.Clear(); - if (!isReadOnly) - { - Services.FileDirectoryProcessorService.DeleteEmptyFolders(d, Logger); - } - sw.Stop(); - var newScanHistory = new data.ScanHistory - { - UserId = user.Id, - NewArtists = FileDirectoryProcessorService.AddedArtistIds.Count(), - NewReleases = FileDirectoryProcessorService.AddedReleaseIds.Count(), - NewTracks = FileDirectoryProcessorService.AddedTrackIds.Count(), - TimeSpanInSeconds = (int)sw.Elapsed.TotalSeconds - }; - DbContext.ScanHistories.Add(newScanHistory); - await DbContext.SaveChangesAsync(); - await LogAndPublish($"** Completed! Processed Folders [{processedFolders}], Processed Files [{processedFiles}], New Artists [{ newScanHistory.NewArtists }], New Releases [{ newScanHistory.NewReleases }], New Tracks [{ newScanHistory.NewTracks }] : Elapsed Time [{sw.Elapsed}]"); - return new OperationResult - { - Data = true, - IsSuccess = true, - OperationTime = sw.ElapsedMilliseconds - }; - } } } \ No newline at end of file diff --git a/Roadie.Api.Services/ArtistService.cs b/Roadie.Api.Services/ArtistService.cs index 627ab41..7419b66 100644 --- a/Roadie.Api.Services/ArtistService.cs +++ b/Roadie.Api.Services/ArtistService.cs @@ -70,818 +70,7 @@ namespace Roadie.Api.Services FileDirectoryProcessorService = fileDirectoryProcessorService; } - public async Task> ById(User roadieUser, Guid id, IEnumerable includes) - { - var timings = new Dictionary(); - var tsw = new Stopwatch(); - - var sw = Stopwatch.StartNew(); - sw.Start(); - var cacheKey = $"urn:artist_by_id_operation:{id}:{(includes == null ? "0" : string.Join("|", includes))}"; - var result = await CacheManager.GetAsync(cacheKey, async () => - { - tsw.Restart(); - var rr = await ArtistByIdAction(id, includes).ConfigureAwait(false); - tsw.Stop(); - timings.Add("ArtistByIdAction", tsw.ElapsedMilliseconds); - return rr; - }, data.Artist.CacheRegionUrn(id)).ConfigureAwait(false); - if (result?.Data != null && roadieUser != null) - { - tsw.Restart(); - var artist = await GetArtist(id).ConfigureAwait(false); - tsw.Stop(); - timings.Add("getArtist", tsw.ElapsedMilliseconds); - tsw.Restart(); - var userBookmarkResult = await BookmarkService.List(roadieUser, new PagedRequest(), false, BookmarkType.Artist).ConfigureAwait(false); - if (userBookmarkResult.IsSuccess) - { - result.Data.UserBookmarked = userBookmarkResult?.Rows?.FirstOrDefault(x => x?.Bookmark?.Value == artist?.RoadieId.ToString()) != null; - } - tsw.Stop(); - timings.Add("userBookmarkResult", tsw.ElapsedMilliseconds); - tsw.Restart(); - var userArtist = DbContext.UserArtists.FirstOrDefault(x => x.ArtistId == artist.Id && x.UserId == roadieUser.Id); - if (userArtist != null) - { - result.Data.UserRating = new UserArtist - { - IsDisliked = userArtist.IsDisliked ?? false, - IsFavorite = userArtist.IsFavorite ?? false, - Rating = userArtist.Rating - }; - } - tsw.Stop(); - timings.Add("userArtist", tsw.ElapsedMilliseconds); - - if (result.Data.Comments.Any()) - { - tsw.Restart(); - var commentIds = result.Data.Comments.Select(x => x.DatabaseId).ToArray(); - var userCommentReactions = (from cr in DbContext.CommentReactions - where commentIds.Contains(cr.CommentId) - where cr.UserId == roadieUser.Id - select cr).ToArray(); - foreach (var comment in result.Data.Comments) - { - var userCommentReaction = Array.Find(userCommentReactions, x => x.CommentId == comment.DatabaseId); - comment.IsDisliked = userCommentReaction?.ReactionValue == CommentReaction.Dislike; - comment.IsLiked = userCommentReaction?.ReactionValue == CommentReaction.Like; - } - - tsw.Stop(); - timings.Add("userCommentReactions", tsw.ElapsedMilliseconds); - } - } - - sw.Stop(); - Logger.LogInformation($"ById Artist: `{ result?.Data }`, includes [{ includes.ToCSV() }], timings [{ timings.ToTimings() }]"); - return new OperationResult(result.Messages) - { - Data = result?.Data, - Errors = result?.Errors, - IsNotFoundResult = result?.IsNotFoundResult ?? false, - IsSuccess = result?.IsSuccess ?? false, - OperationTime = sw.ElapsedMilliseconds - }; - } - - public async Task> Delete(Library.Identity.User user, data.Artist artist, bool deleteFolder) - { - var isSuccess = false; - try - { - if (artist != null) - { - DbContext.Artists.Remove(artist); - await DbContext.SaveChangesAsync().ConfigureAwait(false); - if (deleteFolder) - { - // Delete all image files for Artist - foreach (var file in ImageHelper.ImageFilesInFolder(artist.ArtistFileFolder(Configuration), SearchOption.TopDirectoryOnly)) - { - try - { - File.Delete(file); - Logger.LogWarning("For Artist [{0}], Deleted File [{1}]", artist.Id, file); - } - catch (Exception ex) - { - Logger.LogError(ex, $"Error Deleting File [{file}] Exception [{ex}]"); - } - } - - var artistDir = new DirectoryInfo(artist.ArtistFileFolder(Configuration)); - FolderPathHelper.DeleteEmptyFolders(artistDir.Parent); - } - await BookmarkService.RemoveAllBookmarksForItem(BookmarkType.Artist, artist.Id).ConfigureAwait(false); - await UpdatePlaylistCountsForArtist(artist.Id, DateTime.UtcNow).ConfigureAwait(false); - CacheManager.ClearRegion(artist.CacheRegion); - Logger.LogWarning("User `{0}` deleted Artist `{1}]`", user, artist); - isSuccess = true; - } - } - catch (Exception ex) - { - Logger.LogError(ex, ex.Serialize()); - return new OperationResult - { - Errors = new Exception[1] { ex } - }; - } - - return new OperationResult - { - IsSuccess = isSuccess, - Data = isSuccess - }; - } - - public async Task> List(User roadieUser, PagedRequest request, bool? doRandomize = false, bool? onlyIncludeWithReleases = true) - { - var sw = new Stopwatch(); - sw.Start(); - - int? rowCount = null; - - IQueryable favoriteArtistIds = null; - if (request.FilterFavoriteOnly) - { - favoriteArtistIds = from a in DbContext.Artists - join ua in DbContext.UserArtists on a.Id equals ua.ArtistId - where ua.IsFavorite ?? false - where roadieUser == null || ua.UserId == roadieUser.Id - select a.Id; - } - IQueryable labelArtistIds = null; - if (request.FilterToLabelId.HasValue) - { - labelArtistIds = (from l in DbContext.Labels - join rl in DbContext.ReleaseLabels on l.Id equals rl.LabelId - join r in DbContext.Releases on rl.ReleaseId equals r.Id - where l.RoadieId == request.FilterToLabelId - select r.ArtistId) - .Distinct(); - } - IQueryable genreArtistIds = null; - var isFilteredToGenre = false; - if (request.FilterToGenreId.HasValue) - { - genreArtistIds = (from ag in DbContext.ArtistGenres - join g in DbContext.Genres on ag.GenreId equals g.Id - where g.RoadieId == request.FilterToGenreId - select ag.ArtistId) - .Distinct(); - isFilteredToGenre = true; - } - else if (!string.IsNullOrEmpty(request.Filter) && request.Filter.StartsWith(":genre", StringComparison.OrdinalIgnoreCase)) - { - var genreFilter = request.Filter.Replace(":genre ", ""); - genreArtistIds = (from ag in DbContext.ArtistGenres - join g in DbContext.Genres on ag.GenreId equals g.Id - where g.Name.Contains(genreFilter) - select ag.ArtistId) - .Distinct(); - isFilteredToGenre = true; - request.Filter = null; - } - var onlyWithReleases = onlyIncludeWithReleases ?? true; - var isEqualFilter = false; - if (!string.IsNullOrEmpty(request.FilterValue)) - { - var filter = request.FilterValue; - // if filter string is wrapped in quotes then is an exact not like search, e.g. "Diana Ross" should not return "Diana Ross & The Supremes" - if (filter.StartsWith('"') && filter.EndsWith('"')) - { - isEqualFilter = true; -#pragma warning disable IDE0057 // Use range operator - request.Filter = filter.Substring(1, filter.Length - 2); -#pragma warning restore IDE0057 // Use range operator - } - } - var normalizedFilterValue = !string.IsNullOrEmpty(request.FilterValue) - ? request.FilterValue.ToAlphanumericName() - : null; - - int[] randomArtistIds = null; - SortedDictionary randomArtistData = null; - if (doRandomize ?? false) - { - var randomLimit = request.Limit ?? roadieUser?.RandomReleaseLimit ?? request.LimitValue; - randomArtistData = await DbContext.RandomArtistIds(roadieUser?.Id ?? -1, randomLimit, request.FilterFavoriteOnly, request.FilterRatedOnly).ConfigureAwait(false); - randomArtistIds = randomArtistData.Select(x => x.Value).ToArray(); - rowCount = await DbContext.Artists.CountAsync().ConfigureAwait(false); - } - var result = from a in DbContext.Artists - where !onlyWithReleases || a.ReleaseCount > 0 - where randomArtistIds == null || randomArtistIds.Contains(a.Id) - where request.FilterToArtistId == null || a.RoadieId == request.FilterToArtistId - where request.FilterMinimumRating == null || a.Rating >= request.FilterMinimumRating.Value - where string.IsNullOrEmpty(normalizedFilterValue) || - a.Name.ToLower().Contains(normalizedFilterValue) || - a.SortName.ToLower().Contains(normalizedFilterValue) || - a.RealName.ToLower().Contains(normalizedFilterValue) || - a.AlternateNames.Contains(normalizedFilterValue) - where !isEqualFilter || - a.Name.ToLower().Equals(normalizedFilterValue) || - a.SortName.ToLower().Equals(normalizedFilterValue) || - a.RealName.ToLower().Equals(normalizedFilterValue) || - a.AlternateNames.ToLower().Equals(normalizedFilterValue) - where !request.FilterFavoriteOnly || favoriteArtistIds.Contains(a.Id) - where request.FilterToLabelId == null || labelArtistIds.Contains(a.Id) - where !isFilteredToGenre || genreArtistIds.Contains(a.Id) - select new ArtistList - { - DatabaseId = a.Id, - Id = a.RoadieId, - Artist = new DataToken - { - Text = a.Name, - Value = a.RoadieId.ToString() - }, - Thumbnail = ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, a.RoadieId), - Rating = a.Rating, - Rank = (double?)a.Rank, - CreatedDate = a.CreatedDate, - LastUpdated = a.LastUpdated, - LastPlayed = a.LastPlayed, - PlayedCount = a.PlayedCount, - ReleaseCount = a.ReleaseCount, - TrackCount = a.TrackCount, - SortName = a.SortName, - Status = a.Status - }; - - ArtistList[] rows; - rowCount ??= result.Count(); - - if (doRandomize ?? false) - { - var resultData = await result.ToArrayAsync().ConfigureAwait(false); - rows = (from r in resultData - join ra in randomArtistData on r.DatabaseId equals ra.Value - orderby ra.Key - select r - ).ToArray(); - } - else - { - string sortBy; - if (request.ActionValue == User.ActionKeyUserRated) - { - sortBy = string.IsNullOrEmpty(request.Sort) - ? request.OrderValue(new Dictionary { { "Rating", "DESC" }, { "Artist.Text", "ASC" } }) - : request.OrderValue(); - } - else - { - sortBy = request.OrderValue(new Dictionary { { "SortName", "ASC" }, { "Artist.Text", "ASC" } }); - } - rows = await result.OrderBy(sortBy).Skip(request.SkipValue).Take(request.LimitValue).ToArrayAsync().ConfigureAwait(false); - } - - if (rows.Length > 0 && roadieUser != null) - { - var rowIds = rows.Select(x => x.DatabaseId).ToArray(); - var userArtistRatings = await (from ua in DbContext.UserArtists - where ua.UserId == roadieUser.Id - where rowIds.Contains(ua.ArtistId) - select ua).ToArrayAsync().ConfigureAwait(false); - - foreach (var userArtistRating in userArtistRatings.Where(x => rows.Select(r => r.DatabaseId).Contains(x.ArtistId))) - { - var row = Array.Find(rows, x => x.DatabaseId == userArtistRating.ArtistId); - if (row != null) - { - row.UserRating = new UserArtist - { - IsDisliked = userArtistRating.IsDisliked ?? false, - IsFavorite = userArtistRating.IsFavorite ?? false, - Rating = userArtistRating.Rating, - RatedDate = userArtistRating.LastUpdated ?? userArtistRating.CreatedDate - }; - } - } - } - if (!string.IsNullOrEmpty(request.Filter) && rowCount == 0 && Configuration.RecordNoResultSearches) - { - // Create request for no artist found - var req = new data.Request - { - UserId = roadieUser?.Id, - Description = request.Filter - }; - DbContext.Requests.Add(req); - await DbContext.SaveChangesAsync().ConfigureAwait(false); - } - sw.Stop(); - return new Library.Models.Pagination.PagedResult - { - TotalCount = rowCount.Value, - CurrentPage = request.PageValue, - TotalPages = (int)Math.Ceiling((double)rowCount / request.LimitValue), - OperationTime = sw.ElapsedMilliseconds, - Rows = rows - }; - } - - /// - /// Merge one Artist into another one - /// - /// The Artist to be merged - /// The Artist to merge into - public async Task> MergeArtists(Library.Identity.User user, Guid artistToMergeId, Guid artistToMergeIntoId) - { - var sw = new Stopwatch(); - sw.Start(); - - var errors = new List(); - var artistToMerge = DbContext.Artists - .Include(x => x.Genres) - .Include("Genres.Genre") - .FirstOrDefault(x => x.RoadieId == artistToMergeId); - if (artistToMerge == null) - { - Logger.LogWarning("MergeArtists Unknown Artist [{0}]", artistToMergeId); - return new OperationResult(true, $"Artist Not Found [{artistToMergeId}]"); - } - - var mergeIntoArtist = DbContext.Artists - .Include(x => x.Genres) - .Include("Genres.Genre") - .FirstOrDefault(x => x.RoadieId == artistToMergeIntoId); - if (mergeIntoArtist == null) - { - Logger.LogWarning("MergeArtists Unknown Artist [{0}]", artistToMergeIntoId); - return new OperationResult(true, $"Artist Not Found [{artistToMergeIntoId}]"); - } - - try - { - var result = await MergeArtists(user, artistToMerge, mergeIntoArtist).ConfigureAwait(false); - if (!result.IsSuccess) - { - CacheManager.ClearRegion(artistToMerge.CacheRegion); - CacheManager.ClearRegion(mergeIntoArtist.CacheRegion); - Logger.LogWarning("MergeArtists `{0}` => `{1}`, By User `{2}`", artistToMerge, mergeIntoArtist, - user); - } - } - catch (Exception ex) - { - Logger.LogError(ex); - errors.Add(ex); - } - - sw.Stop(); - - return new OperationResult - { - IsSuccess = errors.Count == 0, - Data = errors.Count == 0, - OperationTime = sw.ElapsedMilliseconds, - Errors = errors - }; - } - - public async Task> RefreshArtistMetadata(Library.Identity.User user, Guid artistId) - { - SimpleContract.Requires(artistId != Guid.Empty, "Invalid ArtistId"); - - var result = true; - var resultErrors = new List(); - var sw = new Stopwatch(); - sw.Start(); - try - { - var artist = DbContext.Artists.FirstOrDefault(x => x.RoadieId == artistId); - if (artist == null) - { - Logger.LogWarning("Unable To Find Artist [{0}]", artistId); - return new OperationResult(); - } - - OperationResult artistSearch = null; - try - { - artistSearch = await ArtistLookupEngine.PerformMetaDataProvidersArtistSearch(new AudioMetaData - { - Artist = artist.Name - }).ConfigureAwait(false); - } - catch (Exception ex) - { - Logger.LogError(ex, ex.Serialize()); - } - - if (artistSearch.IsSuccess) - { - // Do metadata search for Artist like if new Artist then set some overides and merge - var mergeResult = await MergeArtists(user, artistSearch.Data, artist).ConfigureAwait(false); - if (mergeResult.IsSuccess) - { - artist = mergeResult.Data; - await DbContext.SaveChangesAsync().ConfigureAwait(false); - sw.Stop(); - CacheManager.ClearRegion(artist.CacheRegion); - Logger.LogTrace("Scanned RefreshArtistMetadata [{0}], OperationTime [{1}]", - artist.ToString(), sw.ElapsedMilliseconds); - } - else - { - sw.Stop(); - } - } - } - catch (Exception ex) - { - Logger.LogError(ex, ex.Serialize()); - resultErrors.Add(ex); - result = false; - } - - return new OperationResult - { - Data = result, - IsSuccess = result, - Errors = resultErrors, - OperationTime = sw.ElapsedMilliseconds - }; - } - - public async Task> ScanArtistReleasesFolders(Library.Identity.User user, Guid artistId, string destinationFolder, bool doJustInfo) - { - SimpleContract.Requires(artistId != Guid.Empty, "Invalid ArtistId"); - - var result = true; - var resultErrors = new List(); - var sw = new Stopwatch(); - sw.Start(); - try - { - var artist = DbContext.Artists - .Include("Releases") - .Include("Releases.Labels") - .FirstOrDefault(x => x.RoadieId == artistId); - if (artist == null) - { - Logger.LogWarning("Unable To Find Artist [{0}]", artistId); - return new OperationResult(); - } - var releaseScannedCount = 0; - var artistFolder = artist.ArtistFileFolder(Configuration); - if (!Directory.Exists(artistFolder)) - { - Logger.LogDebug($"ScanArtistReleasesFolders: ArtistFolder Not Found [{ artistFolder }] For Artist `{ artist }`"); - return new OperationResult(); - } - var scannedArtistFolders = new List(); - // Scan known releases for changes - if (artist.Releases != null) - { - foreach (var release in artist.Releases) - { - try - { - result = result && (await ReleaseService.ScanReleaseFolder(user, Guid.Empty, doJustInfo, release).ConfigureAwait(false)).Data; - releaseScannedCount++; - scannedArtistFolders.Add(release.ReleaseFileFolder(artistFolder)); - } - catch (Exception ex) - { - Logger.LogError(ex, ex.Serialize()); - } - } - } - var artistImage = ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(artistFolder), ImageType.Artist).FirstOrDefault(); - if (artistImage != null) - { - // See if image file is valid image if not delete it - if (ImageHelper.ConvertToJpegFormat(File.ReadAllBytes(artistImage.FullName)) == null) - { - artistImage.Delete(); - } - } - // Any folder found in Artist folder not already scanned scan - var nonReleaseFolders = from d in Directory.EnumerateDirectories(artistFolder) - where !(from r in scannedArtistFolders select r).Contains(d) - orderby d - select d; - foreach (var folder in nonReleaseFolders) - { - await FileDirectoryProcessorService.Process(user, new DirectoryInfo(folder), doJustInfo).ConfigureAwait(false); - } - if (!doJustInfo) - { - Services.FileDirectoryProcessorService.DeleteEmptyFolders(new DirectoryInfo(artistFolder), Logger); - } - sw.Stop(); - CacheManager.ClearRegion(artist.CacheRegion); - Logger.LogInformation("Scanned Artist [{0}], Releases Scanned [{1}], OperationTime [{2}]", artist.ToString(), releaseScannedCount, sw.ElapsedMilliseconds); - } - catch (Exception ex) - { - Logger.LogError(ex, ex.Serialize()); - resultErrors.Add(ex); - } - - return new OperationResult - { - Data = result, - IsSuccess = result, - Errors = resultErrors, - OperationTime = sw.ElapsedMilliseconds - }; - } - - public async Task> SetReleaseImageByUrl(Library.Identity.User user, Guid id, string imageUrl) => await SaveImageBytes(user, id, WebHelper.BytesForImageUrl(imageUrl)).ConfigureAwait(false); - - public async Task> UpdateArtist(Library.Identity.User user, Artist model) - { - var didRenameArtist = false; - var sw = new Stopwatch(); - sw.Start(); - var errors = new List(); - var artist = DbContext.Artists - .Include(x => x.Genres) - .Include("Genres.Genre") - .FirstOrDefault(x => x.RoadieId == model.Id); - if (artist == null) - { - return new OperationResult(true, $"Artist Not Found [{model.Id}]"); - } - // If artist is being renamed, see if artist already exists with new model supplied name - var artistName = artist.SortNameValue; - var artistModelName = model.SortNameValue; - if (artistName.ToAlphanumericName() != artistModelName.ToAlphanumericName()) - { - var existingArtist = DbContext.Artists.FirstOrDefault(x => x.Name == model.Name || x.SortName == model.SortName); - if (existingArtist != null) - { - return new OperationResult($"Artist already exists `{ existingArtist }` with name [{artistModelName }]."); - } - } - try - { - var now = DateTime.UtcNow; - var originalArtistFolder = artist.ArtistFileFolder(Configuration); - var specialArtistName = model.Name.ToAlphanumericName(); - var alt = new List(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; - artist.BioContext = model.BioContext; - artist.BirthDate = model.BirthDate; - artist.DiscogsId = model.DiscogsId; - artist.EndDate = model.EndDate; - artist.IsLocked = model.IsLocked; - artist.ISNI = model.ISNIList.ToDelimitedList(); - artist.ITunesId = model.ITunesId; - artist.MusicBrainzId = model.MusicBrainzId; - artist.Name = model.Name; - artist.Profile = model.Profile; - artist.Rating = model.Rating; - artist.RealName = model.RealName; - artist.SortName = model.SortName; - artist.SpotifyId = model.SpotifyId; - artist.Status = SafeParser.ToEnum(model.Status); - artist.Tags = model.TagsList.ToDelimitedList(); - artist.URLs = model.URLsList.ToDelimitedList(); - - var newArtistFolder = artist.ArtistFileFolder(Configuration); - // Rename artist folder to reflect new artist name - if (!newArtistFolder.Equals(originalArtistFolder, StringComparison.OrdinalIgnoreCase)) - { - // If folder already exists for new artist name that means another artist has that folder (usually sort name) - if (Directory.Exists(newArtistFolder)) - { - // Set sortname to be unique and try again - var oldSortName = artist.SortName; - artist.SortName = $"{ artist.SortName} [{ artist.Id }]"; - Logger.LogTrace($"Updated Artist SortName From [{ oldSortName }] to [{ artist.SortName }]"); - newArtistFolder = artist.ArtistFileFolder(Configuration); - if (Directory.Exists(newArtistFolder)) - { - return new OperationResult($"Artist Folder [{ newArtistFolder }] already exists."); - } - } - didRenameArtist = true; - if (Directory.Exists(originalArtistFolder)) - { - Logger.LogTrace($"Moving Artist From Folder [{originalArtistFolder}] -> [{newArtistFolder}]"); - Directory.Move(originalArtistFolder, newArtistFolder); - } - } - if (!Directory.Exists(newArtistFolder)) - { - Directory.CreateDirectory(newArtistFolder); - } - - var artistImage = ImageHelper.ImageDataFromUrl(model.NewThumbnailData); - if (artistImage != null) - { - // Save unaltered image to cover file - var artistImageName = Path.Combine(newArtistFolder, ImageHelper.ArtistImageFilename); - File.WriteAllBytes(artistImageName, ImageHelper.ConvertToJpegFormat(artistImage)); - } - - if (model.NewSecondaryImagesData?.Any() == true) - { - // Additional images to add to artist - var looper = 0; - foreach (var newSecondaryImageData in model.NewSecondaryImagesData) - { - var artistSecondaryImage = ImageHelper.ImageDataFromUrl(newSecondaryImageData); - if (artistSecondaryImage != null) - { - // Ensure is jpeg first - artistSecondaryImage = ImageHelper.ConvertToJpegFormat(artistSecondaryImage); - - var artistImageFilename = Path.Combine(newArtistFolder, string.Format(ImageHelper.ArtistSecondaryImageFilename, looper.ToString("00"))); - while (File.Exists(artistImageFilename)) - { - looper++; - artistImageFilename = Path.Combine(newArtistFolder, string.Format(ImageHelper.ArtistSecondaryImageFilename, looper.ToString("00"))); - } - - File.WriteAllBytes(artistImageFilename, artistSecondaryImage); - } - - looper++; - } - } - - if (model.Genres?.Any() == true) - { - // Remove existing Genres not in model list - foreach (var genre in artist.Genres.ToList()) - { - var doesExistInModel = model.Genres.Any(x => SafeParser.ToGuid(x.Genre.Value) == genre.Genre.RoadieId); - if (!doesExistInModel) artist.Genres.Remove(genre); - } - - // Add new Genres in model not in data - foreach (var genre in model.Genres) - { - var genreId = SafeParser.ToGuid(genre.Genre.Value); - var doesExistInData = artist.Genres.Any(x => x.Genre.RoadieId == genreId); - if (!doesExistInData) - { - var g = DbContext.Genres.FirstOrDefault(x => x.RoadieId == genreId); - if (g != null) - { - artist.Genres.Add(new data.ArtistGenre - { - ArtistId = artist.Id, - GenreId = g.Id, - Genre = g - }); - } - } - } - } - else if (model.Genres?.Any() != true) - { - artist.Genres.Clear(); - } - - if (model.AssociatedArtistsTokens?.Any() == true) - { - var associatedArtists = DbContext.ArtistAssociations.Include(x => x.AssociatedArtist) - .Where(x => x.ArtistId == artist.Id).ToList(); - - // Remove existing AssociatedArtists not in model list - foreach (var associatedArtist in associatedArtists) - { - var doesExistInModel = model.AssociatedArtistsTokens.Any(x => - SafeParser.ToGuid(x.Value) == associatedArtist.AssociatedArtist.RoadieId); - if (!doesExistInModel) DbContext.ArtistAssociations.Remove(associatedArtist); - } - - // Add new AssociatedArtists in model not in data - foreach (var associatedArtist in model.AssociatedArtistsTokens) - { - var associatedArtistId = SafeParser.ToGuid(associatedArtist.Value); - var doesExistInData = - associatedArtists.Any(x => x.AssociatedArtist.RoadieId == associatedArtistId); - if (!doesExistInData) - { - var a = DbContext.Artists.FirstOrDefault(x => x.RoadieId == associatedArtistId); - if (a != null) - { - DbContext.ArtistAssociations.Add(new data.ArtistAssociation - { - ArtistId = artist.Id, - AssociatedArtistId = a.Id - }); - } - } - } - } - else if (model.AssociatedArtistsTokens?.Any() != true) - { - var associatedArtists = DbContext.ArtistAssociations.Include(x => x.AssociatedArtist).Where(x => x.ArtistId == artist.Id || x.AssociatedArtistId == artist.Id).ToList(); - DbContext.ArtistAssociations.RemoveRange(associatedArtists); - } - - if (model.SimilarArtistsTokens?.Any() == true) - { - var similarArtists = DbContext.ArtistSimilar.Include(x => x.SimilarArtist) - .Where(x => x.ArtistId == artist.Id).ToList(); - - // Remove existing AssociatedArtists not in model list - foreach (var similarArtist in similarArtists) - { - var doesExistInModel = model.SimilarArtistsTokens.Any(x => - SafeParser.ToGuid(x.Value) == similarArtist.SimilarArtist.RoadieId); - if (!doesExistInModel) DbContext.ArtistSimilar.Remove(similarArtist); - } - - // Add new SimilarArtists in model not in data - foreach (var similarArtist in model.SimilarArtistsTokens) - { - var similarArtistId = SafeParser.ToGuid(similarArtist.Value); - var doesExistInData = similarArtists.Any(x => x.SimilarArtist.RoadieId == similarArtistId); - if (!doesExistInData) - { - var a = DbContext.Artists.FirstOrDefault(x => x.RoadieId == similarArtistId); - if (a != null) - { - DbContext.ArtistSimilar.Add(new data.ArtistSimilar - { - ArtistId = artist.Id, - SimilarArtistId = a.Id - }); - } - } - } - } - else if (model.SimilarArtistsTokens?.Any() != true) - { - var similarArtists = DbContext.ArtistSimilar.Include(x => x.SimilarArtist).Where(x => x.ArtistId == artist.Id || x.SimilarArtistId == artist.Id).ToList(); - DbContext.ArtistSimilar.RemoveRange(similarArtists); - } - artist.LastUpdated = now; - await DbContext.SaveChangesAsync().ConfigureAwait(false); - if (didRenameArtist) - { - // Many contributing artists do not have releases and will not have an empty Artist folder - if (Directory.Exists(newArtistFolder)) - { - // Update artist tracks to have new artist name in ID3 metadata - foreach (var mp3 in Directory.GetFiles(newArtistFolder, "*.mp3", SearchOption.AllDirectories)) - { - var trackFileInfo = new FileInfo(mp3); - var audioMetaData = await AudioMetaDataHelper.GetInfo(trackFileInfo).ConfigureAwait(false); - if (audioMetaData != null) - { - audioMetaData.Artist = artist.Name; - AudioMetaDataHelper.WriteTags(audioMetaData, trackFileInfo); - } - } - - await ScanArtistReleasesFolders(user, artist.RoadieId, Configuration.LibraryFolder, false).ConfigureAwait(false); - } - } - - CacheManager.ClearRegion(artist.CacheRegion); - Logger.LogInformation($"UpdateArtist `{artist}` By User `{user}`: Renamed Artist [{didRenameArtist}]"); - } - catch (Exception ex) - { - Logger.LogError(ex); - errors.Add(ex); - } - - sw.Stop(); - - return new OperationResult - { - IsSuccess = errors.Count == 0, - Data = errors.Count == 0, - OperationTime = sw.ElapsedMilliseconds, - Errors = errors - }; - } - - public async Task> UploadArtistImage(Library.Identity.User user, Guid id, IFormFile file) - { - var bytes = new byte[0]; - using (var ms = new MemoryStream()) - { - file.CopyTo(ms); - bytes = ms.ToArray(); - } - - return await SaveImageBytes(user, id, bytes).ConfigureAwait(false); - } - - private async Task> ArtistByIdAction(Guid id, IEnumerable includes) + private async Task> ArtistByIdActionAsync(Guid id, IEnumerable includes) { var timings = new Dictionary(); var tsw = new Stopwatch(); @@ -894,7 +83,11 @@ namespace Roadie.Api.Services tsw.Stop(); timings.Add("getArtist", tsw.ElapsedMilliseconds); - if (artist == null) return new OperationResult(true, $"Artist Not Found [{id}]"); + if (artist == null) + { + return new OperationResult(true, $"Artist Not Found [{id}]"); + } + tsw.Restart(); var result = artist.Adapt(); result.BandStatus ??= nameof(BandStatus.Unknown); @@ -970,7 +163,9 @@ namespace Roadie.Api.Services { var ta = DbContext.Artists.FirstOrDefault(x => x.Id == t.ArtistId.Value); if (ta != null) + { trackArtist = ArtistList.FromDataArtist(ta, ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, ta.RoadieId)); + } } track.TrackArtist = trackArtist; tracks.Add(track); @@ -1164,8 +359,12 @@ namespace Roadie.Api.Services { Limit = 100 }; - var r = await CollectionService.List(null, collectionPagedRequest, artistId: artist.RoadieId).ConfigureAwait(false); - if (r.IsSuccess) result.CollectionsWithArtistReleases = r.Rows.ToArray(); + var r = await CollectionService.ListAsync(null, collectionPagedRequest, artistId: artist.RoadieId).ConfigureAwait(false); + if (r.IsSuccess) + { + result.CollectionsWithArtistReleases = r.Rows.ToArray(); + } + tsw.Stop(); timings.Add("collections", tsw.ElapsedMilliseconds); } @@ -1211,7 +410,7 @@ namespace Roadie.Api.Services { FilterToArtistId = artist.RoadieId }; - var r = await PlaylistService.List(pg).ConfigureAwait(false); + var r = await PlaylistService.ListAsync(pg).ConfigureAwait(false); if (r.IsSuccess) { result.PlaylistsWithArtistReleases = r.Rows.ToArray(); @@ -1278,7 +477,7 @@ namespace Roadie.Api.Services }; } - private async Task> MergeArtists(Library.Identity.User user, data.Artist artistToMerge, data.Artist artistToMergeInto) + private async Task> MergeArtistsAsync(Library.Identity.User user, data.Artist artistToMerge, data.Artist artistToMergeInto) { SimpleContract.Requires(artistToMerge != null, "Invalid Artist"); SimpleContract.Requires(artistToMergeInto != null, "Invalid Artist"); @@ -1380,25 +579,35 @@ namespace Roadie.Api.Services Logger.LogError(ex, "MergeArtists: Error Moving Artist Primary and Secondary Images"); } - var userArtists = DbContext.UserArtists.Where(x => x.ArtistId == artistToMerge.Id).ToArray(); - var artistTracks = DbContext.Tracks.Where(x => x.ArtistId == artistToMerge.Id).ToArray(); + var userArtists = await DbContext.UserArtists.Where(x => x.ArtistId == artistToMerge.Id).ToArrayAsync().ConfigureAwait(false); + if(userArtists != null) + { + foreach(var userArtist in userArtists) + { + userArtist.ArtistId = artistToMergeInto.Id; + userArtist.LastUpdated = now; + } + } + + var artistTracks = await DbContext.Tracks.Where(x => x.ArtistId == artistToMerge.Id).ToArrayAsync().ConfigureAwait(false); if (artistTracks != null) { foreach (var artistTrack in artistTracks) { artistTrack.ArtistId = artistToMergeInto.Id; + artistTrack.LastUpdated = now; } } - var artistReleases = DbContext.Releases.Where(x => x.ArtistId == artistToMerge.Id).ToArray(); + var artistReleases = await DbContext.Releases.Where(x => x.ArtistId == artistToMerge.Id).ToArrayAsync().ConfigureAwait(false); if (artistReleases != null) { foreach (var artistRelease in artistReleases) { // See if there is already a release by the same name for the artist to merge into, if so then merge releases - var artistToMergeHasRelease = DbContext.Releases.FirstOrDefault(x => x.ArtistId == artistToMerge.Id && x.Title == artistRelease.Title); + var artistToMergeHasRelease = DbContext.Releases.FirstOrDefault(x => x.ArtistId == artistToMerge.Id && string.Equals(x.Title, artistRelease.Title, StringComparison.OrdinalIgnoreCase)); if (artistToMergeHasRelease != null) { - await ReleaseService.MergeReleases(user, artistRelease, artistToMergeHasRelease, false).ConfigureAwait(false); + await ReleaseService.MergeReleasesAsync(user, artistRelease, artistToMergeHasRelease, false).ConfigureAwait(false); } else { @@ -1415,11 +624,11 @@ namespace Roadie.Api.Services foreach (var release in DbContext.Releases.Include("Artist").Where(x => x.ArtistId == artistToMerge.Id).ToArray()) { var originalReleaseFolder = release.ReleaseFileFolder(artistToMergeFolder); - await ReleaseService.UpdateRelease(user, release.Adapt(), originalReleaseFolder).ConfigureAwait(false); + await ReleaseService.UpdateReleaseAsync(user, release.Adapt(), originalReleaseFolder).ConfigureAwait(false); } await DbContext.SaveChangesAsync().ConfigureAwait(false); - await Delete(user, artistToMerge, true).ConfigureAwait(false); + await DeleteAsync(user, artistToMerge, true).ConfigureAwait(false); result = true; @@ -1437,7 +646,7 @@ namespace Roadie.Api.Services var sw = new Stopwatch(); sw.Start(); var errors = new List(); - var artist = DbContext.Artists.FirstOrDefault(x => x.RoadieId == id); + var artist = await DbContext.Artists.FirstOrDefaultAsync(x => x.RoadieId == id).ConfigureAwait(false); if (artist == null) { return new OperationResult(true, $"Artist Not Found [{id}]"); @@ -1478,5 +687,824 @@ namespace Roadie.Api.Services Errors = errors }; } + + public async Task> ByIdAsync(User roadieUser, Guid id, IEnumerable includes) + { + var timings = new Dictionary(); + var tsw = new Stopwatch(); + + var sw = Stopwatch.StartNew(); + sw.Start(); + var cacheKey = $"urn:artist_by_id_operation:{id}:{(includes == null ? "0" : string.Join("|", includes))}"; + var result = await CacheManager.GetAsync(cacheKey, async () => + { + tsw.Restart(); + var rr = await ArtistByIdActionAsync(id, includes).ConfigureAwait(false); + tsw.Stop(); + timings.Add(nameof(ArtistByIdActionAsync), tsw.ElapsedMilliseconds); + return rr; + }, data.Artist.CacheRegionUrn(id)).ConfigureAwait(false); + if (result?.Data != null && roadieUser != null) + { + tsw.Restart(); + var artist = await GetArtist(id).ConfigureAwait(false); + tsw.Stop(); + timings.Add("getArtist", tsw.ElapsedMilliseconds); + tsw.Restart(); + var userBookmarkResult = await BookmarkService.ListAsync(roadieUser, new PagedRequest(), false, BookmarkType.Artist).ConfigureAwait(false); + if (userBookmarkResult.IsSuccess) + { + result.Data.UserBookmarked = userBookmarkResult?.Rows?.FirstOrDefault(x => x?.Bookmark?.Value == artist?.RoadieId.ToString()) != null; + } + tsw.Stop(); + timings.Add("userBookmarkResult", tsw.ElapsedMilliseconds); + tsw.Restart(); + var userArtist = DbContext.UserArtists.FirstOrDefault(x => x.ArtistId == artist.Id && x.UserId == roadieUser.Id); + if (userArtist != null) + { + result.Data.UserRating = new UserArtist + { + IsDisliked = userArtist.IsDisliked ?? false, + IsFavorite = userArtist.IsFavorite ?? false, + Rating = userArtist.Rating + }; + } + tsw.Stop(); + timings.Add("userArtist", tsw.ElapsedMilliseconds); + + if (result.Data.Comments.Any()) + { + tsw.Restart(); + var commentIds = result.Data.Comments.Select(x => x.DatabaseId).ToArray(); + var userCommentReactions = (from cr in DbContext.CommentReactions + where commentIds.Contains(cr.CommentId) + where cr.UserId == roadieUser.Id + select cr).ToArray(); + foreach (var comment in result.Data.Comments) + { + var userCommentReaction = Array.Find(userCommentReactions, x => x.CommentId == comment.DatabaseId); + comment.IsDisliked = userCommentReaction?.ReactionValue == CommentReaction.Dislike; + comment.IsLiked = userCommentReaction?.ReactionValue == CommentReaction.Like; + } + + tsw.Stop(); + timings.Add("userCommentReactions", tsw.ElapsedMilliseconds); + } + } + + sw.Stop(); + Logger.LogInformation($"ById Artist: `{ result?.Data }`, includes [{ includes.ToCSV() }], timings [{ timings.ToTimings() }]"); + return new OperationResult(result.Messages) + { + Data = result?.Data, + Errors = result?.Errors, + IsNotFoundResult = result?.IsNotFoundResult ?? false, + IsSuccess = result?.IsSuccess ?? false, + OperationTime = sw.ElapsedMilliseconds + }; + } + + public async Task> DeleteAsync(Library.Identity.User user, data.Artist artist, bool deleteFolder) + { + var isSuccess = false; + try + { + if (artist != null) + { + DbContext.Artists.Remove(artist); + await DbContext.SaveChangesAsync().ConfigureAwait(false); + if (deleteFolder) + { + // Delete all image files for Artist + foreach (var file in ImageHelper.ImageFilesInFolder(artist.ArtistFileFolder(Configuration), SearchOption.TopDirectoryOnly)) + { + try + { + File.Delete(file); + Logger.LogWarning("For Artist [{0}], Deleted File [{1}]", artist.Id, file); + } + catch (Exception ex) + { + Logger.LogError(ex, $"Error Deleting File [{file}] Exception [{ex}]"); + } + } + + var artistDir = new DirectoryInfo(artist.ArtistFileFolder(Configuration)); + FolderPathHelper.DeleteEmptyFolders(artistDir.Parent); + } + await BookmarkService.RemoveAllBookmarksForItemAsync(BookmarkType.Artist, artist.Id).ConfigureAwait(false); + await UpdatePlaylistCountsForArtist(artist.Id, DateTime.UtcNow).ConfigureAwait(false); + CacheManager.ClearRegion(artist.CacheRegion); + Logger.LogWarning("User `{0}` deleted Artist `{1}]`", user, artist); + isSuccess = true; + } + } + catch (Exception ex) + { + Logger.LogError(ex, ex.Serialize()); + return new OperationResult + { + Errors = new Exception[1] { ex } + }; + } + + return new OperationResult + { + IsSuccess = isSuccess, + Data = isSuccess + }; + } + + public async Task> ListAsync(User roadieUser, PagedRequest request, bool? doRandomize = false, bool? onlyIncludeWithReleases = true) + { + var sw = new Stopwatch(); + sw.Start(); + + int? rowCount = null; + + IQueryable favoriteArtistIds = null; + if (request.FilterFavoriteOnly) + { + favoriteArtistIds = from a in DbContext.Artists + join ua in DbContext.UserArtists on a.Id equals ua.ArtistId + where ua.IsFavorite ?? false + where roadieUser == null || ua.UserId == roadieUser.Id + select a.Id; + } + IQueryable labelArtistIds = null; + if (request.FilterToLabelId.HasValue) + { + labelArtistIds = (from l in DbContext.Labels + join rl in DbContext.ReleaseLabels on l.Id equals rl.LabelId + join r in DbContext.Releases on rl.ReleaseId equals r.Id + where l.RoadieId == request.FilterToLabelId + select r.ArtistId) + .Distinct(); + } + IQueryable genreArtistIds = null; + var isFilteredToGenre = false; + if (request.FilterToGenreId.HasValue) + { + genreArtistIds = (from ag in DbContext.ArtistGenres + join g in DbContext.Genres on ag.GenreId equals g.Id + where g.RoadieId == request.FilterToGenreId + select ag.ArtistId) + .Distinct(); + isFilteredToGenre = true; + } + else if (!string.IsNullOrEmpty(request.Filter) && request.Filter.StartsWith(":genre", StringComparison.OrdinalIgnoreCase)) + { + var genreFilter = request.Filter.Replace(":genre ", string.Empty); + genreArtistIds = (from ag in DbContext.ArtistGenres + join g in DbContext.Genres on ag.GenreId equals g.Id + where g.Name.Contains(genreFilter) + select ag.ArtistId) + .Distinct(); + isFilteredToGenre = true; + request.Filter = null; + } + var onlyWithReleases = onlyIncludeWithReleases ?? true; + var isEqualFilter = false; + if (!string.IsNullOrEmpty(request.FilterValue)) + { + var filter = request.FilterValue; + // if filter string is wrapped in quotes then is an exact not like search, e.g. "Diana Ross" should not return "Diana Ross & The Supremes" + if (filter.StartsWith('"') && filter.EndsWith('"')) + { + isEqualFilter = true; +#pragma warning disable IDE0057 // Use range operator + request.Filter = filter.Substring(1, filter.Length - 2); +#pragma warning restore IDE0057 // Use range operator + } + } + var normalizedFilterValue = !string.IsNullOrEmpty(request.FilterValue) + ? request.FilterValue.ToAlphanumericName() + : null; + + int[] randomArtistIds = null; + SortedDictionary randomArtistData = null; + if (doRandomize ?? false) + { + var randomLimit = request.Limit ?? roadieUser?.RandomReleaseLimit ?? request.LimitValue; + randomArtistData = await DbContext.RandomArtistIdsAsync(roadieUser?.Id ?? -1, randomLimit, request.FilterFavoriteOnly, request.FilterRatedOnly).ConfigureAwait(false); + randomArtistIds = randomArtistData.Select(x => x.Value).ToArray(); + rowCount = await DbContext.Artists.CountAsync().ConfigureAwait(false); + } + var result = from a in DbContext.Artists + where !onlyWithReleases || a.ReleaseCount > 0 + where randomArtistIds == null || randomArtistIds.Contains(a.Id) + where request.FilterToArtistId == null || a.RoadieId == request.FilterToArtistId + where request.FilterMinimumRating == null || a.Rating >= request.FilterMinimumRating.Value + where string.IsNullOrEmpty(normalizedFilterValue) || + a.Name.ToLower().Contains(normalizedFilterValue) || + a.SortName.ToLower().Contains(normalizedFilterValue) || + a.RealName.ToLower().Contains(normalizedFilterValue) || + a.AlternateNames.Contains(normalizedFilterValue) + where !isEqualFilter || + a.Name.ToLower().Equals(normalizedFilterValue) || + a.SortName.ToLower().Equals(normalizedFilterValue) || + a.RealName.ToLower().Equals(normalizedFilterValue) || + a.AlternateNames.ToLower().Equals(normalizedFilterValue) + where !request.FilterFavoriteOnly || favoriteArtistIds.Contains(a.Id) + where request.FilterToLabelId == null || labelArtistIds.Contains(a.Id) + where !isFilteredToGenre || genreArtistIds.Contains(a.Id) + select new ArtistList + { + DatabaseId = a.Id, + Id = a.RoadieId, + Artist = new DataToken + { + Text = a.Name, + Value = a.RoadieId.ToString() + }, + Thumbnail = ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, a.RoadieId), + Rating = a.Rating, + Rank = (double?)a.Rank, + CreatedDate = a.CreatedDate, + LastUpdated = a.LastUpdated, + LastPlayed = a.LastPlayed, + PlayedCount = a.PlayedCount, + ReleaseCount = a.ReleaseCount, + TrackCount = a.TrackCount, + SortName = a.SortName, + Status = a.Status + }; + + ArtistList[] rows; + rowCount ??= result.Count(); + + if (doRandomize ?? false) + { + var resultData = await result.ToArrayAsync().ConfigureAwait(false); + rows = (from r in resultData + join ra in randomArtistData on r.DatabaseId equals ra.Value + orderby ra.Key + select r + ).ToArray(); + } + else + { + string sortBy; + if (request.ActionValue == User.ActionKeyUserRated) + { + sortBy = string.IsNullOrEmpty(request.Sort) + ? request.OrderValue(new Dictionary { { "Rating", "DESC" }, { "Artist.Text", "ASC" } }) + : request.OrderValue(); + } + else + { + sortBy = request.OrderValue(new Dictionary { { "SortName", "ASC" }, { "Artist.Text", "ASC" } }); + } + rows = await result.OrderBy(sortBy).Skip(request.SkipValue).Take(request.LimitValue).ToArrayAsync().ConfigureAwait(false); + } + + if (rows.Length > 0 && roadieUser != null) + { + var rowIds = rows.Select(x => x.DatabaseId).ToArray(); + var userArtistRatings = await (from ua in DbContext.UserArtists + where ua.UserId == roadieUser.Id + where rowIds.Contains(ua.ArtistId) + select ua).ToArrayAsync().ConfigureAwait(false); + + foreach (var userArtistRating in userArtistRatings.Where(x => rows.Select(r => r.DatabaseId).Contains(x.ArtistId))) + { + var row = Array.Find(rows, x => x.DatabaseId == userArtistRating.ArtistId); + if (row != null) + { + row.UserRating = new UserArtist + { + IsDisliked = userArtistRating.IsDisliked ?? false, + IsFavorite = userArtistRating.IsFavorite ?? false, + Rating = userArtistRating.Rating, + RatedDate = userArtistRating.LastUpdated ?? userArtistRating.CreatedDate + }; + } + } + } + if (!string.IsNullOrEmpty(request.Filter) && rowCount == 0 && Configuration.RecordNoResultSearches) + { + // Create request for no artist found + var req = new data.Request + { + UserId = roadieUser?.Id, + Description = request.Filter + }; + DbContext.Requests.Add(req); + await DbContext.SaveChangesAsync().ConfigureAwait(false); + } + sw.Stop(); + return new Library.Models.Pagination.PagedResult + { + TotalCount = rowCount.Value, + CurrentPage = request.PageValue, + TotalPages = (int)Math.Ceiling((double)rowCount / request.LimitValue), + OperationTime = sw.ElapsedMilliseconds, + Rows = rows + }; + } + + /// + /// Merge one Artist into another one + /// + /// The Artist to be merged + /// The Artist to merge into + public async Task> MergeArtistsAsync(Library.Identity.User user, Guid artistToMergeId, Guid artistToMergeIntoId) + { + var sw = new Stopwatch(); + sw.Start(); + + var errors = new List(); + var artistToMerge = DbContext.Artists + .Include(x => x.Genres) + .Include("Genres.Genre") + .FirstOrDefault(x => x.RoadieId == artistToMergeId); + if (artistToMerge == null) + { + Logger.LogWarning("MergeArtists Unknown Artist [{0}]", artistToMergeId); + return new OperationResult(true, $"Artist Not Found [{artistToMergeId}]"); + } + + var mergeIntoArtist = DbContext.Artists + .Include(x => x.Genres) + .Include("Genres.Genre") + .FirstOrDefault(x => x.RoadieId == artistToMergeIntoId); + if (mergeIntoArtist == null) + { + Logger.LogWarning("MergeArtists Unknown Artist [{0}]", artistToMergeIntoId); + return new OperationResult(true, $"Artist Not Found [{artistToMergeIntoId}]"); + } + + try + { + var result = await MergeArtistsAsync(user, artistToMerge, mergeIntoArtist).ConfigureAwait(false); + if (!result.IsSuccess) + { + CacheManager.ClearRegion(artistToMerge.CacheRegion); + CacheManager.ClearRegion(mergeIntoArtist.CacheRegion); + Logger.LogWarning("MergeArtists `{0}` => `{1}`, By User `{2}`", artistToMerge, mergeIntoArtist, + user); + } + } + catch (Exception ex) + { + Logger.LogError(ex); + errors.Add(ex); + } + + sw.Stop(); + + return new OperationResult + { + IsSuccess = errors.Count == 0, + Data = errors.Count == 0, + OperationTime = sw.ElapsedMilliseconds, + Errors = errors + }; + } + + public async Task> RefreshArtistMetadataAsync(Library.Identity.User user, Guid artistId) + { + SimpleContract.Requires(artistId != Guid.Empty, "Invalid ArtistId"); + + var result = true; + var resultErrors = new List(); + var sw = new Stopwatch(); + sw.Start(); + try + { + var artist = DbContext.Artists.FirstOrDefault(x => x.RoadieId == artistId); + if (artist == null) + { + Logger.LogWarning("Unable To Find Artist [{0}]", artistId); + return new OperationResult(); + } + + OperationResult artistSearch = null; + try + { + artistSearch = await ArtistLookupEngine.PerformMetaDataProvidersArtistSearch(new AudioMetaData + { + Artist = artist.Name + }).ConfigureAwait(false); + } + catch (Exception ex) + { + Logger.LogError(ex, ex.Serialize()); + } + + if (artistSearch.IsSuccess) + { + // Do metadata search for Artist like if new Artist then set some overides and merge + var mergeResult = await MergeArtistsAsync(user, artistSearch.Data, artist).ConfigureAwait(false); + if (mergeResult.IsSuccess) + { + artist = mergeResult.Data; + await DbContext.SaveChangesAsync().ConfigureAwait(false); + sw.Stop(); + CacheManager.ClearRegion(artist.CacheRegion); + Logger.LogTrace("Scanned RefreshArtistMetadata [{0}], OperationTime [{1}]", + artist.ToString(), sw.ElapsedMilliseconds); + } + else + { + sw.Stop(); + } + } + } + catch (Exception ex) + { + Logger.LogError(ex, ex.Serialize()); + resultErrors.Add(ex); + result = false; + } + + return new OperationResult + { + Data = result, + IsSuccess = result, + Errors = resultErrors, + OperationTime = sw.ElapsedMilliseconds + }; + } + + public async Task> ScanArtistReleasesFoldersAsync(Library.Identity.User user, Guid artistId, string destinationFolder, bool doJustInfo) + { + SimpleContract.Requires(artistId != Guid.Empty, "Invalid ArtistId"); + + var result = true; + var resultErrors = new List(); + var sw = new Stopwatch(); + sw.Start(); + try + { + var artist = DbContext.Artists + .Include("Releases") + .Include("Releases.Labels") + .FirstOrDefault(x => x.RoadieId == artistId); + if (artist == null) + { + Logger.LogWarning("Unable To Find Artist [{0}]", artistId); + return new OperationResult(); + } + var releaseScannedCount = 0; + var artistFolder = artist.ArtistFileFolder(Configuration); + if (!Directory.Exists(artistFolder)) + { + Logger.LogDebug($"ScanArtistReleasesFolders: ArtistFolder Not Found [{ artistFolder }] For Artist `{ artist }`"); + return new OperationResult(); + } + var scannedArtistFolders = new List(); + // Scan known releases for changes + if (artist.Releases != null) + { + foreach (var release in artist.Releases) + { + try + { + result = result && (await ReleaseService.ScanReleaseFolderAsync(user, Guid.Empty, doJustInfo, release).ConfigureAwait(false)).Data; + releaseScannedCount++; + scannedArtistFolders.Add(release.ReleaseFileFolder(artistFolder)); + } + catch (Exception ex) + { + Logger.LogError(ex, ex.Serialize()); + } + } + } + var artistImage = ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(artistFolder), ImageType.Artist).FirstOrDefault(); + if (artistImage != null) + { + // See if image file is valid image if not delete it + if (ImageHelper.ConvertToJpegFormat(File.ReadAllBytes(artistImage.FullName)) == null) + { + artistImage.Delete(); + } + } + // Any folder found in Artist folder not already scanned scan + var nonReleaseFolders = from d in Directory.EnumerateDirectories(artistFolder) + where !(from r in scannedArtistFolders select r).Contains(d) + orderby d + select d; + foreach (var folder in nonReleaseFolders) + { + await FileDirectoryProcessorService.ProcessAsync(user, new DirectoryInfo(folder), doJustInfo).ConfigureAwait(false); + } + if (!doJustInfo) + { + Services.FileDirectoryProcessorService.DeleteEmptyFolders(new DirectoryInfo(artistFolder), Logger); + } + sw.Stop(); + CacheManager.ClearRegion(artist.CacheRegion); + Logger.LogInformation("Scanned Artist [{0}], Releases Scanned [{1}], OperationTime [{2}]", artist.ToString(), releaseScannedCount, sw.ElapsedMilliseconds); + } + catch (Exception ex) + { + Logger.LogError(ex, ex.Serialize()); + resultErrors.Add(ex); + } + + return new OperationResult + { + Data = result, + IsSuccess = result, + Errors = resultErrors, + OperationTime = sw.ElapsedMilliseconds + }; + } + + public Task> SetReleaseImageByUrlAsync(Library.Identity.User user, Guid id, string imageUrl) => SaveImageBytes(user, id, WebHelper.BytesForImageUrl(imageUrl)); + + public async Task> UpdateArtistAsync(Library.Identity.User user, Artist model) + { + var didRenameArtist = false; + var sw = new Stopwatch(); + sw.Start(); + var errors = new List(); + var artist = DbContext.Artists + .Include(x => x.Genres) + .Include("Genres.Genre") + .FirstOrDefault(x => x.RoadieId == model.Id); + if (artist == null) + { + return new OperationResult(true, $"Artist Not Found [{model.Id}]"); + } + // If artist is being renamed, see if artist already exists with new model supplied name + var artistName = artist.SortNameValue; + var artistModelName = model.SortNameValue; + if (artistName.ToAlphanumericName() != artistModelName.ToAlphanumericName()) + { + var existingArtist = DbContext.Artists.FirstOrDefault(x => x.Name == model.Name || x.SortName == model.SortName); + if (existingArtist != null) + { + return new OperationResult($"Artist already exists `{ existingArtist }` with name [{artistModelName }]."); + } + } + try + { + var now = DateTime.UtcNow; + var originalArtistFolder = artist.ArtistFileFolder(Configuration); + var specialArtistName = model.Name.ToAlphanumericName(); + var alt = new List(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; + artist.BioContext = model.BioContext; + artist.BirthDate = model.BirthDate; + artist.DiscogsId = model.DiscogsId; + artist.EndDate = model.EndDate; + artist.IsLocked = model.IsLocked; + artist.ISNI = model.ISNIList.ToDelimitedList(); + artist.ITunesId = model.ITunesId; + artist.MusicBrainzId = model.MusicBrainzId; + artist.Name = model.Name; + artist.Profile = model.Profile; + artist.Rating = model.Rating; + artist.RealName = model.RealName; + artist.SortName = model.SortName; + artist.SpotifyId = model.SpotifyId; + artist.Status = SafeParser.ToEnum(model.Status); + artist.Tags = model.TagsList.ToDelimitedList(); + artist.URLs = model.URLsList.ToDelimitedList(); + + var newArtistFolder = artist.ArtistFileFolder(Configuration); + // Rename artist folder to reflect new artist name + if (!newArtistFolder.Equals(originalArtistFolder, StringComparison.OrdinalIgnoreCase)) + { + // If folder already exists for new artist name that means another artist has that folder (usually sort name) + if (Directory.Exists(newArtistFolder)) + { + // Set sortname to be unique and try again + var oldSortName = artist.SortName; + artist.SortName = $"{ artist.SortName} [{ artist.Id }]"; + Logger.LogTrace($"Updated Artist SortName From [{ oldSortName }] to [{ artist.SortName }]"); + newArtistFolder = artist.ArtistFileFolder(Configuration); + if (Directory.Exists(newArtistFolder)) + { + return new OperationResult($"Artist Folder [{ newArtistFolder }] already exists."); + } + } + didRenameArtist = true; + if (Directory.Exists(originalArtistFolder)) + { + Logger.LogTrace($"Moving Artist From Folder [{originalArtistFolder}] -> [{newArtistFolder}]"); + Directory.Move(originalArtistFolder, newArtistFolder); + } + } + if (!Directory.Exists(newArtistFolder)) + { + Directory.CreateDirectory(newArtistFolder); + } + + var artistImage = ImageHelper.ImageDataFromUrl(model.NewThumbnailData); + if (artistImage != null) + { + // Save unaltered image to cover file + var artistImageName = Path.Combine(newArtistFolder, ImageHelper.ArtistImageFilename); + File.WriteAllBytes(artistImageName, ImageHelper.ConvertToJpegFormat(artistImage)); + } + + if (model.NewSecondaryImagesData?.Any() == true) + { + // Additional images to add to artist + var looper = 0; + foreach (var newSecondaryImageData in model.NewSecondaryImagesData) + { + var artistSecondaryImage = ImageHelper.ImageDataFromUrl(newSecondaryImageData); + if (artistSecondaryImage != null) + { + // Ensure is jpeg first + artistSecondaryImage = ImageHelper.ConvertToJpegFormat(artistSecondaryImage); + + var artistImageFilename = Path.Combine(newArtistFolder, string.Format(ImageHelper.ArtistSecondaryImageFilename, looper.ToString("00"))); + while (File.Exists(artistImageFilename)) + { + looper++; + artistImageFilename = Path.Combine(newArtistFolder, string.Format(ImageHelper.ArtistSecondaryImageFilename, looper.ToString("00"))); + } + + File.WriteAllBytes(artistImageFilename, artistSecondaryImage); + } + + looper++; + } + } + + if (model.Genres?.Any() == true) + { + // Remove existing Genres not in model list + foreach (var genre in artist.Genres.ToList()) + { + var doesExistInModel = model.Genres.Any(x => SafeParser.ToGuid(x.Genre.Value) == genre.Genre.RoadieId); + if (!doesExistInModel) + { + artist.Genres.Remove(genre); + } + } + + // Add new Genres in model not in data + foreach (var genre in model.Genres) + { + var genreId = SafeParser.ToGuid(genre.Genre.Value); + var doesExistInData = artist.Genres.Any(x => x.Genre.RoadieId == genreId); + if (!doesExistInData) + { + var g = DbContext.Genres.FirstOrDefault(x => x.RoadieId == genreId); + if (g != null) + { + artist.Genres.Add(new data.ArtistGenre + { + ArtistId = artist.Id, + GenreId = g.Id, + Genre = g + }); + } + } + } + } + else if (model.Genres?.Any() != true) + { + artist.Genres.Clear(); + } + + if (model.AssociatedArtistsTokens?.Any() == true) + { + var associatedArtists = DbContext.ArtistAssociations.Include(x => x.AssociatedArtist) + .Where(x => x.ArtistId == artist.Id).ToList(); + + // Remove existing AssociatedArtists not in model list + foreach (var associatedArtist in associatedArtists) + { + var doesExistInModel = model.AssociatedArtistsTokens.Any(x => + SafeParser.ToGuid(x.Value) == associatedArtist.AssociatedArtist.RoadieId); + if (!doesExistInModel) + { + DbContext.ArtistAssociations.Remove(associatedArtist); + } + } + + // Add new AssociatedArtists in model not in data + foreach (var associatedArtist in model.AssociatedArtistsTokens) + { + var associatedArtistId = SafeParser.ToGuid(associatedArtist.Value); + var doesExistInData = + associatedArtists.Any(x => x.AssociatedArtist.RoadieId == associatedArtistId); + if (!doesExistInData) + { + var a = DbContext.Artists.FirstOrDefault(x => x.RoadieId == associatedArtistId); + if (a != null) + { + DbContext.ArtistAssociations.Add(new data.ArtistAssociation + { + ArtistId = artist.Id, + AssociatedArtistId = a.Id + }); + } + } + } + } + else if (model.AssociatedArtistsTokens?.Any() != true) + { + var associatedArtists = DbContext.ArtistAssociations.Include(x => x.AssociatedArtist).Where(x => x.ArtistId == artist.Id || x.AssociatedArtistId == artist.Id).ToList(); + DbContext.ArtistAssociations.RemoveRange(associatedArtists); + } + + if (model.SimilarArtistsTokens?.Any() == true) + { + var similarArtists = DbContext.ArtistSimilar.Include(x => x.SimilarArtist) + .Where(x => x.ArtistId == artist.Id).ToList(); + + // Remove existing AssociatedArtists not in model list + foreach (var similarArtist in similarArtists) + { + var doesExistInModel = model.SimilarArtistsTokens.Any(x => + SafeParser.ToGuid(x.Value) == similarArtist.SimilarArtist.RoadieId); + if (!doesExistInModel) + { + DbContext.ArtistSimilar.Remove(similarArtist); + } + } + + // Add new SimilarArtists in model not in data + foreach (var similarArtist in model.SimilarArtistsTokens) + { + var similarArtistId = SafeParser.ToGuid(similarArtist.Value); + var doesExistInData = similarArtists.Any(x => x.SimilarArtist.RoadieId == similarArtistId); + if (!doesExistInData) + { + var a = DbContext.Artists.FirstOrDefault(x => x.RoadieId == similarArtistId); + if (a != null) + { + DbContext.ArtistSimilar.Add(new data.ArtistSimilar + { + ArtistId = artist.Id, + SimilarArtistId = a.Id + }); + } + } + } + } + else if (model.SimilarArtistsTokens?.Any() != true) + { + var similarArtists = DbContext.ArtistSimilar.Include(x => x.SimilarArtist).Where(x => x.ArtistId == artist.Id || x.SimilarArtistId == artist.Id).ToList(); + DbContext.ArtistSimilar.RemoveRange(similarArtists); + } + artist.LastUpdated = now; + await DbContext.SaveChangesAsync().ConfigureAwait(false); + if (didRenameArtist) + { + // Many contributing artists do not have releases and will not have an empty Artist folder + if (Directory.Exists(newArtistFolder)) + { + // Update artist tracks to have new artist name in ID3 metadata + foreach (var mp3 in Directory.GetFiles(newArtistFolder, "*.mp3", SearchOption.AllDirectories)) + { + var trackFileInfo = new FileInfo(mp3); + var audioMetaData = await AudioMetaDataHelper.GetInfo(trackFileInfo).ConfigureAwait(false); + if (audioMetaData != null) + { + audioMetaData.Artist = artist.Name; + AudioMetaDataHelper.WriteTags(audioMetaData, trackFileInfo); + } + } + + await ScanArtistReleasesFoldersAsync(user, artist.RoadieId, Configuration.LibraryFolder, false).ConfigureAwait(false); + } + } + + CacheManager.ClearRegion(artist.CacheRegion); + Logger.LogInformation($"UpdateArtist `{artist}` By User `{user}`: Renamed Artist [{didRenameArtist}]"); + } + catch (Exception ex) + { + Logger.LogError(ex); + errors.Add(ex); + } + + sw.Stop(); + + return new OperationResult + { + IsSuccess = errors.Count == 0, + Data = errors.Count == 0, + OperationTime = sw.ElapsedMilliseconds, + Errors = errors + }; + } + + public Task> UploadArtistImageAsync(Library.Identity.User user, Guid id, IFormFile file) + { + var bytes = new byte[0]; + using (var ms = new MemoryStream()) + { + file.CopyTo(ms); + bytes = ms.ToArray(); + } + return SaveImageBytes(user, id, bytes); + } } } \ No newline at end of file diff --git a/Roadie.Api.Services/BookmarkService.cs b/Roadie.Api.Services/BookmarkService.cs index 985cd83..e8ed4db 100644 --- a/Roadie.Api.Services/BookmarkService.cs +++ b/Roadie.Api.Services/BookmarkService.cs @@ -1,5 +1,4 @@ -using Mapster; -using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Roadie.Library; using Roadie.Library.Caching; @@ -20,7 +19,6 @@ using System.Diagnostics; using System.Linq; using System.Linq.Dynamic.Core; using System.Threading.Tasks; -using data = Roadie.Library.Data; using models = Roadie.Library.Models; namespace Roadie.Api.Services @@ -41,30 +39,7 @@ namespace Roadie.Api.Services UserService = userService; } - /// - /// When a bookmarkable item gets deleted then delete any bookmarks to that item, since its a generic column there is not FK setup. - /// - public async Task> RemoveAllBookmarksForItem(BookmarkType type, int id) - { - var bookmarks = await DbContext.Bookmarks.Include(x => x.User) - .Where(x => x.BookmarkType == type && x.BookmarkTargetId == id) - .ToListAsync(); - - var users = bookmarks.Select(x => x.User).ToList().Distinct(); - DbContext.Bookmarks.RemoveRange(bookmarks); - await DbContext.SaveChangesAsync(); - foreach(var user in users) - { - CacheManager.ClearRegion(user.CacheRegion); - } - return new OperationResult - { - IsSuccess = true, - Data = true - }; - } - - public async Task> List(User roadieUser, PagedRequest request, bool? doRandomize = false, BookmarkType? filterType = null) + public async Task> ListAsync(User roadieUser, PagedRequest request, bool? doRandomize = false, BookmarkType? filterType = null) { var sw = new Stopwatch(); sw.Start(); @@ -95,14 +70,19 @@ namespace Roadie.Api.Services var rowCount = result.Count(); var rows = result.OrderBy(sortBy).Skip(request.SkipValue).Take(request.LimitValue).ToArray(); - var user = await GetUser(roadieUser.UserId); + var user = await GetUser(roadieUser.UserId).ConfigureAwait(false); foreach (var row in rows) + { switch (row.Type) { case BookmarkType.Artist: var artist = DbContext.Artists.FirstOrDefault(x => x.Id == row.BookmarkTargetId); - if (artist == null) continue; + if (artist == null) + { + continue; + } + row.Bookmark = new models.DataToken { Text = artist.Name, @@ -117,7 +97,11 @@ namespace Roadie.Api.Services case BookmarkType.Release: var release = DbContext.Releases.Include(x => x.Artist) .FirstOrDefault(x => x.Id == row.BookmarkTargetId); - if (release == null) continue; + if (release == null) + { + continue; + } + row.Bookmark = new models.DataToken { Text = release.Title, @@ -137,7 +121,11 @@ namespace Roadie.Api.Services .Include(x => x.ReleaseMedia.Release.Artist) .Include(x => x.TrackArtist) .FirstOrDefault(x => x.Id == row.BookmarkTargetId); - if (track == null) continue; + if (track == null) + { + continue; + } + row.Bookmark = new models.DataToken { Text = track.Title, @@ -165,7 +153,11 @@ namespace Roadie.Api.Services var playlist = DbContext.Playlists .Include(x => x.User) .FirstOrDefault(x => x.Id == row.BookmarkTargetId); - if (playlist == null) continue; + if (playlist == null) + { + continue; + } + row.Bookmark = new models.DataToken { Text = playlist.Name, @@ -180,7 +172,11 @@ namespace Roadie.Api.Services case BookmarkType.Collection: var collection = DbContext.Collections.FirstOrDefault(x => x.Id == row.BookmarkTargetId); - if (collection == null) continue; + if (collection == null) + { + continue; + } + row.Bookmark = new models.DataToken { Text = collection.Name, @@ -196,7 +192,11 @@ namespace Roadie.Api.Services case BookmarkType.Label: var label = DbContext.Labels.FirstOrDefault(x => x.Id == row.BookmarkTargetId); - if (label == null) continue; + if (label == null) + { + continue; + } + row.Bookmark = new models.DataToken { Text = label.Name, @@ -207,8 +207,7 @@ namespace Roadie.Api.Services row.SortName = label.SortName ?? label.Name; break; } - - ; + }; sw.Stop(); return new Library.Models.Pagination.PagedResult { @@ -219,5 +218,29 @@ namespace Roadie.Api.Services Rows = rows }; } + + /// + /// When a bookmarkable item gets deleted then delete any bookmarks to that item, since its a generic column there is not FK setup. + /// + public async Task> RemoveAllBookmarksForItemAsync(BookmarkType type, int id) + { + var bookmarks = await DbContext.Bookmarks.Include(x => x.User) + .Where(x => x.BookmarkType == type && x.BookmarkTargetId == id) + .ToListAsync() + .ConfigureAwait(false); + + var users = bookmarks.Select(x => x.User).ToList().Distinct(); + DbContext.Bookmarks.RemoveRange(bookmarks); + await DbContext.SaveChangesAsync().ConfigureAwait(false); + foreach (var user in users) + { + CacheManager.ClearRegion(user.CacheRegion); + } + return new OperationResult + { + IsSuccess = true, + Data = true + }; + } } } \ No newline at end of file diff --git a/Roadie.Api.Services/CollectionService.cs b/Roadie.Api.Services/CollectionService.cs index fe985fb..ae3678a 100644 --- a/Roadie.Api.Services/CollectionService.cs +++ b/Roadie.Api.Services/CollectionService.cs @@ -43,307 +43,7 @@ namespace Roadie.Api.Services BookmarkService = bookmarkService; } - /// - /// Get blank Collection to add - /// - /// - /// - public OperationResult Add(User roadieUser) - { - var sw = Stopwatch.StartNew(); - sw.Start(); - - var id = Guid.Empty; - var collection = new data.Collection - { - Status = Statuses.New - }; - var result = collection.Adapt(); - result.Id = id; - result.Thumbnail = ImageHelper.MakeNewImage(HttpContext,"collection"); - result.MediumThumbnail = ImageHelper.MakeNewImage(HttpContext,"collection"); - result.Maintainer = new DataToken - { - Value = roadieUser.UserId.ToString(), - Text = roadieUser.UserName - }; - sw.Stop(); - - return new OperationResult - { - Data = result, - IsSuccess = true, - OperationTime = sw.ElapsedMilliseconds - }; - } - - public async Task> ById(User roadieUser, Guid id, IEnumerable includes = null) - { - var sw = Stopwatch.StartNew(); - sw.Start(); - - var cacheKey = string.Format("urn:collection_by_id_operation:{0}:{1}", id, includes == null ? "0" : string.Join("|", includes)); - var result = await CacheManager.GetAsync(cacheKey, async () => - { - return await CollectionByIdAction(id, includes); - }, data.Artist.CacheRegionUrn(id)); - sw.Stop(); - if (result?.Data != null && roadieUser != null) - { - var userBookmarkResult = await BookmarkService.List(roadieUser, new PagedRequest(), false, BookmarkType.Collection); - if (userBookmarkResult.IsSuccess) - { - result.Data.UserBookmarked = userBookmarkResult?.Rows?.FirstOrDefault(x => x?.Bookmark?.Value == result?.Data?.Id?.ToString()) != null; - } - if (result.Data.Comments.Any()) - { - var commentIds = result.Data.Comments.Select(x => x.DatabaseId).ToArray(); - var userCommentReactions = (from cr in DbContext.CommentReactions - where commentIds.Contains(cr.CommentId) - where cr.UserId == roadieUser.Id - select cr).ToArray(); - foreach (var comment in result.Data.Comments) - { - var userCommentReaction = - userCommentReactions.FirstOrDefault(x => x.CommentId == comment.DatabaseId); - comment.IsDisliked = userCommentReaction?.ReactionValue == CommentReaction.Dislike; - comment.IsLiked = userCommentReaction?.ReactionValue == CommentReaction.Like; - } - } - } - - return new OperationResult(result.Messages) - { - Data = result?.Data, - IsNotFoundResult = result?.IsNotFoundResult ?? false, - Errors = result?.Errors, - IsSuccess = result?.IsSuccess ?? false, - OperationTime = sw.ElapsedMilliseconds - }; - } - - public async Task> DeleteCollection(User user, Guid id) - { - var sw = new Stopwatch(); - sw.Start(); - var errors = new List(); - var collection = DbContext.Collections.FirstOrDefault(x => x.RoadieId == id); - if (collection == null) return new OperationResult(true, $"Collection Not Found [{id}]"); - if (!user.IsEditor) - { - Logger.LogWarning($"DeleteCollection: Access Denied: `{collection}`, By User `{user}`"); - var r = new OperationResult("Access Denied"); - r.IsAccessDeniedResult = true; - return r; - } - - try - { - DbContext.Collections.Remove(collection); - await DbContext.SaveChangesAsync(); - await BookmarkService.RemoveAllBookmarksForItem(BookmarkType.Collection, collection.Id); - var collectionImageFilename = collection.PathToImage(Configuration); - if (File.Exists(collectionImageFilename)) - { - File.Delete(collectionImageFilename); - } - } - catch (Exception ex) - { - Logger.LogError(ex); - errors.Add(ex); - } - - sw.Stop(); - Logger.LogWarning($"DeleteCollection `{collection}`, By User `{user}`"); - return new OperationResult - { - IsSuccess = !errors.Any(), - Data = true, - OperationTime = sw.ElapsedMilliseconds, - Errors = errors - }; - } - - public Task> List(User roadieUser, PagedRequest request, - bool? doRandomize = false, Guid? releaseId = null, Guid? artistId = null) - { - var sw = new Stopwatch(); - sw.Start(); - IQueryable collections = null; - if (artistId.HasValue) - { - collections = (from cr in DbContext.CollectionReleases - join c in DbContext.Collections on cr.CollectionId equals c.Id - join r in DbContext.Releases on cr.ReleaseId equals r.Id - join a in DbContext.Artists on r.ArtistId equals a.Id - where a.RoadieId == artistId - select c).Distinct(); - } - else if (releaseId.HasValue) - { - collections = (from cr in DbContext.CollectionReleases - join c in DbContext.Collections on cr.CollectionId equals c.Id - join r in DbContext.Releases on cr.ReleaseId equals r.Id - where r.RoadieId == releaseId - select c).Distinct(); - } - else - { - collections = DbContext.Collections; - } - - var normalizedFilterValue = !string.IsNullOrEmpty(request.FilterValue) - ? request.FilterValue.ToAlphanumericName() - : null; - - var result = from c in collections - let collectionCount = (from crc in DbContext.CollectionReleases - where crc.CollectionId == c.Id - select crc.Id).Count() - where string.IsNullOrEmpty(normalizedFilterValue) || ( - c.Name.ToLower().Contains(normalizedFilterValue) || - c.SortName.ToLower().Contains(normalizedFilterValue) || - c.AlternateNames.ToLower().Contains(normalizedFilterValue) - ) - where request.FilterToStatusValue == Statuses.Ok || c.Status == request.FilterToStatusValue - select new CollectionList - { - DatabaseId = c.Id, - Collection = new DataToken - { - Text = c.Name, - Value = c.RoadieId.ToString() - }, - Id = c.RoadieId, - CollectionCount = c.CollectionCount, - CollectionType = (c.CollectionType ?? CollectionType.Unknown).ToString(), - CollectionFoundCount = collectionCount, - CreatedDate = c.CreatedDate, - IsLocked = c.IsLocked, - LastUpdated = c.LastUpdated, - Thumbnail = ImageHelper.MakeCollectionThumbnailImage(Configuration, HttpContext, c.RoadieId) - }; - - var sortBy = string.IsNullOrEmpty(request.Sort) - ? request.OrderValue(new Dictionary { { "Collection.Text", "ASC" } }) - : request.OrderValue(); - 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 - { - TotalCount = rowCount, - CurrentPage = request.PageValue, - TotalPages = (int)Math.Ceiling((double)rowCount / request.LimitValue), - OperationTime = sw.ElapsedMilliseconds, - Rows = rows - }); - } - - /// - /// Updates (or Adds) Collection - /// - public async Task> UpdateCollection(User user, Collection model) - { - var isNew = model.Id == Guid.Empty; - var now = DateTime.UtcNow; - - var sw = new Stopwatch(); - sw.Start(); - var errors = new List(); - - var collection = new data.Collection(); - - if (!isNew) - { - collection = DbContext.Collections.FirstOrDefault(x => x.RoadieId == model.Id); - if (collection == null) - { - return new OperationResult(true, string.Format("Collection Not Found [{0}]", model.Id)); - } - // If collection is being renamed, see if collection already exists with new model supplied name - var collectionName = collection.SortNameValue; - var collectionModelName = model.SortNameValue; - if (collectionName.ToAlphanumericName() != collectionModelName.ToAlphanumericName()) - { - var existingCollection = DbContext.Collections.FirstOrDefault(x => x.Name == model.Name || x.SortName == model.SortName); - if (existingCollection != null) - { - return new OperationResult($"Collection already exists `{ collection }` with name [{ collectionModelName }]."); - } - } - } - collection.IsLocked = model.IsLocked; - var oldPathToImage = collection.PathToImage(Configuration); - var didChangeName = collection.Name != model.Name && !isNew; - collection.Name = model.Name; - collection.SortName = model.SortName; - collection.Edition = model.Edition; - collection.ListInCSVFormat = model.ListInCSVFormat; - collection.ListInCSV = model.ListInCSV; - collection.Description = model.Description; - collection.URLs = model.URLs; - collection.Status = SafeParser.ToEnum(model.Status); - collection.CollectionType = SafeParser.ToEnum(model.CollectionType); - collection.Tags = model.TagsList.ToDelimitedList(); - collection.URLs = model.URLsList.ToDelimitedList(); - collection.AlternateNames = model.AlternateNamesList.ToDelimitedList(); - collection.CollectionCount = model.CollectionCount; - - if (model.Maintainer?.Value != null) - { - var maintainer = DbContext.Users.FirstOrDefault(x => x.RoadieId == SafeParser.ToGuid(model.Maintainer.Value)); - if (maintainer != null) - { - collection.MaintainerId = maintainer.Id; - } - } - - if (isNew) - { - await DbContext.Collections.AddAsync(collection); - await DbContext.SaveChangesAsync(); - } - - if (didChangeName) - { - if (File.Exists(oldPathToImage)) - { - File.Move(oldPathToImage, collection.PathToImage(Configuration)); - } - } - - var collectionImage = ImageHelper.ImageDataFromUrl(model.NewThumbnailData); - if (collectionImage != null) - { - // Save unaltered collection image - File.WriteAllBytes(collection.PathToImage(Configuration, true), ImageHelper.ConvertToJpegFormat(collectionImage)); - } - - collection.LastUpdated = now; - await DbContext.SaveChangesAsync(); - - CacheManager.ClearRegion(collection.CacheRegion); - Logger.LogInformation($"UpdateCollection `{collection}` By User `{user}`"); - return new OperationResult - { - IsSuccess = !errors.Any(), - Data = !errors.Any(), - OperationTime = sw.ElapsedMilliseconds, - Errors = errors - }; - } - - private async Task> CollectionByIdAction(Guid id, IEnumerable includes = null) + private async Task> CollectionByIdActionAsync(Guid id, IEnumerable includes = null) { var timings = new Dictionary(); var tsw = new Stopwatch(); @@ -352,13 +52,13 @@ namespace Roadie.Api.Services sw.Start(); tsw.Restart(); - var collection = await GetCollection(id); + var collection = await GetCollection(id).ConfigureAwait(false); tsw.Stop(); timings.Add("getCollection", tsw.ElapsedMilliseconds); if (collection == null) { - return new OperationResult(true, string.Format("Collection Not Found [{0}]", id)); + return new OperationResult(true, $"Collection Not Found [{id}]"); } var result = collection.Adapt(); var maintainer = DbContext.Users.FirstOrDefault(x => x.Id == collection.MaintainerId); @@ -474,5 +174,309 @@ namespace Roadie.Api.Services OperationTime = sw.ElapsedMilliseconds }; } + + /// + /// Get blank Collection to add + /// + /// + /// + public OperationResult Add(User roadieUser) + { + var sw = Stopwatch.StartNew(); + sw.Start(); + + var id = Guid.Empty; + var collection = new data.Collection + { + Status = Statuses.New + }; + var result = collection.Adapt(); + result.Id = id; + result.Thumbnail = ImageHelper.MakeNewImage(HttpContext, "collection"); + result.MediumThumbnail = ImageHelper.MakeNewImage(HttpContext, "collection"); + result.Maintainer = new DataToken + { + Value = roadieUser.UserId.ToString(), + Text = roadieUser.UserName + }; + sw.Stop(); + + return new OperationResult + { + Data = result, + IsSuccess = true, + OperationTime = sw.ElapsedMilliseconds + }; + } + + public async Task> ByIdAsync(User roadieUser, Guid id, IEnumerable includes = null) + { + var sw = Stopwatch.StartNew(); + sw.Start(); + + var cacheKey = $"urn:collection_by_id_operation:{id}:{(includes == null ? "0" : string.Join("|", includes))}"; + var result = await CacheManager.GetAsync(cacheKey, async () => + { + return await CollectionByIdActionAsync(id, includes).ConfigureAwait(false); + }, data.Artist.CacheRegionUrn(id)).ConfigureAwait(false); + sw.Stop(); + if (result?.Data != null && roadieUser != null) + { + var userBookmarkResult = await BookmarkService.ListAsync(roadieUser, new PagedRequest(), false, BookmarkType.Collection).ConfigureAwait(false); + if (userBookmarkResult.IsSuccess) + { + result.Data.UserBookmarked = userBookmarkResult?.Rows?.FirstOrDefault(x => x?.Bookmark?.Value == result?.Data?.Id?.ToString()) != null; + } + if (result.Data.Comments.Any()) + { + var commentIds = result.Data.Comments.Select(x => x.DatabaseId).ToArray(); + var userCommentReactions = (from cr in DbContext.CommentReactions + where commentIds.Contains(cr.CommentId) + where cr.UserId == roadieUser.Id + select cr).ToArray(); + foreach (var comment in result.Data.Comments) + { + var userCommentReaction = + userCommentReactions.FirstOrDefault(x => x.CommentId == comment.DatabaseId); + comment.IsDisliked = userCommentReaction?.ReactionValue == CommentReaction.Dislike; + comment.IsLiked = userCommentReaction?.ReactionValue == CommentReaction.Like; + } + } + } + + return new OperationResult(result.Messages) + { + Data = result?.Data, + IsNotFoundResult = result?.IsNotFoundResult ?? false, + Errors = result?.Errors, + IsSuccess = result?.IsSuccess ?? false, + OperationTime = sw.ElapsedMilliseconds + }; + } + + public async Task> DeleteCollectionAsync(User user, Guid id) + { + var sw = new Stopwatch(); + sw.Start(); + var errors = new List(); + var collection = DbContext.Collections.FirstOrDefault(x => x.RoadieId == id); + if (collection == null) + { + return new OperationResult(true, $"Collection Not Found [{id}]"); + } + + if (!user.IsEditor) + { + Logger.LogWarning($"DeleteCollection: Access Denied: `{collection}`, By User `{user}`"); + var r = new OperationResult("Access Denied"); + r.IsAccessDeniedResult = true; + return r; + } + + try + { + DbContext.Collections.Remove(collection); + await DbContext.SaveChangesAsync().ConfigureAwait(false); + await BookmarkService.RemoveAllBookmarksForItemAsync(BookmarkType.Collection, collection.Id).ConfigureAwait(false); + var collectionImageFilename = collection.PathToImage(Configuration); + if (File.Exists(collectionImageFilename)) + { + File.Delete(collectionImageFilename); + } + } + catch (Exception ex) + { + Logger.LogError(ex); + errors.Add(ex); + } + + sw.Stop(); + Logger.LogWarning($"DeleteCollection `{collection}`, By User `{user}`"); + return new OperationResult + { + IsSuccess = !errors.Any(), + Data = true, + OperationTime = sw.ElapsedMilliseconds, + Errors = errors + }; + } + + public Task> ListAsync(User roadieUser, PagedRequest request, + bool? doRandomize = false, Guid? releaseId = null, Guid? artistId = null) + { + var sw = new Stopwatch(); + sw.Start(); + IQueryable collections = null; + if (artistId.HasValue) + { + collections = (from cr in DbContext.CollectionReleases + join c in DbContext.Collections on cr.CollectionId equals c.Id + join r in DbContext.Releases on cr.ReleaseId equals r.Id + join a in DbContext.Artists on r.ArtistId equals a.Id + where a.RoadieId == artistId + select c).Distinct(); + } + else if (releaseId.HasValue) + { + collections = (from cr in DbContext.CollectionReleases + join c in DbContext.Collections on cr.CollectionId equals c.Id + join r in DbContext.Releases on cr.ReleaseId equals r.Id + where r.RoadieId == releaseId + select c).Distinct(); + } + else + { + collections = DbContext.Collections; + } + + var normalizedFilterValue = !string.IsNullOrEmpty(request.FilterValue) + ? request.FilterValue.ToAlphanumericName() + : null; + + var result = from c in collections + let collectionCount = (from crc in DbContext.CollectionReleases + where crc.CollectionId == c.Id + select crc.Id).Count() + where string.IsNullOrEmpty(normalizedFilterValue) || ( + c.Name.ToLower().Contains(normalizedFilterValue) || + c.SortName.ToLower().Contains(normalizedFilterValue) || + c.AlternateNames.ToLower().Contains(normalizedFilterValue) + ) + where request.FilterToStatusValue == Statuses.Ok || c.Status == request.FilterToStatusValue + select new CollectionList + { + DatabaseId = c.Id, + Collection = new DataToken + { + Text = c.Name, + Value = c.RoadieId.ToString() + }, + Id = c.RoadieId, + CollectionCount = c.CollectionCount, + CollectionType = (c.CollectionType ?? CollectionType.Unknown).ToString(), + CollectionFoundCount = collectionCount, + CreatedDate = c.CreatedDate, + IsLocked = c.IsLocked, + LastUpdated = c.LastUpdated, + Thumbnail = ImageHelper.MakeCollectionThumbnailImage(Configuration, HttpContext, c.RoadieId) + }; + + var sortBy = string.IsNullOrEmpty(request.Sort) + ? request.OrderValue(new Dictionary { { "Collection.Text", "ASC" } }) + : request.OrderValue(); + 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 + { + TotalCount = rowCount, + CurrentPage = request.PageValue, + TotalPages = (int)Math.Ceiling((double)rowCount / request.LimitValue), + OperationTime = sw.ElapsedMilliseconds, + Rows = rows + }); + } + + /// + /// Updates (or Adds) Collection + /// + public async Task> UpdateCollectionAsync(User user, Collection model) + { + var isNew = model.Id == Guid.Empty; + var now = DateTime.UtcNow; + + var sw = new Stopwatch(); + sw.Start(); + var errors = new List(); + + var collection = new data.Collection(); + + if (!isNew) + { + collection = DbContext.Collections.FirstOrDefault(x => x.RoadieId == model.Id); + if (collection == null) + { + return new OperationResult(true, $"Collection Not Found [{model.Id}]"); + } + // If collection is being renamed, see if collection already exists with new model supplied name + var collectionName = collection.SortNameValue; + var collectionModelName = model.SortNameValue; + if (collectionName.ToAlphanumericName() != collectionModelName.ToAlphanumericName()) + { + var existingCollection = DbContext.Collections.FirstOrDefault(x => x.Name == model.Name || x.SortName == model.SortName); + if (existingCollection != null) + { + return new OperationResult($"Collection already exists `{ collection }` with name [{ collectionModelName }]."); + } + } + } + collection.IsLocked = model.IsLocked; + var oldPathToImage = collection.PathToImage(Configuration); + var didChangeName = collection.Name != model.Name && !isNew; + collection.Name = model.Name; + collection.SortName = model.SortName; + collection.Edition = model.Edition; + collection.ListInCSVFormat = model.ListInCSVFormat; + collection.ListInCSV = model.ListInCSV; + collection.Description = model.Description; + collection.URLs = model.URLs; + collection.Status = SafeParser.ToEnum(model.Status); + collection.CollectionType = SafeParser.ToEnum(model.CollectionType); + collection.Tags = model.TagsList.ToDelimitedList(); + collection.URLs = model.URLsList.ToDelimitedList(); + collection.AlternateNames = model.AlternateNamesList.ToDelimitedList(); + collection.CollectionCount = model.CollectionCount; + + if (model.Maintainer?.Value != null) + { + var maintainer = await DbContext.Users.FirstOrDefaultAsync(x => x.RoadieId == SafeParser.ToGuid(model.Maintainer.Value)).ConfigureAwait(false); + if (maintainer != null) + { + collection.MaintainerId = maintainer.Id; + } + } + + if (isNew) + { + await DbContext.Collections.AddAsync(collection).ConfigureAwait(false); + await DbContext.SaveChangesAsync().ConfigureAwait(false); + } + + if (didChangeName) + { + if (File.Exists(oldPathToImage)) + { + File.Move(oldPathToImage, collection.PathToImage(Configuration)); + } + } + + var collectionImage = ImageHelper.ImageDataFromUrl(model.NewThumbnailData); + if (collectionImage != null) + { + // Save unaltered collection image + File.WriteAllBytes(collection.PathToImage(Configuration, true), ImageHelper.ConvertToJpegFormat(collectionImage)); + } + + collection.LastUpdated = now; + await DbContext.SaveChangesAsync().ConfigureAwait(false); + + CacheManager.ClearRegion(collection.CacheRegion); + Logger.LogInformation($"UpdateCollection `{collection}` By User `{user}`"); + return new OperationResult + { + IsSuccess = !errors.Any(), + Data = !errors.Any(), + OperationTime = sw.ElapsedMilliseconds, + Errors = errors + }; + } } } \ No newline at end of file diff --git a/Roadie.Api.Services/CommentService.cs b/Roadie.Api.Services/CommentService.cs index 63fa7be..86afc20 100644 --- a/Roadie.Api.Services/CommentService.cs +++ b/Roadie.Api.Services/CommentService.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.Logging; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; using Roadie.Library; using Roadie.Library.Caching; using Roadie.Library.Configuration; @@ -28,15 +29,85 @@ namespace Roadie.Api.Services { } - public async Task> AddNewArtistComment(User user, Guid artistId, string cmt) + private void ClearCaches(data.Comment comment) + { + switch (comment.CommentType) + { + case CommentType.Artist: + var artist = DbContext.Artists.FirstOrDefault(x => x.Id == comment.ArtistId); + if (artist != null) + { + CacheManager.ClearRegion(artist.CacheRegion); + } + + break; + + case CommentType.Collection: + var collection = DbContext.Collections.FirstOrDefault(x => x.Id == comment.CollectionId); + if (collection != null) + { + CacheManager.ClearRegion(collection.CacheRegion); + } + + break; + + case CommentType.Genre: + var genre = DbContext.Genres.FirstOrDefault(x => x.Id == comment.GenreId); + if (genre != null) + { + CacheManager.ClearRegion(genre.CacheRegion); + } + + break; + + case CommentType.Label: + var label = DbContext.Labels.FirstOrDefault(x => x.Id == comment.LabelId); + if (label != null) + { + CacheManager.ClearRegion(label.CacheRegion); + } + + break; + + case CommentType.Playlist: + var playlist = DbContext.Playlists.FirstOrDefault(x => x.Id == comment.PlaylistId); + if (playlist != null) + { + CacheManager.ClearRegion(playlist.CacheRegion); + } + + break; + + case CommentType.Release: + var release = DbContext.Releases.FirstOrDefault(x => x.Id == comment.ReleaseId); + if (release != null) + { + CacheManager.ClearRegion(release.CacheRegion); + } + + break; + + case CommentType.Track: + var track = DbContext.Tracks.FirstOrDefault(x => x.Id == comment.TrackId); + if (track != null) + { + CacheManager.ClearRegion(track.CacheRegion); + } + + break; + } + } + + public async Task> AddNewArtistCommentAsync(User user, Guid artistId, string cmt) { var sw = Stopwatch.StartNew(); var result = false; var errors = new List(); - var artist = DbContext.Artists - .FirstOrDefault(x => x.RoadieId == artistId); + var artist = await DbContext.Artists.FirstOrDefaultAsync(x => x.RoadieId == artistId).ConfigureAwait(false); if (artist == null) - return new OperationResult(true, string.Format("Artist Not Found [{0}]", artistId)); + { + return new OperationResult(true, $"Artist Not Found [{artistId}]"); + } var newComment = new data.Comment { @@ -44,8 +115,8 @@ namespace Roadie.Api.Services UserId = user.Id.Value, Cmt = cmt }; - DbContext.Comments.Add(newComment); - await DbContext.SaveChangesAsync(); + await DbContext.Comments.AddAsync(newComment).ConfigureAwait(false); + await DbContext.SaveChangesAsync().ConfigureAwait(false); CacheManager.ClearRegion(artist.CacheRegion); sw.Stop(); result = true; @@ -58,15 +129,16 @@ namespace Roadie.Api.Services }; } - public async Task> AddNewCollectionComment(User user, Guid collectionId, string cmt) + public async Task> AddNewCollectionCommentAsync(User user, Guid collectionId, string cmt) { var sw = Stopwatch.StartNew(); var result = false; var errors = new List(); - var collection = DbContext.Collections - .FirstOrDefault(x => x.RoadieId == collectionId); + var collection = await DbContext.Collections.FirstOrDefaultAsync(x => x.RoadieId == collectionId).ConfigureAwait(false); if (collection == null) - return new OperationResult(true, string.Format("Collection Not Found [{0}]", collectionId)); + { + return new OperationResult(true, $"Collection Not Found [{collectionId}]"); + } var newComment = new data.Comment { @@ -74,8 +146,8 @@ namespace Roadie.Api.Services UserId = user.Id.Value, Cmt = cmt }; - DbContext.Comments.Add(newComment); - await DbContext.SaveChangesAsync(); + await DbContext.Comments.AddAsync(newComment).ConfigureAwait(false); + await DbContext.SaveChangesAsync().ConfigureAwait(false); CacheManager.ClearRegion(collection.CacheRegion); sw.Stop(); result = true; @@ -88,14 +160,16 @@ namespace Roadie.Api.Services }; } - public async Task> AddNewGenreComment(User user, Guid genreId, string cmt) + public async Task> AddNewGenreCommentAsync(User user, Guid genreId, string cmt) { var sw = Stopwatch.StartNew(); var result = false; var errors = new List(); - var genre = DbContext.Genres - .FirstOrDefault(x => x.RoadieId == genreId); - if (genre == null) return new OperationResult(true, string.Format("Genre Not Found [{0}]", genreId)); + var genre = await DbContext.Genres.FirstOrDefaultAsync(x => x.RoadieId == genreId).ConfigureAwait(false); + if (genre == null) + { + return new OperationResult(true, $"Genre Not Found [{genreId}]"); + } var newComment = new data.Comment { @@ -103,8 +177,8 @@ namespace Roadie.Api.Services UserId = user.Id.Value, Cmt = cmt }; - DbContext.Comments.Add(newComment); - await DbContext.SaveChangesAsync(); + await DbContext.Comments.AddAsync(newComment).ConfigureAwait(false); + await DbContext.SaveChangesAsync().ConfigureAwait(false); CacheManager.ClearRegion(genre.CacheRegion); sw.Stop(); result = true; @@ -117,14 +191,16 @@ namespace Roadie.Api.Services }; } - public async Task> AddNewLabelComment(User user, Guid labelId, string cmt) + public async Task> AddNewLabelCommentAsync(User user, Guid labelId, string cmt) { var sw = Stopwatch.StartNew(); var result = false; var errors = new List(); - var label = DbContext.Labels - .FirstOrDefault(x => x.RoadieId == labelId); - if (label == null) return new OperationResult(true, string.Format("Label Not Found [{0}]", labelId)); + var label = await DbContext.Labels.FirstOrDefaultAsync(x => x.RoadieId == labelId).ConfigureAwait(false); + if (label == null) + { + return new OperationResult(true, $"Label Not Found [{labelId}]"); + } var newComment = new data.Comment { @@ -132,8 +208,8 @@ namespace Roadie.Api.Services UserId = user.Id.Value, Cmt = cmt }; - DbContext.Comments.Add(newComment); - await DbContext.SaveChangesAsync(); + await DbContext.Comments.AddAsync(newComment).ConfigureAwait(false); + await DbContext.SaveChangesAsync().ConfigureAwait(false); CacheManager.ClearRegion(label.CacheRegion); sw.Stop(); result = true; @@ -146,15 +222,16 @@ namespace Roadie.Api.Services }; } - public async Task> AddNewPlaylistComment(User user, Guid playlistId, string cmt) + public async Task> AddNewPlaylistCommentAsync(User user, Guid playlistId, string cmt) { var sw = Stopwatch.StartNew(); var result = false; var errors = new List(); - var playlist = DbContext.Playlists - .FirstOrDefault(x => x.RoadieId == playlistId); + var playlist = await DbContext.Playlists.FirstOrDefaultAsync(x => x.RoadieId == playlistId).ConfigureAwait(false); if (playlist == null) - return new OperationResult(true, string.Format("Playlist Not Found [{0}]", playlistId)); + { + return new OperationResult(true, $"Playlist Not Found [{playlistId}]"); + } var newComment = new data.Comment { @@ -162,8 +239,8 @@ namespace Roadie.Api.Services UserId = user.Id.Value, Cmt = cmt }; - DbContext.Comments.Add(newComment); - await DbContext.SaveChangesAsync(); + await DbContext.Comments.AddAsync(newComment).ConfigureAwait(false); + await DbContext.SaveChangesAsync().ConfigureAwait(false); CacheManager.ClearRegion(playlist.CacheRegion); sw.Stop(); result = true; @@ -176,15 +253,16 @@ namespace Roadie.Api.Services }; } - public async Task> AddNewReleaseComment(User user, Guid releaseId, string cmt) + public async Task> AddNewReleaseCommentAsync(User user, Guid releaseId, string cmt) { var sw = Stopwatch.StartNew(); var result = false; var errors = new List(); - var release = DbContext.Releases - .FirstOrDefault(x => x.RoadieId == releaseId); + var release = await DbContext.Releases.FirstOrDefaultAsync(x => x.RoadieId == releaseId).ConfigureAwait(false); if (release == null) - return new OperationResult(true, string.Format("Release Not Found [{0}]", releaseId)); + { + return new OperationResult(true, $"Release Not Found [{releaseId}]"); + } var newComment = new data.Comment { @@ -192,8 +270,8 @@ namespace Roadie.Api.Services UserId = user.Id.Value, Cmt = cmt }; - DbContext.Comments.Add(newComment); - await DbContext.SaveChangesAsync(); + await DbContext.Comments.AddAsync(newComment).ConfigureAwait(false); + await DbContext.SaveChangesAsync().ConfigureAwait(false); CacheManager.ClearRegion(release.CacheRegion); sw.Stop(); result = true; @@ -206,14 +284,16 @@ namespace Roadie.Api.Services }; } - public async Task> AddNewTrackComment(User user, Guid trackId, string cmt) + public async Task> AddNewTrackCommentAsync(User user, Guid trackId, string cmt) { var sw = Stopwatch.StartNew(); var result = false; var errors = new List(); - var track = DbContext.Tracks - .FirstOrDefault(x => x.RoadieId == trackId); - if (track == null) return new OperationResult(true, string.Format("Track Not Found [{0}]", trackId)); + var track = await DbContext.Tracks.FirstOrDefaultAsync(x => x.RoadieId == trackId).ConfigureAwait(false); + if (track == null) + { + return new OperationResult(true, $"Track Not Found [{trackId}]"); + } var newComment = new data.Comment { @@ -221,8 +301,8 @@ namespace Roadie.Api.Services UserId = user.Id.Value, Cmt = cmt }; - DbContext.Comments.Add(newComment); - await DbContext.SaveChangesAsync(); + await DbContext.Comments.AddAsync(newComment).ConfigureAwait(false); + await DbContext.SaveChangesAsync().ConfigureAwait(false); CacheManager.ClearRegion(track.CacheRegion); sw.Stop(); result = true; @@ -235,15 +315,19 @@ namespace Roadie.Api.Services }; } - public async Task> DeleteComment(User user, Guid id) + public async Task> DeleteCommentAsync(User user, Guid id) { var sw = Stopwatch.StartNew(); var result = false; var errors = new List(); - var comment = DbContext.Comments.FirstOrDefault(x => x.RoadieId == id); - if (comment == null) return new OperationResult(true, string.Format("Comment Not Found [{0}]", id)); + var comment = await DbContext.Comments.FirstOrDefaultAsync(x => x.RoadieId == id).ConfigureAwait(false); + if (comment == null) + { + return new OperationResult(true, $"Comment Not Found [{id}]"); + } + DbContext.Remove(comment); - await DbContext.SaveChangesAsync(); + await DbContext.SaveChangesAsync().ConfigureAwait(false); ClearCaches(comment); sw.Stop(); result = true; @@ -256,15 +340,18 @@ namespace Roadie.Api.Services }; } - public async Task> SetCommentReaction(User user, Guid id, CommentReaction reaction) + public async Task> SetCommentReactionAsync(User user, Guid id, CommentReaction reaction) { var sw = Stopwatch.StartNew(); var result = false; var errors = new List(); - var comment = DbContext.Comments.FirstOrDefault(x => x.RoadieId == id); - if (comment == null) return new OperationResult(true, string.Format("Comment Not Found [{0}]", id)); - var userCommentReaction = - DbContext.CommentReactions.FirstOrDefault(x => x.CommentId == comment.Id && x.UserId == user.Id); + var comment = await DbContext.Comments.FirstOrDefaultAsync(x => x.RoadieId == id).ConfigureAwait(false); + if (comment == null) + { + return new OperationResult(true, $"Comment Not Found [{id}]"); + } + + var userCommentReaction = await DbContext.CommentReactions.FirstOrDefaultAsync(x => x.CommentId == comment.Id && x.UserId == user.Id).ConfigureAwait(false); if (userCommentReaction == null) { userCommentReaction = new data.CommentReaction @@ -272,20 +359,20 @@ namespace Roadie.Api.Services CommentId = comment.Id, UserId = user.Id.Value }; - DbContext.CommentReactions.Add(userCommentReaction); + await DbContext.CommentReactions.AddAsync(userCommentReaction).ConfigureAwait(false); } userCommentReaction.Reaction = reaction == CommentReaction.Unknown ? null : reaction.ToString(); - await DbContext.SaveChangesAsync(); + await DbContext.SaveChangesAsync().ConfigureAwait(false); ClearCaches(comment); - var userCommentReactions = (from cr in DbContext.CommentReactions + var userCommentReactions = await (from cr in DbContext.CommentReactions where cr.CommentId == comment.Id - select cr).ToArray(); + select cr) + .ToArrayAsync() + .ConfigureAwait(false); var additionalData = new Dictionary(); - additionalData.Add("likedCount", - userCommentReactions.Where(x => x.ReactionValue == CommentReaction.Like).Count()); - additionalData.Add("dislikedCount", - userCommentReactions.Where(x => x.ReactionValue == CommentReaction.Dislike).Count()); + additionalData.Add("likedCount", userCommentReactions.Where(x => x.ReactionValue == CommentReaction.Like).Count()); + additionalData.Add("dislikedCount", userCommentReactions.Where(x => x.ReactionValue == CommentReaction.Dislike).Count()); sw.Stop(); result = true; return new OperationResult @@ -297,46 +384,5 @@ namespace Roadie.Api.Services OperationTime = sw.ElapsedMilliseconds }; } - - private void ClearCaches(data.Comment comment) - { - switch (comment.CommentType) - { - case CommentType.Artist: - var artist = DbContext.Artists.FirstOrDefault(x => x.Id == comment.ArtistId); - if (artist != null) CacheManager.ClearRegion(artist.CacheRegion); - break; - - case CommentType.Collection: - var collection = DbContext.Collections.FirstOrDefault(x => x.Id == comment.CollectionId); - if (collection != null) CacheManager.ClearRegion(collection.CacheRegion); - break; - - case CommentType.Genre: - var genre = DbContext.Genres.FirstOrDefault(x => x.Id == comment.GenreId); - if (genre != null) CacheManager.ClearRegion(genre.CacheRegion); - break; - - case CommentType.Label: - var label = DbContext.Labels.FirstOrDefault(x => x.Id == comment.LabelId); - if (label != null) CacheManager.ClearRegion(label.CacheRegion); - break; - - case CommentType.Playlist: - var playlist = DbContext.Playlists.FirstOrDefault(x => x.Id == comment.PlaylistId); - if (playlist != null) CacheManager.ClearRegion(playlist.CacheRegion); - break; - - case CommentType.Release: - var release = DbContext.Releases.FirstOrDefault(x => x.Id == comment.ReleaseId); - if (release != null) CacheManager.ClearRegion(release.CacheRegion); - break; - - case CommentType.Track: - var track = DbContext.Tracks.FirstOrDefault(x => x.Id == comment.TrackId); - if (track != null) CacheManager.ClearRegion(track.CacheRegion); - break; - } - } } } \ No newline at end of file diff --git a/Roadie.Api.Services/EmailSenderService.cs b/Roadie.Api.Services/EmailSenderService.cs index 281abae..bbe994b 100644 --- a/Roadie.Api.Services/EmailSenderService.cs +++ b/Roadie.Api.Services/EmailSenderService.cs @@ -18,7 +18,7 @@ namespace Roadie.Api.Services public async Task SendEmailAsync(string email, string subject, string htmlMessage) { - if(string.IsNullOrEmpty(Configuration.SmtpHost)) + if (string.IsNullOrEmpty(Configuration.SmtpHost)) { Trace.WriteLine("Email Server (Configuration.SmtpHost) Not Configured", "Warning"); return; @@ -37,7 +37,7 @@ namespace Roadie.Api.Services mail.IsBodyHtml = true; mail.Body = htmlMessage; ServicePointManager.ServerCertificateValidationCallback = delegate { return true; }; - await client.SendMailAsync(mail); + await client.SendMailAsync(mail).ConfigureAwait(false); } } } diff --git a/Roadie.Api.Services/FileDirectoryProcessorService.cs b/Roadie.Api.Services/FileDirectoryProcessorService.cs index 39829d6..651b450 100644 --- a/Roadie.Api.Services/FileDirectoryProcessorService.cs +++ b/Roadie.Api.Services/FileDirectoryProcessorService.cs @@ -18,12 +18,20 @@ using System.IO; using System.Linq; using System.Linq.Dynamic.Core; using System.Threading.Tasks; -using data = Roadie.Library.Data; namespace Roadie.Api.Services { public class FileDirectoryProcessorService : ServiceBase, IFileDirectoryProcessorService { + + private IArtistLookupEngine ArtistLookupEngine { get; } + + private IAudioMetaDataHelper AudioMetaDataHelper { get; } + + private IFileProcessor FileProcessor { get; } + private IReleaseLookupEngine ReleaseLookupEngine { get; } + private IReleaseService ReleaseService { get; } + private List _addedArtistIds = new List(); private List _addedReleaseIds = new List(); private List _addedTrackIds = new List(); @@ -34,14 +42,6 @@ namespace Roadie.Api.Services public int? ProcessLimit { get; set; } - private IArtistLookupEngine ArtistLookupEngine { get; } - - private IAudioMetaDataHelper AudioMetaDataHelper { get; } - - private IFileProcessor FileProcessor { get; } - private IReleaseLookupEngine ReleaseLookupEngine { get; } - private IReleaseService ReleaseService { get; } - public FileDirectoryProcessorService(IRoadieSettings configuration, IHttpEncoder httpEncoder, IHttpContext httpContext, @@ -62,73 +62,17 @@ namespace Roadie.Api.Services FileProcessor = fileProcessor; } - public static OperationResult DeleteEmptyFolders(DirectoryInfo processingFolder, ILogger logger) - { - var result = new OperationResult(); - try - { - result.IsSuccess = FolderPathHelper.DeleteEmptyFolders(processingFolder); - } - catch (Exception ex) - { - logger.LogError(ex, string.Format("Error Deleting Empty Folder [{0}] Error [{1}]", processingFolder.FullName, ex.Serialize())); - } - return result; - } - - public async Task> Process(User user, DirectoryInfo folder, bool doJustInfo, int? submissionId = null, bool doDeleteFiles = true) - { - var sw = new Stopwatch(); - sw.Start(); - await PreProcessFolder(folder, doJustInfo); - var processedFiles = 0; - var pluginResultInfos = new List(); - var errors = new List(); - - _addedArtistIds.Clear(); - _addedReleaseIds.Clear(); - _addedTrackIds.Clear(); - - FileProcessor.SubmissionId = submissionId; - - foreach (var file in Directory.EnumerateFiles(folder.FullName, "*.*", SearchOption.AllDirectories) - .ToArray()) - { - var operation = await FileProcessor.Process(file, doJustInfo); - if (operation != null && operation.AdditionalData != null && - operation.AdditionalData.ContainsKey(PluginResultInfo.AdditionalDataKeyPluginResultInfo)) - { - pluginResultInfos.Add(operation.AdditionalData[PluginResultInfo.AdditionalDataKeyPluginResultInfo] as PluginResultInfo); - processedFiles++; - } - if (ProcessLimit.HasValue && processedFiles > ProcessLimit.Value) break; - } - - await PostProcessFolder(user, folder, pluginResultInfos, doJustInfo, doDeleteFiles); - sw.Stop(); - _addedArtistIds.AddRange(ArtistLookupEngine.AddedArtistIds); - _addedReleaseIds.AddRange(ReleaseLookupEngine.AddedReleaseIds); - _addedTrackIds.AddRange(ReleaseLookupEngine.AddedTrackIds); - Logger.LogInformation("Completed! Processed Folder [{0}]: Processed Files [{1}] : Elapsed Time [{2}]", folder.FullName, processedFiles, sw.Elapsed); - return new OperationResult - { - IsSuccess = !errors.Any(), - AdditionalData = new Dictionary { { "ProcessedFiles", processedFiles } }, - OperationTime = sw.ElapsedMilliseconds - }; - } - /// /// Perform any operations to the given folder and the plugin results after processing /// - private async Task PostProcessFolder(User user, DirectoryInfo inboundFolder, IEnumerable pluginResults, bool doJustInfo, bool doDeleteFiles = true) + private async Task PostProcessFolderAsync(User user, DirectoryInfo inboundFolder, IEnumerable pluginResults, bool doJustInfo, bool doDeleteFiles = true) { SimpleContract.Requires(inboundFolder != null, "Invalid InboundFolder"); if (pluginResults != null) { foreach (var releasesInfo in pluginResults.GroupBy(x => x.ReleaseId).Select(x => x.First())) { - await ReleaseService.ScanReleaseFolder(user, releasesInfo.ReleaseId, doJustInfo); + await ReleaseService.ScanReleaseFolderAsync(user, releasesInfo.ReleaseId, doJustInfo).ConfigureAwait(false); _addedTrackIds.AddRange(ReleaseService.AddedTrackIds); } } @@ -136,6 +80,7 @@ namespace Roadie.Api.Services { var fileExtensionsToDelete = Configuration.FileExtensionsToDelete ?? new string[0]; if (fileExtensionsToDelete.Any()) + { foreach (var fileInFolder in inboundFolder.GetFiles("*.*", SearchOption.AllDirectories)) { if (fileExtensionsToDelete.Any(x => x.Equals(fileInFolder.Extension, StringComparison.OrdinalIgnoreCase))) @@ -148,6 +93,7 @@ namespace Roadie.Api.Services } } } + } DeleteEmptyFolders(inboundFolder, Logger); } @@ -158,9 +104,67 @@ namespace Roadie.Api.Services /// /// Perform any operations to the given folder before processing /// - private Task PreProcessFolder(DirectoryInfo inboundFolder, bool doJustInfo = false) + private Task PreProcessFolderAsync(DirectoryInfo inboundFolder, bool doJustInfo = false) { return Task.FromResult(true); } + + public static OperationResult DeleteEmptyFolders(DirectoryInfo processingFolder, ILogger logger) + { + var result = new OperationResult(); + try + { + result.IsSuccess = FolderPathHelper.DeleteEmptyFolders(processingFolder); + } + catch (Exception ex) + { + logger.LogError(ex, $"Error Deleting Empty Folder [{processingFolder.FullName}] Error [{ex.Serialize()}]"); + } + return result; + } + + public async Task> ProcessAsync(User user, DirectoryInfo folder, bool doJustInfo, int? submissionId = null, bool doDeleteFiles = true) + { + var sw = new Stopwatch(); + sw.Start(); + await PreProcessFolderAsync(folder, doJustInfo).ConfigureAwait(false); + var processedFiles = 0; + var pluginResultInfos = new List(); + var errors = new List(); + + _addedArtistIds.Clear(); + _addedReleaseIds.Clear(); + _addedTrackIds.Clear(); + + FileProcessor.SubmissionId = submissionId; + + foreach (var file in Directory.EnumerateFiles(folder.FullName, "*.*", SearchOption.AllDirectories) .ToArray()) + { + var operation = await FileProcessor.Process(file, doJustInfo).ConfigureAwait(false); + if (operation != null && operation.AdditionalData != null && + operation.AdditionalData.ContainsKey(PluginResultInfo.AdditionalDataKeyPluginResultInfo)) + { + pluginResultInfos.Add(operation.AdditionalData[PluginResultInfo.AdditionalDataKeyPluginResultInfo] as PluginResultInfo); + processedFiles++; + } + if (ProcessLimit.HasValue && processedFiles > ProcessLimit.Value) + { + break; + } + } + + await PostProcessFolderAsync(user, folder, pluginResultInfos, doJustInfo, doDeleteFiles).ConfigureAwait(false); + sw.Stop(); + _addedArtistIds.AddRange(ArtistLookupEngine.AddedArtistIds); + _addedReleaseIds.AddRange(ReleaseLookupEngine.AddedReleaseIds); + _addedTrackIds.AddRange(ReleaseLookupEngine.AddedTrackIds); + Logger.LogInformation("Completed! Processed Folder [{0}]: Processed Files [{1}] : Elapsed Time [{2}]", folder.FullName, processedFiles, sw.Elapsed); + return new OperationResult + { + IsSuccess = !errors.Any(), + AdditionalData = new Dictionary { { "ProcessedFiles", processedFiles } }, + OperationTime = sw.ElapsedMilliseconds + }; + } } } \ No newline at end of file diff --git a/Roadie.Api.Services/GenreService.cs b/Roadie.Api.Services/GenreService.cs index 732e3b5..7077d94 100644 --- a/Roadie.Api.Services/GenreService.cs +++ b/Roadie.Api.Services/GenreService.cs @@ -9,11 +9,9 @@ using Roadie.Library.Data.Context; using Roadie.Library.Encoding; using Roadie.Library.Enums; using Roadie.Library.Extensions; -using Roadie.Library.Identity; using Roadie.Library.Imaging; using Roadie.Library.Models; using Roadie.Library.Models.Pagination; -using Roadie.Library.Models.Users; using Roadie.Library.Utility; using System; using System.Collections.Generic; @@ -38,15 +36,109 @@ namespace Roadie.Api.Services { } - public async Task> ById(Library.Models.Users.User roadieUser, Guid id, IEnumerable includes = null) + private Task> GenreByIdAction(Guid id, IEnumerable includes = null) + { + var timings = new Dictionary(); + var tsw = new Stopwatch(); + + var sw = Stopwatch.StartNew(); + sw.Start(); + + var genre = DbContext.Genres.FirstOrDefault(x => x.RoadieId == id); + if (genre == null) + { + return Task.FromResult(new OperationResult(true, $"Genre Not Found [{id}]")); + } + tsw.Stop(); + timings.Add("getGenre", tsw.ElapsedMilliseconds); + + tsw.Restart(); + var result = genre.Adapt(); + result.AlternateNames = genre.AlternateNames; + result.Tags = genre.Tags; + result.Thumbnail = ImageHelper.MakeGenreThumbnailImage(Configuration, HttpContext, genre.RoadieId); + result.MediumThumbnail = ImageHelper.MakeThumbnailImage(Configuration, HttpContext, id, "genre", Configuration.MediumImageSize.Width, Configuration.MediumImageSize.Height); + tsw.Stop(); + timings.Add("adapt", tsw.ElapsedMilliseconds); + if (includes != null && includes.Any()) + { + if (includes.Contains("stats")) + { + tsw.Restart(); + var releaseCount = (from rg in DbContext.ReleaseGenres + where rg.GenreId == genre.Id + select rg.Id).Count(); + var artistCount = (from rg in DbContext.ArtistGenres + where rg.GenreId == genre.Id + select rg.Id).Count(); + result.Statistics = new Library.Models.Statistics.ReleaseGroupingStatistics + { + ArtistCount = artistCount, + ReleaseCount = releaseCount + }; + tsw.Stop(); + timings.Add("stats", tsw.ElapsedMilliseconds); + } + } + sw.Stop(); + Logger.LogInformation($"ByIdAction: Genre `{ genre }`: includes [{includes.ToCSV()}], timings: [{ timings.ToTimings() }]"); + return Task.FromResult(new OperationResult + { + Data = result, + IsSuccess = result != null, + OperationTime = sw.ElapsedMilliseconds + }); + } + + private async Task> SaveImageBytes(Library.Models.Users.User user, Guid id, byte[] imageBytes) + { + var sw = new Stopwatch(); + sw.Start(); + var errors = new List(); + var genre = await DbContext.Genres.FirstOrDefaultAsync(x => x.RoadieId == id).ConfigureAwait(false); + if (genre == null) + { + return new OperationResult(true, $"Genre Not Found [{id}]"); + } + try + { + var now = DateTime.UtcNow; + if (imageBytes != null) + { + // Save unaltered genre image + File.WriteAllBytes(genre.PathToImage(Configuration, true), ImageHelper.ConvertToJpegFormat(imageBytes)); + } + genre.LastUpdated = now; + await DbContext.SaveChangesAsync().ConfigureAwait(false); + CacheManager.ClearRegion(genre.CacheRegion); + Logger.LogInformation($"UploadGenreImage `{genre}` By User `{user}`"); + } + catch (Exception ex) + { + Logger.LogError(ex); + errors.Add(ex); + } + + sw.Stop(); + + return new OperationResult + { + IsSuccess = !errors.Any(), + Data = ImageHelper.MakeThumbnailImage(Configuration, HttpContext, id, "genre", Configuration.MediumImageSize.Width, Configuration.MediumImageSize.Height, true), + OperationTime = sw.ElapsedMilliseconds, + Errors = errors + }; + } + + public async Task> ByIdAsync(Library.Models.Users.User roadieUser, Guid id, IEnumerable includes = null) { var sw = Stopwatch.StartNew(); sw.Start(); - var cacheKey = string.Format("urn:genre_by_id_operation:{0}:{1}", id, includes == null ? "0" : string.Join("|", includes)); + var cacheKey = $"urn:genre_by_id_operation:{id}:{(includes == null ? "0" : string.Join("|", includes))}"; var result = await CacheManager.GetAsync(cacheKey, async () => { - return await GenreByIdAction(id, includes); - }, data.Genre.CacheRegionUrn(id)); + return await GenreByIdAction(id, includes).ConfigureAwait(false); + }, data.Genre.CacheRegionUrn(id)).ConfigureAwait(false); sw.Stop(); return new OperationResult(result.Messages) { @@ -58,14 +150,18 @@ namespace Roadie.Api.Services }; } - public async Task> Delete(Library.Identity.User user, Guid id) + public async Task> DeleteAsync(Library.Identity.User user, Guid id) { var sw = new Stopwatch(); sw.Start(); - var genre = DbContext.Genres.FirstOrDefault(x => x.RoadieId == id); - if (genre == null) return new OperationResult(true, string.Format("Genre Not Found [{0}]", id)); + var genre = await DbContext.Genres.FirstOrDefaultAsync(x => x.RoadieId == id).ConfigureAwait(false); + if (genre == null) + { + return new OperationResult(true, $"Genre Not Found [{id}]"); + } + DbContext.Genres.Remove(genre); - await DbContext.SaveChangesAsync(); + await DbContext.SaveChangesAsync().ConfigureAwait(false); var genreImageFilename = genre.PathToImage(Configuration); if (File.Exists(genreImageFilename)) @@ -84,7 +180,7 @@ namespace Roadie.Api.Services }; } - public async Task> List(Library.Models.Users.User roadieUser, PagedRequest request, bool? doRandomize = false) + public async Task> ListAsync(Library.Models.Users.User roadieUser, PagedRequest request, bool? doRandomize = false) { var sw = new Stopwatch(); sw.Start(); @@ -102,7 +198,7 @@ namespace Roadie.Api.Services if (doRandomize ?? false) { var randomLimit = request.Limit ?? roadieUser?.RandomReleaseLimit ?? request.LimitValue; - randomGenreData = await DbContext.RandomGenreIds(roadieUser?.Id ?? -1, randomLimit, request.FilterFavoriteOnly, request.FilterRatedOnly); + randomGenreData = await DbContext.RandomGenreIdsAsync(roadieUser?.Id ?? -1, randomLimit, request.FilterFavoriteOnly, request.FilterRatedOnly).ConfigureAwait(false); randomGenreIds = randomGenreData.Select(x => x.Value).ToArray(); rowCount = DbContext.Genres.Count(); } @@ -169,20 +265,17 @@ namespace Roadie.Api.Services }); } - public async Task> SetGenreImageByUrl(Library.Models.Users.User user, Guid id, string imageUrl) - { - return await SaveImageBytes(user, id, WebHelper.BytesForImageUrl(imageUrl)); - } + public Task> SetGenreImageByUrlAsync(Library.Models.Users.User user, Guid id, string imageUrl) => SaveImageBytes(user, id, WebHelper.BytesForImageUrl(imageUrl)); - public async Task> UpdateGenre(Library.Models.Users.User user, Genre model) + public async Task> UpdateGenreAsync(Library.Models.Users.User user, Genre model) { var sw = new Stopwatch(); sw.Start(); var errors = new List(); - var genre = DbContext.Genres.FirstOrDefault(x => x.RoadieId == model.Id); + var genre = await DbContext.Genres.FirstOrDefaultAsync(x => x.RoadieId == model.Id).ConfigureAwait(false); if (genre == null) { - return new OperationResult(true, string.Format("Genre Not Found [{0}]", model.Id)); + return new OperationResult(true, $"Genre Not Found [{model.Id}]"); } // If genre is being renamed, see if genre already exists with new model supplied name var genreName = genre.SortNameValue; @@ -229,7 +322,7 @@ namespace Roadie.Api.Services File.WriteAllBytes(genre.PathToImage(Configuration), ImageHelper.ConvertToJpegFormat(genreImage)); } genre.LastUpdated = now; - await DbContext.SaveChangesAsync(); + await DbContext.SaveChangesAsync().ConfigureAwait(false); CacheManager.ClearRegion(genre.CacheRegion); Logger.LogInformation($"UpdateGenre `{genre}` By User `{user}`"); @@ -244,14 +337,14 @@ namespace Roadie.Api.Services return new OperationResult { - IsSuccess = !errors.Any(), - Data = !errors.Any(), + IsSuccess = errors.Count == 0, + Data = errors.Count == 0, OperationTime = sw.ElapsedMilliseconds, Errors = errors }; } - public async Task> UploadGenreImage(Library.Models.Users.User user, Guid id, IFormFile file) + public async Task> UploadGenreImageAsync(Library.Models.Users.User user, Guid id, IFormFile file) { var bytes = new byte[0]; using (var ms = new MemoryStream()) @@ -259,102 +352,7 @@ namespace Roadie.Api.Services file.CopyTo(ms); bytes = ms.ToArray(); } - - return await SaveImageBytes(user, id, bytes); - } - - private Task> GenreByIdAction(Guid id, IEnumerable includes = null) - { - var timings = new Dictionary(); - var tsw = new Stopwatch(); - - var sw = Stopwatch.StartNew(); - sw.Start(); - - var genre = DbContext.Genres.FirstOrDefault(x => x.RoadieId == id); - if (genre == null) - { - return Task.FromResult(new OperationResult(true, string.Format("Genre Not Found [{0}]", id))); - } - tsw.Stop(); - timings.Add("getGenre", tsw.ElapsedMilliseconds); - - tsw.Restart(); - var result = genre.Adapt(); - result.AlternateNames = genre.AlternateNames; - result.Tags = genre.Tags; - result.Thumbnail = ImageHelper.MakeGenreThumbnailImage(Configuration, HttpContext, genre.RoadieId); - result.MediumThumbnail = ImageHelper.MakeThumbnailImage(Configuration, HttpContext, id, "genre", Configuration.MediumImageSize.Width, Configuration.MediumImageSize.Height); - tsw.Stop(); - timings.Add("adapt", tsw.ElapsedMilliseconds); - if (includes != null && includes.Any()) - { - if (includes.Contains("stats")) - { - tsw.Restart(); - var releaseCount = (from rg in DbContext.ReleaseGenres - where rg.GenreId == genre.Id - select rg.Id).Count(); - var artistCount = (from rg in DbContext.ArtistGenres - where rg.GenreId == genre.Id - select rg.Id).Count(); - result.Statistics = new Library.Models.Statistics.ReleaseGroupingStatistics - { - ArtistCount = artistCount, - ReleaseCount = releaseCount - }; - tsw.Stop(); - timings.Add("stats", tsw.ElapsedMilliseconds); - } - } - sw.Stop(); - Logger.LogInformation($"ByIdAction: Genre `{ genre }`: includes [{includes.ToCSV()}], timings: [{ timings.ToTimings() }]"); - return Task.FromResult(new OperationResult - { - Data = result, - IsSuccess = result != null, - OperationTime = sw.ElapsedMilliseconds - }); - } - - private async Task> SaveImageBytes(Library.Models.Users.User user, Guid id, byte[] imageBytes) - { - var sw = new Stopwatch(); - sw.Start(); - var errors = new List(); - var genre = DbContext.Genres.FirstOrDefault(x => x.RoadieId == id); - if (genre == null) - { - return new OperationResult(true, string.Format("Genre Not Found [{0}]", id)); - } - try - { - var now = DateTime.UtcNow; - if (imageBytes != null) - { - // Save unaltered genre image - File.WriteAllBytes(genre.PathToImage(Configuration, true), ImageHelper.ConvertToJpegFormat(imageBytes)); - } - genre.LastUpdated = now; - await DbContext.SaveChangesAsync(); - CacheManager.ClearRegion(genre.CacheRegion); - Logger.LogInformation($"UploadGenreImage `{genre}` By User `{user}`"); - } - catch (Exception ex) - { - Logger.LogError(ex); - errors.Add(ex); - } - - sw.Stop(); - - return new OperationResult - { - IsSuccess = !errors.Any(), - Data = ImageHelper.MakeThumbnailImage(Configuration, HttpContext, id, "genre", Configuration.MediumImageSize.Width, Configuration.MediumImageSize.Height, true), - OperationTime = sw.ElapsedMilliseconds, - Errors = errors - }; + return await SaveImageBytes(user, id, bytes).ConfigureAwait(false); } } } \ No newline at end of file diff --git a/Roadie.Api.Services/HttpContext.cs b/Roadie.Api.Services/HttpContext.cs index 5e57e77..091d6b8 100644 --- a/Roadie.Api.Services/HttpContext.cs +++ b/Roadie.Api.Services/HttpContext.cs @@ -14,10 +14,17 @@ namespace Roadie.Api.Services public HttpContext(IRoadieSettings configuration, IUrlHelper urlHelper) { var scheme = urlHelper.ActionContext.HttpContext.Request.Scheme; - if (configuration.UseSSLBehindProxy) scheme = "https"; + if (configuration.UseSSLBehindProxy) + { + scheme = "https"; + } + var host = urlHelper.ActionContext.HttpContext.Request.Host; if (!string.IsNullOrEmpty(configuration.BehindProxyHost)) + { host = new HostString(configuration.BehindProxyHost); + } + BaseUrl = $"{scheme}://{host}"; ImageBaseUrl = $"{BaseUrl}/images"; } diff --git a/Roadie.Api.Services/IAdminService.cs b/Roadie.Api.Services/IAdminService.cs index 06ae1d5..0dff8d0 100644 --- a/Roadie.Api.Services/IAdminService.cs +++ b/Roadie.Api.Services/IAdminService.cs @@ -9,48 +9,48 @@ namespace Roadie.Api.Services { public interface IAdminService { - Task> DeleteArtist(User user, Guid artistId, bool deleteFolder); + Task> DeleteArtistAsync(User user, Guid artistId, bool deleteFolder); - Task> DeleteArtistReleases(User user, Guid artistId, bool doDeleteFiles = false); + Task> DeleteArtistReleasesAsync(User user, Guid artistId, bool doDeleteFiles = false); - Task> DeleteArtistSecondaryImage(User user, Guid artistId, int index); + Task> DeleteArtistSecondaryImageAsync(User user, Guid artistId, int index); - Task> DeleteGenre(User user, Guid genreId); + Task> DeleteGenreAsync(User user, Guid genreId); - Task> DeleteLabel(User user, Guid labelId); + Task> DeleteLabelAsync(User user, Guid labelId); - Task> DeleteRelease(User user, Guid releaseId, bool? doDeleteFiles); + Task> DeleteReleaseAsync(User user, Guid releaseId, bool? doDeleteFiles); - Task> DeleteReleaseSecondaryImage(User user, Guid releaseId, int index); + Task> DeleteReleaseSecondaryImageAsync(User user, Guid releaseId, int index); - Task> DeleteTracks(User user, IEnumerable trackIds, bool? doDeleteFile); + Task> DeleteTracksAsync(User user, IEnumerable trackIds, bool? doDeleteFile); - Task> DeleteUser(User applicationUser, Guid id); + Task> DeleteUserAsync(User applicationUser, Guid id); - Task> DoInitialSetup(User user, UserManager userManager); + Task> DoInitialSetupAsync(User user, UserManager userManager); - Task>>> MissingCollectionReleases(User user); + Task>>> MissingCollectionReleasesAsync(User user); void PerformStartUpTasks(); - Task> ScanAllCollections(User user, bool isReadOnly = false, bool doPurgeFirst = false); + Task> ScanAllCollectionsAsync(User user, bool isReadOnly = false, bool doPurgeFirst = false); - Task> ScanArtist(User user, Guid artistId, bool isReadOnly = false); + Task> ScanArtistAsync(User user, Guid artistId, bool isReadOnly = false); - Task> ScanArtists(User user, IEnumerable artistIds, bool isReadOnly = false); + Task> ScanArtistsAsync(User user, IEnumerable artistIds, bool isReadOnly = false); - Task> ScanCollection(User user, Guid collectionId, bool isReadOnly = false, bool doPurgeFirst = false, bool doUpdateRanks = true); + Task> ScanCollectionAsync(User user, Guid collectionId, bool isReadOnly = false, bool doPurgeFirst = false, bool doUpdateRanks = true); - Task> ScanInboundFolder(User user, bool isReadOnly = false); + Task> ScanInboundFolderAsync(User user, bool isReadOnly = false); - Task> ScanLibraryFolder(User user, bool isReadOnly = false); + Task> ScanLibraryFolderAsync(User user, bool isReadOnly = false); - Task> ScanRelease(User user, Guid releaseIds, bool isReadOnly = false, bool wasDoneForInvalidTrackPlay = false); + Task> ScanReleaseAsync(User user, Guid releaseIds, bool isReadOnly = false, bool wasDoneForInvalidTrackPlay = false); - Task> ScanReleases(User user, IEnumerable releaseId, bool isReadOnly = false, bool wasDoneForInvalidTrackPlay = false); + Task> ScanReleasesAsync(User user, IEnumerable releaseId, bool isReadOnly = false, bool wasDoneForInvalidTrackPlay = false); - Task> UpdateInviteTokenUsed(Guid? tokenId); + Task> UpdateInviteTokenUsedAsync(Guid? tokenId); - Task> ValidateInviteToken(Guid? tokenId); + Task> ValidateInviteTokenAsync(Guid? tokenId); } } \ No newline at end of file diff --git a/Roadie.Api.Services/IArtistService.cs b/Roadie.Api.Services/IArtistService.cs index 4c74292..9be2598 100644 --- a/Roadie.Api.Services/IArtistService.cs +++ b/Roadie.Api.Services/IArtistService.cs @@ -1,9 +1,7 @@ using Microsoft.AspNetCore.Http; using Roadie.Library; -using Roadie.Library.Identity; using Roadie.Library.Models; using Roadie.Library.Models.Pagination; -using Roadie.Library.Models.Users; using System; using System.Collections.Generic; using System.Threading.Tasks; @@ -12,22 +10,22 @@ namespace Roadie.Api.Services { public interface IArtistService { - Task> ById(Library.Models.Users.User user, Guid id, IEnumerable includes); + Task> ByIdAsync(Library.Models.Users.User user, Guid id, IEnumerable includes); - Task> Delete(Library.Identity.User user, Library.Data.Artist Artist, bool deleteFolder); + Task> DeleteAsync(Library.Identity.User user, Library.Data.Artist Artist, bool deleteFolder); - Task> List(Library.Models.Users.User user, PagedRequest request, bool? doRandomize = false, bool? onlyIncludeWithReleases = true); + Task> ListAsync(Library.Models.Users.User user, PagedRequest request, bool? doRandomize = false, bool? onlyIncludeWithReleases = true); - Task> MergeArtists(Library.Identity.User user, Guid artistToMergeId, Guid artistToMergeIntoId); + Task> MergeArtistsAsync(Library.Identity.User user, Guid artistToMergeId, Guid artistToMergeIntoId); - Task> RefreshArtistMetadata(Library.Identity.User user, Guid ArtistId); + Task> RefreshArtistMetadataAsync(Library.Identity.User user, Guid ArtistId); - Task> ScanArtistReleasesFolders(Library.Identity.User user, Guid artistId, string destinationFolder, bool doJustInfo); + Task> ScanArtistReleasesFoldersAsync(Library.Identity.User user, Guid artistId, string destinationFolder, bool doJustInfo); - Task> SetReleaseImageByUrl(Library.Identity.User user, Guid id, string imageUrl); + Task> SetReleaseImageByUrlAsync(Library.Identity.User user, Guid id, string imageUrl); - Task> UpdateArtist(Library.Identity.User user, Artist artist); + Task> UpdateArtistAsync(Library.Identity.User user, Artist artist); - Task> UploadArtistImage(Library.Identity.User user, Guid id, IFormFile file); + Task> UploadArtistImageAsync(Library.Identity.User user, Guid id, IFormFile file); } } \ No newline at end of file diff --git a/Roadie.Api.Services/IBookmarkService.cs b/Roadie.Api.Services/IBookmarkService.cs index d142039..14f99ca 100644 --- a/Roadie.Api.Services/IBookmarkService.cs +++ b/Roadie.Api.Services/IBookmarkService.cs @@ -9,7 +9,7 @@ namespace Roadie.Api.Services { public interface IBookmarkService { - Task> RemoveAllBookmarksForItem(BookmarkType type, int id); - Task> List(User roadieUser, PagedRequest request, bool? doRandomize = false, BookmarkType? filterType = null); + Task> ListAsync(User roadieUser, PagedRequest request, bool? doRandomize = false, BookmarkType? filterType = null); + Task> RemoveAllBookmarksForItemAsync(BookmarkType type, int id); } } \ No newline at end of file diff --git a/Roadie.Api.Services/ICollectionService.cs b/Roadie.Api.Services/ICollectionService.cs index 0c53b7f..af3fe4e 100644 --- a/Roadie.Api.Services/ICollectionService.cs +++ b/Roadie.Api.Services/ICollectionService.cs @@ -12,13 +12,12 @@ namespace Roadie.Api.Services { OperationResult Add(User roadieUser); - Task> ById(User roadieUser, Guid id, IEnumerable includes = null); + Task> ByIdAsync(User roadieUser, Guid id, IEnumerable includes = null); - Task> DeleteCollection(User user, Guid id); + Task> DeleteCollectionAsync(User user, Guid id); - Task> List(User roadieUser, PagedRequest request, bool? doRandomize = false, - Guid? releaseId = null, Guid? artistId = null); + Task> ListAsync(User roadieUser, PagedRequest request, bool? doRandomize = false, Guid? releaseId = null, Guid? artistId = null); - Task> UpdateCollection(User roadieUser, Collection collection); + Task> UpdateCollectionAsync(User roadieUser, Collection collection); } } \ No newline at end of file diff --git a/Roadie.Api.Services/ICommentService.cs b/Roadie.Api.Services/ICommentService.cs index 35763a2..005676d 100644 --- a/Roadie.Api.Services/ICommentService.cs +++ b/Roadie.Api.Services/ICommentService.cs @@ -8,22 +8,22 @@ namespace Roadie.Api.Services { public interface ICommentService { - Task> AddNewArtistComment(User user, Guid artistId, string cmt); + Task> AddNewArtistCommentAsync(User user, Guid artistId, string cmt); - Task> AddNewCollectionComment(User user, Guid collectionId, string cmt); + Task> AddNewCollectionCommentAsync(User user, Guid collectionId, string cmt); - Task> AddNewGenreComment(User user, Guid genreId, string cmt); + Task> AddNewGenreCommentAsync(User user, Guid genreId, string cmt); - Task> AddNewLabelComment(User user, Guid labelId, string cmt); + Task> AddNewLabelCommentAsync(User user, Guid labelId, string cmt); - Task> AddNewPlaylistComment(User user, Guid playlistId, string cmt); + Task> AddNewPlaylistCommentAsync(User user, Guid playlistId, string cmt); - Task> AddNewReleaseComment(User user, Guid releaseId, string cmt); + Task> AddNewReleaseCommentAsync(User user, Guid releaseId, string cmt); - Task> AddNewTrackComment(User user, Guid trackId, string cmt); + Task> AddNewTrackCommentAsync(User user, Guid trackId, string cmt); - Task> DeleteComment(User user, Guid id); + Task> DeleteCommentAsync(User user, Guid id); - Task> SetCommentReaction(User user, Guid id, CommentReaction reaction); + Task> SetCommentReactionAsync(User user, Guid id, CommentReaction reaction); } } \ No newline at end of file diff --git a/Roadie.Api.Services/IFileDirectoryProcessorService.cs b/Roadie.Api.Services/IFileDirectoryProcessorService.cs index db81a38..9a4aa06 100644 --- a/Roadie.Api.Services/IFileDirectoryProcessorService.cs +++ b/Roadie.Api.Services/IFileDirectoryProcessorService.cs @@ -14,6 +14,6 @@ namespace Roadie.Api.Services int? ProcessLimit { get; set; } - Task> Process(User user, DirectoryInfo folder, bool doJustInfo, int? submissionId = null, bool doDeleteFiles = true); + Task> ProcessAsync(User user, DirectoryInfo folder, bool doJustInfo, int? submissionId = null, bool doDeleteFiles = true); } } \ No newline at end of file diff --git a/Roadie.Api.Services/IGenreService.cs b/Roadie.Api.Services/IGenreService.cs index fddf0bf..6b3aa20 100644 --- a/Roadie.Api.Services/IGenreService.cs +++ b/Roadie.Api.Services/IGenreService.cs @@ -1,9 +1,7 @@ using Microsoft.AspNetCore.Http; using Roadie.Library; -using Roadie.Library.Identity; using Roadie.Library.Models; using Roadie.Library.Models.Pagination; -using Roadie.Library.Models.Users; using System; using System.Collections.Generic; using System.Threading.Tasks; @@ -12,16 +10,16 @@ namespace Roadie.Api.Services { public interface IGenreService { - Task> ById(Library.Models.Users.User roadieUser, Guid id, IEnumerable includes = null); + Task> ByIdAsync(Library.Models.Users.User roadieUser, Guid id, IEnumerable includes = null); - Task> Delete(Library.Identity.User user, Guid id); + Task> DeleteAsync(Library.Identity.User user, Guid id); - Task> List(Library.Models.Users.User roadieUser, PagedRequest request, bool? doRandomize = false); + Task> ListAsync(Library.Models.Users.User roadieUser, PagedRequest request, bool? doRandomize = false); - Task> SetGenreImageByUrl(Library.Models.Users.User user, Guid id, string imageUrl); + Task> SetGenreImageByUrlAsync(Library.Models.Users.User user, Guid id, string imageUrl); - Task> UpdateGenre(Library.Models.Users.User user, Genre model); + Task> UpdateGenreAsync(Library.Models.Users.User user, Genre model); - Task> UploadGenreImage(Library.Models.Users.User user, Guid id, IFormFile file); + Task> UploadGenreImageAsync(Library.Models.Users.User user, Guid id, IFormFile file); } } \ No newline at end of file diff --git a/Roadie.Api.Services/IImageService.cs b/Roadie.Api.Services/IImageService.cs index 0f9001b..0dd361c 100644 --- a/Roadie.Api.Services/IImageService.cs +++ b/Roadie.Api.Services/IImageService.cs @@ -1,8 +1,6 @@ using Microsoft.Net.Http.Headers; using Roadie.Library; -using Roadie.Library.Identity; using Roadie.Library.Imaging; -using Roadie.Library.Models; using Roadie.Library.SearchEngines.Imaging; using System; using System.Collections.Generic; @@ -16,26 +14,26 @@ namespace Roadie.Api.Services string RequestIp { get; set; } - Task> ArtistImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); + Task> ArtistImageAsync(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); - Task> ArtistSecondaryImage(Guid id, int imageId, int? width, int? height, EntityTagHeaderValue etag = null); + Task> ArtistSecondaryImageAsync(Guid id, int imageId, int? width, int? height, EntityTagHeaderValue etag = null); - Task> CollectionImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); + Task> CollectionImageAsync(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); - Task> GenreImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); + Task> GenreImageAsync(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); - Task> LabelImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); + Task> LabelImageAsync(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); - Task> PlaylistImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); + Task> PlaylistImageAsync(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); - Task> ReleaseImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); + Task> ReleaseImageAsync(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); - Task> ReleaseSecondaryImage(Guid id, int imageId, int? width, int? height, EntityTagHeaderValue etag = null); + Task> ReleaseSecondaryImageAsync(Guid id, int imageId, int? width, int? height, EntityTagHeaderValue etag = null); - Task>> Search(string query, int resultsCount = 10); + Task>> SearchAsync(string query, int resultsCount = 10); - Task> TrackImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); + Task> TrackImageAsync(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); - Task> UserImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); + Task> UserImageAsync(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); } } \ No newline at end of file diff --git a/Roadie.Api.Services/ILabelService.cs b/Roadie.Api.Services/ILabelService.cs index 12b904b..d80e43c 100644 --- a/Roadie.Api.Services/ILabelService.cs +++ b/Roadie.Api.Services/ILabelService.cs @@ -1,9 +1,7 @@ using Microsoft.AspNetCore.Http; using Roadie.Library; -using Roadie.Library.Identity; using Roadie.Library.Models; using Roadie.Library.Models.Pagination; -using Roadie.Library.Models.Users; using System; using System.Collections.Generic; using System.Threading.Tasks; @@ -12,18 +10,18 @@ namespace Roadie.Api.Services { public interface ILabelService { - Task> ById(Library.Models.Users.User roadieUser, Guid id, IEnumerable includes = null); + Task> ByIdAsync(Library.Models.Users.User roadieUser, Guid id, IEnumerable includes = null); - Task> Delete(Library.Identity.User user, Guid id); + Task> DeleteAsync(Library.Identity.User user, Guid id); - Task> List(Library.Models.Users.User roadieUser, PagedRequest request, bool? doRandomize = false); + Task> ListAsync(Library.Models.Users.User roadieUser, PagedRequest request, bool? doRandomize = false); - Task> MergeLabelsIntoLabel(Library.Identity.User user, Guid intoLabelId, IEnumerable labelIdsToMerge); + Task> MergeLabelsIntoLabelAsync(Library.Identity.User user, Guid intoLabelId, IEnumerable labelIdsToMerge); - Task> SetLabelImageByUrl(Library.Models.Users.User user, Guid id, string imageUrl); + Task> SetLabelImageByUrlAsync(Library.Models.Users.User user, Guid id, string imageUrl); - Task> UpdateLabel(Library.Models.Users.User user, Label label); + Task> UpdateLabelAsync(Library.Models.Users.User user, Label label); - Task> UploadLabelImage(Library.Models.Users.User user, Guid id, IFormFile file); + Task> UploadLabelImageAsync(Library.Models.Users.User user, Guid id, IFormFile file); } } \ No newline at end of file diff --git a/Roadie.Api.Services/ILookupService.cs b/Roadie.Api.Services/ILookupService.cs index 617fa75..d17ada9 100644 --- a/Roadie.Api.Services/ILookupService.cs +++ b/Roadie.Api.Services/ILookupService.cs @@ -7,24 +7,24 @@ namespace Roadie.Api.Services { public interface ILookupService { - Task>> ArtistTypes(); + Task>> ArtistTypesAsync(); - Task>> BandStatus(); + Task>> BandStatusAsync(); - Task>> BookmarkTypes(); + Task>> BookmarkTypesAsync(); - Task>> CollectionTypes(); + Task>> CollectionTypesAsync(); - Task>> LibraryStatus(); + Task>> CreditCategoriesAsync(); - Task>> QueMessageTypes(); + Task>> LibraryStatusAsync(); - Task>> ReleaseTypes(); + Task>> QueMessageTypesAsync(); - Task>> RequestStatus(); + Task>> ReleaseTypesAsync(); - Task>> Status(); + Task>> RequestStatusAsync(); - Task>> CreditCategories(); + Task>> StatusAsync(); } } \ No newline at end of file diff --git a/Roadie.Api.Services/IPlayActivityService.cs b/Roadie.Api.Services/IPlayActivityService.cs index 54fbde6..efd47e7 100644 --- a/Roadie.Api.Services/IPlayActivityService.cs +++ b/Roadie.Api.Services/IPlayActivityService.cs @@ -10,10 +10,10 @@ namespace Roadie.Api.Services { public interface IPlayActivityService { - Task> List(PagedRequest request, User roadieUser = null, DateTime? newerThan = null); + Task> ListAsync(PagedRequest request, User roadieUser = null, DateTime? newerThan = null); - Task> NowPlaying(User roadieUser, ScrobbleInfo scrobble); + Task> NowPlayingAsync(User roadieUser, ScrobbleInfo scrobble); - Task> Scrobble(User roadieUser, ScrobbleInfo scrobble); + Task> ScrobbleAsync(User roadieUser, ScrobbleInfo scrobble); } } \ No newline at end of file diff --git a/Roadie.Api.Services/IPlaylistService.cs b/Roadie.Api.Services/IPlaylistService.cs index 836c004..2bbdd8a 100644 --- a/Roadie.Api.Services/IPlaylistService.cs +++ b/Roadie.Api.Services/IPlaylistService.cs @@ -11,20 +11,20 @@ namespace Roadie.Api.Services { public interface IPlaylistService { - Task> AddNewPlaylist(User user, Playlist model); + Task> AddNewPlaylistAsync(User user, Playlist model); - Task> AddTracksToPlaylist(data.Playlist playlist, IEnumerable trackIds); + Task> AddTracksToPlaylistAsync(data.Playlist playlist, IEnumerable trackIds); - Task> ById(User roadieUser, Guid id, IEnumerable includes = null); + Task> ByIdAsync(User roadieUser, Guid id, IEnumerable includes = null); - Task> DeletePlaylist(User user, Guid id); + Task> DeletePlaylistAsync(User user, Guid id); - Task> List(PagedRequest request, User roadieUser = null); + Task> ListAsync(PagedRequest request, User roadieUser = null); - Task> ReorderPlaylist(data.Playlist playlist); + Task> ReorderPlaylistAsync(data.Playlist playlist); - Task> UpdatePlaylist(User user, Playlist label); + Task> UpdatePlaylistAsync(User user, Playlist label); - Task> UpdatePlaylistTracks(User user, PlaylistTrackModifyRequest request); + Task> UpdatePlaylistTracksAsync(User user, PlaylistTrackModifyRequest request); } } \ No newline at end of file diff --git a/Roadie.Api.Services/IReleaseService.cs b/Roadie.Api.Services/IReleaseService.cs index 99f3b5b..e1b413c 100644 --- a/Roadie.Api.Services/IReleaseService.cs +++ b/Roadie.Api.Services/IReleaseService.cs @@ -1,10 +1,8 @@ using Microsoft.AspNetCore.Http; using Roadie.Library; -using Roadie.Library.Identity; using Roadie.Library.Models; using Roadie.Library.Models.Pagination; using Roadie.Library.Models.Releases; -using Roadie.Library.Models.Users; using System; using System.Collections.Generic; using System.Threading.Tasks; @@ -15,26 +13,26 @@ namespace Roadie.Api.Services { IEnumerable AddedTrackIds { get; } - Task> ById(Library.Models.Users.User roadieUser, Guid id, IEnumerable includes = null); + Task> ByIdAsync(Library.Models.Users.User roadieUser, Guid id, IEnumerable includes = null); - Task> Delete(Library.Identity.User user, Library.Data.Release release, bool doDeleteFiles = false, bool doUpdateArtistCounts = true); + Task> DeleteAsync(Library.Identity.User user, Library.Data.Release release, bool doDeleteFiles = false, bool doUpdateArtistCounts = true); - Task> DeleteReleases(Library.Identity.User user, IEnumerable releaseIds, bool doDeleteFiles = false); + Task> DeleteReleasesAsync(Library.Identity.User user, IEnumerable releaseIds, bool doDeleteFiles = false); - Task> List(Library.Models.Users.User user, PagedRequest request, bool? doRandomize = false, IEnumerable includes = null); + Task> ListAsync(Library.Models.Users.User user, PagedRequest request, bool? doRandomize = false, IEnumerable includes = null); - Task> MergeReleases(Library.Identity.User user, Guid releaseToMergeId, Guid releaseToMergeIntoId, bool addAsMedia); + Task> MergeReleasesAsync(Library.Identity.User user, Guid releaseToMergeId, Guid releaseToMergeIntoId, bool addAsMedia); - Task> MergeReleases(Library.Identity.User user, Library.Data.Release releaseToMerge, Library.Data.Release releaseToMergeInto, bool addAsMedia); + Task> MergeReleasesAsync(Library.Identity.User user, Library.Data.Release releaseToMerge, Library.Data.Release releaseToMergeInto, bool addAsMedia); - Task> ReleaseZipped(Library.Models.Users.User roadieUser, Guid id); + Task> ReleaseZippedAsync(Library.Models.Users.User roadieUser, Guid id); - Task> ScanReleaseFolder(Library.Identity.User user, Guid releaseId, bool doJustInfo, Library.Data.Release releaseToScan = null); + Task> ScanReleaseFolderAsync(Library.Identity.User user, Guid releaseId, bool doJustInfo, Library.Data.Release releaseToScan = null); - Task> SetReleaseImageByUrl(Library.Identity.User user, Guid id, string imageUrl); + Task> SetReleaseImageByUrlAsync(Library.Identity.User user, Guid id, string imageUrl); - Task> UpdateRelease(Library.Identity.User user, Release release, string originalReleaseFolder = null); + Task> UpdateReleaseAsync(Library.Identity.User user, Release release, string originalReleaseFolder = null); - Task> UploadReleaseImage(Library.Identity.User user, Guid id, IFormFile file); + Task> UploadReleaseImageAsync(Library.Identity.User user, Guid id, IFormFile file); } } \ No newline at end of file diff --git a/Roadie.Api.Services/IStatisticsService.cs b/Roadie.Api.Services/IStatisticsService.cs index 2ab1b12..690c4f0 100644 --- a/Roadie.Api.Services/IStatisticsService.cs +++ b/Roadie.Api.Services/IStatisticsService.cs @@ -7,17 +7,17 @@ namespace Roadie.Api.Services { public interface IStatisticsService { - Task> LibraryStatistics(); - Task>> ArtistsByDate(); + Task>> ArtistsByDateAsync(); + Task> LibraryStatisticsAsync(); - Task>> ReleasesByDate(); + Task>> ReleasesByDateAsync(); - Task>> ReleasesByDecade(); + Task>> ReleasesByDecadeAsync(); - Task>> SongsPlayedByDate(); + Task>> SongsPlayedByDateAsync(); - Task>> SongsPlayedByUser(); + Task>> SongsPlayedByUserAsync(); } } \ No newline at end of file diff --git a/Roadie.Api.Services/ISubsonicService.cs b/Roadie.Api.Services/ISubsonicService.cs index 1d4751b..f0fbc14 100644 --- a/Roadie.Api.Services/ISubsonicService.cs +++ b/Roadie.Api.Services/ISubsonicService.cs @@ -1,5 +1,4 @@ -using Roadie.Library.Models; -using Roadie.Library.Models.ThirdPartyApi.Subsonic; +using Roadie.Library.Models.ThirdPartyApi.Subsonic; using System.Threading.Tasks; using User = Roadie.Library.Models.Users.User; @@ -7,85 +6,85 @@ namespace Roadie.Api.Services { public interface ISubsonicService { - Task> AddChatMessage(Request request, User roadieUser); + Task> AddChatMessageAsync(Request request, User roadieUser); - Task> Authenticate(Request request); + Task> AuthenticateAsync(Request request); - Task> CreateBookmark(Request request, User roadieUser, int position, string comment); + Task> CreateBookmarkAsync(Request request, User roadieUser, int position, string comment); - Task> CreatePlaylist(Request request, User roadieUser, string name, string[] songIds, string playlistId = null); + Task> CreatePlaylistAsync(Request request, User roadieUser, string name, string[] songIds, string playlistId = null); - Task> DeleteBookmark(Request request, User roadieUser); + Task> DeleteBookmarkAsync(Request request, User roadieUser); - Task> DeletePlaylist(Request request, User roadieUser); + Task> DeletePlaylistAsync(Request request, User roadieUser); - Task> GetAlbum(Request request, User roadieUser); + Task> GetAlbumAsync(Request request, User roadieUser); - Task> GetAlbumInfo(Request request, User roadieUser, AlbumInfoVersion version); + Task> GetAlbumInfoAsync(Request request, User roadieUser, AlbumInfoVersion version); - Task> GetAlbumList(Request request, User roadieUser, AlbumListVersions version); + Task> GetAlbumListAsync(Request request, User roadieUser, AlbumListVersions version); - Task> GetArtist(Request request, User roadieUser); + Task> GetArtistAsync(Request request, User roadieUser); - Task> GetArtistInfo(Request request, int? count, bool includeNotPresent, ArtistInfoVersion version); + Task> GetArtistInfoAsync(Request request, int? count, bool includeNotPresent, ArtistInfoVersion version); - Task> GetArtists(Request request, User roadieUser); + Task> GetArtistsAsync(Request request, User roadieUser); - Task> GetBookmarks(Request request, User roadieUser); + Task> GetBookmarksAsync(Request request, User roadieUser); - Task> GetChatMessages(Request request, User roadieUser, long? since); + Task> GetChatMessagesAsync(Request request, User roadieUser, long? since); - Task> GetCoverArt(Request request, int? size); + Task> GetCoverArtAsync(Request request, int? size); - Task> GetGenres(Request request); + Task> GetGenresAsync(Request request); - Task> GetIndexes(Request request, User roadieUser, long? ifModifiedSince = null); + Task> GetIndexesAsync(Request request, User roadieUser, long? ifModifiedSince = null); SubsonicOperationResult GetLicense(Request request); SubsonicOperationResult GetLyrics(Request request, string artistId, string title); - Task> GetMusicDirectory(Request request, User roadieUser); + Task> GetMusicDirectoryAsync(Request request, User roadieUser); - Task> GetMusicFolders(Request request); + Task> GetMusicFoldersAsync(Request request); - Task> GetNowPlaying(Request request, User roadieUser); + Task> GetNowPlayingAsync(Request request, User roadieUser); - Task> GetPlaylist(Request request, User roadieUser); + Task> GetPlaylistAsync(Request request, User roadieUser); - Task> GetPlaylists(Request request, User roadieUser, string filterToUserName); + Task> GetPlaylistsAsync(Request request, User roadieUser, string filterToUserName); - Task> GetPlayQueue(Request request, User roadieUser); + Task> GetPlayQueueAsync(Request request, User roadieUser); - Task> GetPodcasts(Request request); + Task> GetPodcastsAsync(Request request); - Task> GetRandomSongs(Request request, User roadieUser); + Task> GetRandomSongsAsync(Request request, User roadieUser); - Task> GetSimliarSongs(Request request, User roadieUser, SimilarSongsVersion version, int? count = 50); + Task> GetSimliarSongsAsync(Request request, User roadieUser, SimilarSongsVersion version, int? count = 50); - Task> GetSong(Request request, User roadieUser); + Task> GetSongAsync(Request request, User roadieUser); - Task> GetSongsByGenre(Request request, User roadieUser); + Task> GetSongsByGenreAsync(Request request, User roadieUser); - Task> GetStarred(Request request, User roadieUser, StarredVersion version); + Task> GetStarredAsync(Request request, User roadieUser, StarredVersion version); - Task> GetTopSongs(Request request, User roadieUser, int? count = 50); + Task> GetTopSongsAsync(Request request, User roadieUser, int? count = 50); - Task> GetUser(Request request, string username); + Task> GetUserAsync(Request request, string username); SubsonicOperationResult GetVideos(Request request); SubsonicOperationResult Ping(Request request); - Task> SavePlayQueue(Request request, User roadieUser, string current, long? position); + Task> SavePlayQueueAsync(Request request, User roadieUser, string current, long? position); - Task> Search(Request request, User roadieUser, SearchVersion version); + Task> SearchAsync(Request request, User roadieUser, SearchVersion version); - Task> SetRating(Request request, User roadieUser, short rating); + Task> SetRatingAsync(Request request, User roadieUser, short rating); - Task> ToggleStar(Request request, User roadieUser, bool star, string[] albumIds = null, string[] artistIds = null); + Task> ToggleStarAsync(Request request, User roadieUser, bool star, string[] albumIds = null, string[] artistIds = null); - Task> UpdatePlaylist(Request request, User roadieUser, string playlistId, + Task> UpdatePlaylistAsync(Request request, User roadieUser, string playlistId, string name = null, string comment = null, bool? isPublic = null, string[] songIdsToAdd = null, int[] songIndexesToRemove = null); } diff --git a/Roadie.Api.Services/ITokenService.cs b/Roadie.Api.Services/ITokenService.cs index bc11828..451157a 100644 --- a/Roadie.Api.Services/ITokenService.cs +++ b/Roadie.Api.Services/ITokenService.cs @@ -6,6 +6,6 @@ namespace Roadie.Api.Services { public interface ITokenService { - Task GenerateToken(User user, UserManager userManager); + Task GenerateTokenAsync(User user, UserManager userManager); } } \ No newline at end of file diff --git a/Roadie.Api.Services/ITrackService.cs b/Roadie.Api.Services/ITrackService.cs index 7696a1c..b145102 100644 --- a/Roadie.Api.Services/ITrackService.cs +++ b/Roadie.Api.Services/ITrackService.cs @@ -10,16 +10,14 @@ namespace Roadie.Api.Services { public interface ITrackService { - Task> ById(User roadieUser, Guid id, IEnumerable includes); + Task> ByIdAsyncAsync(User roadieUser, Guid id, IEnumerable includes); - Task> List(PagedRequest request, User roadieUser, bool? doRandomize = false, - Guid? releaseId = null); + Task> ListAsync(PagedRequest request, User roadieUser, bool? doRandomize = false, Guid? releaseId = null); OperationResult StreamCheckAndInfo(User roadieUser, Guid id); - Task> TrackStreamInfo(Guid trackId, long beginBytes, long endBytes, - User roadieUser); + Task> TrackStreamInfoAsync(Guid trackId, long beginBytes, long endBytes, User roadieUser); - Task> UpdateTrack(User user, Track track); + Task> UpdateTrackAsync(User user, Track track); } } \ No newline at end of file diff --git a/Roadie.Api.Services/IUserService.cs b/Roadie.Api.Services/IUserService.cs index e7bc1dc..26f8b64 100644 --- a/Roadie.Api.Services/IUserService.cs +++ b/Roadie.Api.Services/IUserService.cs @@ -9,44 +9,44 @@ namespace Roadie.Api.Services { public interface IUserService { - Task> ById(User user, Guid id, IEnumerable includes, bool isAccountSettingsEdit = false); + Task> ByIdAsync(User user, Guid id, IEnumerable includes, bool isAccountSettingsEdit = false); - Task> List(PagedRequest request); + Task> DeleteAllBookmarksAsync(User roadieUser); - Task> DeleteAllBookmarks(User roadieUser); + Task> ListAsync(PagedRequest request); - Task> SetArtistBookmark(Guid artistId, User roadieUser, bool isBookmarked); + Task> SetArtistBookmarkAsync(Guid artistId, User roadieUser, bool isBookmarked); - Task> SetArtistDisliked(Guid artistId, User roadieUser, bool isDisliked); + Task> SetArtistDislikedAsync(Guid artistId, User roadieUser, bool isDisliked); - Task> SetArtistFavorite(Guid artistId, User roadieUser, bool isFavorite); + Task> SetArtistFavoriteAsync(Guid artistId, User roadieUser, bool isFavorite); - Task> SetArtistRating(Guid artistId, User roadieUser, short rating); + Task> SetArtistRatingAsync(Guid artistId, User roadieUser, short rating); - Task> SetCollectionBookmark(Guid collectionId, User roadieUser, bool isBookmarked); + Task> SetCollectionBookmarkAsync(Guid collectionId, User roadieUser, bool isBookmarked); - Task> SetLabelBookmark(Guid labelId, User roadieUser, bool isBookmarked); + Task> SetLabelBookmarkAsync(Guid labelId, User roadieUser, bool isBookmarked); - Task> SetPlaylistBookmark(Guid playlistId, User roadieUser, bool isBookmarked); + Task> SetPlaylistBookmarkAsync(Guid playlistId, User roadieUser, bool isBookmarked); - Task> SetReleaseBookmark(Guid releaseid, User roadieUser, bool isBookmarked); + Task> SetReleaseBookmarkAsync(Guid releaseid, User roadieUser, bool isBookmarked); - Task> SetReleaseDisliked(Guid releaseId, User roadieUser, bool isDisliked); + Task> SetReleaseDislikedAsync(Guid releaseId, User roadieUser, bool isDisliked); - Task> SetReleaseFavorite(Guid releaseId, User roadieUser, bool isFavorite); + Task> SetReleaseFavoriteAsync(Guid releaseId, User roadieUser, bool isFavorite); - Task> SetReleaseRating(Guid releaseId, User roadieUser, short rating); + Task> SetReleaseRatingAsync(Guid releaseId, User roadieUser, short rating); - Task> SetTrackBookmark(Guid trackId, User roadieUser, bool isBookmarked); + Task> SetTrackBookmarkAsync(Guid trackId, User roadieUser, bool isBookmarked); - Task> SetTrackDisliked(Guid trackId, User roadieUser, bool isDisliked); + Task> SetTrackDislikedAsync(Guid trackId, User roadieUser, bool isDisliked); - Task> SetTrackFavorite(Guid releaseId, User roadieUser, bool isFavorite); + Task> SetTrackFavoriteAsync(Guid releaseId, User roadieUser, bool isFavorite); - Task> SetTrackRating(Guid trackId, User roadieUser, short rating); + Task> SetTrackRatingAsync(Guid trackId, User roadieUser, short rating); - Task> UpdateIntegrationGrant(Guid userId, string integrationName, string token); + Task> UpdateIntegrationGrantAsync(Guid userId, string integrationName, string token); - Task> UpdateProfile(User userPerformingUpdate, User userBeingUpdatedModel); + Task> UpdateProfileAsync(User userPerformingUpdate, User userBeingUpdatedModel); } } \ No newline at end of file diff --git a/Roadie.Api.Services/ImageService.cs b/Roadie.Api.Services/ImageService.cs index ceb7cb8..503196b 100644 --- a/Roadie.Api.Services/ImageService.cs +++ b/Roadie.Api.Services/ImageService.cs @@ -23,12 +23,20 @@ namespace Roadie.Api.Services { public class ImageService : ServiceBase, IImageService { - public string Referrer { get; set; } - public string RequestIp { get; set; } private IDefaultNotFoundImages DefaultNotFoundImages { get; } private IImageSearchManager ImageSearchManager { get; } + public string Referrer { get; set; } + public string RequestIp { get; set; } + + public ImageService(IRoadieSettings configuration, IRoadieDbContext dbContext, ICacheManager cacheManager, + ILogger logger, DefaultNotFoundImages defaultNotFoundImages) + : base(configuration, null, dbContext, cacheManager, logger, null) + { + DefaultNotFoundImages = defaultNotFoundImages; + } + public ImageService(IRoadieSettings configuration, IHttpEncoder httpEncoder, IHttpContext httpContext, @@ -43,149 +51,6 @@ namespace Roadie.Api.Services ImageSearchManager = imageSearchManager; } - public ImageService(IRoadieSettings configuration, IRoadieDbContext dbContext, ICacheManager cacheManager, - ILogger logger, DefaultNotFoundImages defaultNotFoundImages) - : base(configuration, null, dbContext, cacheManager, logger, null) - { - DefaultNotFoundImages = defaultNotFoundImages; - } - - public async Task> ArtistImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null) - { - return await GetImageFileOperation("ArtistImage", - data.Artist.CacheRegionUrn(id), - id, - width, - height, - async () => { return await ArtistImageAction(id, etag); }, - etag); - } - - public async Task> ArtistSecondaryImage(Guid id, int imageId, int? width, int? height, EntityTagHeaderValue etag = null) - { - return await GetImageFileOperation($"ArtistSecondaryThumbnail.{imageId}", - data.Artist.CacheRegionUrn(id), - id, - width, - height, - async () => { return await ArtistSecondaryImageAction(id, imageId, etag); }, - etag); - } - - public async Task> CollectionImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null) - { - return await GetImageFileOperation("CollectionThumbnail", - data.Collection.CacheRegionUrn(id), - id, - width, - height, - async () => { return await CollectionImageAction(id, etag); }, - etag); - } - - public async Task> GenreImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null) - { - return await GetImageFileOperation("GenreThumbnail", - data.Genre.CacheRegionUrn(id), - id, - width, - height, - async () => await GenreImageAction(id, etag), - etag); - } - - public async Task> LabelImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null) - { - return await GetImageFileOperation("LabelThumbnail", - data.Label.CacheRegionUrn(id), - id, - width, - height, - async () => await LabelImageAction(id, etag), - etag); - } - - public async Task> PlaylistImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null) - { - return await GetImageFileOperation("PlaylistThumbnail", - data.Playlist.CacheRegionUrn(id), - id, - width, - height, - async () => await PlaylistImageAction(id, etag), - etag); - } - - public async Task> ReleaseImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null) - { - return await GetImageFileOperation("ReleaseThumbnail", - data.Release.CacheRegionUrn(id), - id, - width, - height, - async () => await ReleaseImageAction(id, etag), - etag); - } - - public async Task> ReleaseSecondaryImage(Guid id, int imageId, int? width, int? height, EntityTagHeaderValue etag = null) - { - return await GetImageFileOperation($"ReleaseSecondaryThumbnail-{imageId}", - data.Release.CacheRegionUrn(id), - id, - width, - height, - async () => { return await ReleaseSecondaryImageAction(id, imageId, etag); }, - etag); - } - - public async Task>> Search(string query, int resultsCount = 10) - { - var sw = new Stopwatch(); - sw.Start(); - var errors = new List(); - IEnumerable searchResults = null; - try - { - searchResults = await ImageSearchManager.ImageSearch(query); - } - catch (Exception ex) - { - Logger.LogError(ex); - errors.Add(ex); - } - - sw.Stop(); - return new OperationResult> - { - Data = searchResults, - IsSuccess = !errors.Any(), - OperationTime = sw.ElapsedMilliseconds, - Errors = errors - }; - } - - public async Task> TrackImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null) - { - return await GetImageFileOperation("TrackThumbnail", - data.Track.CacheRegionUrn(id), - id, - width, - height, - async () => await TrackImageAction(id, width, height, etag), - etag); - } - - public async Task> UserImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null) - { - return await GetImageFileOperation("UserById", - User.CacheRegionUrn(id), - id, - width, - height, - async () => await UserImageAction(id, etag), - etag); - } - /// /// Get image for an artist, see if the artist has an image in their folder and use that else use Artist.Thumbnail, is also not found use Artist DefaultNotFound image. /// @@ -193,10 +58,10 @@ namespace Roadie.Api.Services { try { - var artist = await GetArtist(id); + var artist = await GetArtist(id).ConfigureAwait(false); if (artist == null) { - return new FileOperationResult(true, string.Format("Artist Not Found [{0}]", id)); + return new FileOperationResult(true, $"Artist Not Found [{id}]"); } byte[] imageBytes = null; string artistFolder = null; @@ -226,7 +91,7 @@ namespace Roadie.Api.Services { Bytes = imageBytes, }; - if (imageBytes == null || !imageBytes.Any()) + if (imageBytes?.Any() != true) { image = DefaultNotFoundImages.Artist; } @@ -244,10 +109,10 @@ namespace Roadie.Api.Services { try { - var artist = await GetArtist(id); + var artist = await GetArtist(id).ConfigureAwait(false); if (artist == null) { - return new FileOperationResult(true, string.Format("Release Not Found [{0}]", id)); + return new FileOperationResult(true, $"Release Not Found [{id}]"); } byte[] imageBytes = null; string artistFolder = null; @@ -265,7 +130,9 @@ namespace Roadie.Api.Services .FindImageTypeInDirectory(new DirectoryInfo(artistFolder), ImageType.ArtistSecondary) .ToArray(); if (artistSecondaryImages.Length >= imageId && artistSecondaryImages[imageId] != null) + { imageBytes = File.ReadAllBytes(artistSecondaryImages[imageId].FullName); + } } } catch (Exception ex) @@ -292,10 +159,10 @@ namespace Roadie.Api.Services { try { - var collection = await GetCollection(id); + var collection = await GetCollection(id).ConfigureAwait(false); if (collection == null) { - return new FileOperationResult(true, string.Format("Collection Not Found [{0}]", id)); + return new FileOperationResult(true, $"Collection Not Found [{id}]"); } IImage image = new Library.Imaging.Image(id) { @@ -313,7 +180,7 @@ namespace Roadie.Api.Services { Logger.LogError(ex, $"Error Reading Image File [{collectionImageFilename}]"); } - if (image.Bytes == null || !image.Bytes.Any()) + if (image.Bytes?.Any() != true) { image = DefaultNotFoundImages.Collection; } @@ -336,7 +203,7 @@ namespace Roadie.Api.Services } if (image?.Bytes?.Any() != true) { - return new FileOperationResult(string.Format("ImageById Not Set [{0}]", id)); + return new FileOperationResult($"ImageById Not Set [{id}]"); } return new FileOperationResult(image?.Bytes?.Any() ?? false ? OperationMessages.OkMessage @@ -354,10 +221,10 @@ namespace Roadie.Api.Services { try { - var genre = await GetGenre(id); + var genre = await GetGenre(id).ConfigureAwait(false); if (genre == null) { - return new FileOperationResult(true, string.Format("Genre Not Found [{0}]", id)); + return new FileOperationResult(true, $"Genre Not Found [{id}]"); } IImage image = new Library.Imaging.Image(id) { @@ -375,7 +242,7 @@ namespace Roadie.Api.Services { Logger.LogError(ex, $"Error Reading Image File [{genreImageFilename}]"); } - if (image.Bytes == null || !image.Bytes.Any()) + if (image.Bytes?.Any() != true) { image = DefaultNotFoundImages.Genre; } @@ -395,7 +262,7 @@ namespace Roadie.Api.Services { var sw = Stopwatch.StartNew(); var sizeHash = (width ?? 0) + (height ?? 0); - var result = await CacheManager.GetAsync($"urn:{type}_by_id_operation:{id}:{sizeHash}", action, regionUrn); + var result = await CacheManager.GetAsync($"urn:{type}_by_id_operation:{id}:{sizeHash}", action, regionUrn).ConfigureAwait(false); if (!result.IsSuccess) { return new FileOperationResult(result.IsNotFoundResult, result.Messages); @@ -450,10 +317,10 @@ namespace Roadie.Api.Services { try { - var label = await GetLabel(id); + var label = await GetLabel(id).ConfigureAwait(false); if (label == null) { - return new FileOperationResult(true, string.Format("Label Not Found [{0}]", id)); + return new FileOperationResult(true, $"Label Not Found [{id}]"); } IImage image = new Library.Imaging.Image(id) { @@ -471,7 +338,7 @@ namespace Roadie.Api.Services { Logger.LogError(ex, $"Error Reading Image File [{labelImageFilename}]"); } - if (image.Bytes == null || !image.Bytes.Any()) + if (image.Bytes?.Any() != true) { image = DefaultNotFoundImages.Label; } @@ -489,10 +356,10 @@ namespace Roadie.Api.Services { try { - var playlist = await GetPlaylist(id); + var playlist = await GetPlaylist(id).ConfigureAwait(false); if (playlist == null) { - return new FileOperationResult(true, string.Format("Playlist Not Found [{0}]", id)); + return new FileOperationResult(true, $"Playlist Not Found [{id}]"); } IImage image = new Library.Imaging.Image(id) { @@ -510,7 +377,7 @@ namespace Roadie.Api.Services { Logger.LogError(ex, $"Error Reading Image File [{playlistImageFilename}]"); } - if (image.Bytes == null || !image.Bytes.Any()) + if (image.Bytes?.Any() != true) { image = DefaultNotFoundImages.Playlist; } @@ -531,10 +398,10 @@ namespace Roadie.Api.Services { try { - var release = await GetRelease(id); + var release = await GetRelease(id).ConfigureAwait(false); if (release == null) { - return new FileOperationResult(true, string.Format("Release Not Found [{0}]", id)); + return new FileOperationResult(true, $"Release Not Found [{id}]"); } byte[] imageBytes = null; string artistFolder = null; @@ -591,10 +458,10 @@ namespace Roadie.Api.Services { try { - var release = await GetRelease(id); + var release = await GetRelease(id).ConfigureAwait(false); if (release == null) { - return new FileOperationResult(true, string.Format("Release Not Found [{0}]", id)); + return new FileOperationResult(true, $"Release Not Found [{id}]"); } byte[] imageBytes = null; string artistFolder = null; @@ -620,7 +487,9 @@ namespace Roadie.Api.Services .FindImageTypeInDirectory(new DirectoryInfo(releaseFolder), ImageType.ReleaseSecondary) .ToArray(); if (releaseSecondaryImages.Length >= imageId && releaseSecondaryImages[imageId] != null) + { imageBytes = File.ReadAllBytes(releaseSecondaryImages[imageId].FullName); + } } } } @@ -649,10 +518,10 @@ namespace Roadie.Api.Services { try { - var track = await GetTrack(id); + var track = await GetTrack(id).ConfigureAwait(false); if (track == null) { - return new FileOperationResult(true, string.Format("Track Not Found [{0}]", id)); + return new FileOperationResult(true, $"Track Not Found [{id}]"); } IImage image = new Library.Imaging.Image(id) { @@ -663,10 +532,10 @@ namespace Roadie.Api.Services { image.Bytes = File.ReadAllBytes(trackImageFileName); } - if (image.Bytes == null || !image.Bytes.Any()) + if (image.Bytes?.Any() != true) { // If no track image is found then return image for release - return await ReleaseImage(track.ReleaseMedia.Release.RoadieId, width, height, etag); + return await ReleaseImageAsync(track.ReleaseMedia.Release.RoadieId, width, height, etag).ConfigureAwait(false); } return GenerateFileOperationResult(id, image, etag); } @@ -682,10 +551,10 @@ namespace Roadie.Api.Services { try { - var user = await GetUser(id); + var user = await GetUser(id).ConfigureAwait(false); if (user == null) { - return new FileOperationResult(true, string.Format("User Not Found [{0}]", id)); + return new FileOperationResult(true, $"User Not Found [{id}]"); } IImage image = new Library.Imaging.Image(id) { @@ -703,7 +572,7 @@ namespace Roadie.Api.Services { Logger.LogError(ex, $"Error Reading Image File [{userImageFilename}]"); } - if (image.Bytes == null || !image.Bytes.Any()) + if (image.Bytes?.Any() != true) { image = DefaultNotFoundImages.User; } @@ -716,5 +585,141 @@ namespace Roadie.Api.Services return new FileOperationResult(OperationMessages.ErrorOccured); } + + public Task> ArtistImageAsync(Guid id, int? width, int? height, EntityTagHeaderValue etag = null) + { + return GetImageFileOperation(nameof(ArtistImageAsync), + data.Artist.CacheRegionUrn(id), + id, + width, + height, + async () => { return await ArtistImageAction(id, etag).ConfigureAwait(false); }, + etag); + } + + public Task> ArtistSecondaryImageAsync(Guid id, int imageId, int? width, int? height, EntityTagHeaderValue etag = null) + { + return GetImageFileOperation($"ArtistSecondaryThumbnail.{imageId}", + data.Artist.CacheRegionUrn(id), + id, + width, + height, + async () => { return await ArtistSecondaryImageAction(id, imageId, etag).ConfigureAwait(false); }, + etag); + } + + public Task> CollectionImageAsync(Guid id, int? width, int? height, EntityTagHeaderValue etag = null) + { + return GetImageFileOperation("CollectionThumbnail", + data.Collection.CacheRegionUrn(id), + id, + width, + height, + async () => { return await CollectionImageAction(id, etag).ConfigureAwait(false); }, + etag); + } + + public Task> GenreImageAsync(Guid id, int? width, int? height, EntityTagHeaderValue etag = null) + { + return GetImageFileOperation("GenreThumbnail", + data.Genre.CacheRegionUrn(id), + id, + width, + height, + async () => await GenreImageAction(id, etag).ConfigureAwait(false), + etag); + } + + public Task> LabelImageAsync(Guid id, int? width, int? height, EntityTagHeaderValue etag = null) + { + return GetImageFileOperation("LabelThumbnail", + data.Label.CacheRegionUrn(id), + id, + width, + height, + async () => await LabelImageAction(id, etag).ConfigureAwait(false), + etag); + } + + public Task> PlaylistImageAsync(Guid id, int? width, int? height, EntityTagHeaderValue etag = null) + { + return GetImageFileOperation("PlaylistThumbnail", + data.Playlist.CacheRegionUrn(id), + id, + width, + height, + async () => await PlaylistImageAction(id, etag).ConfigureAwait(false), + etag); + } + + public Task> ReleaseImageAsync(Guid id, int? width, int? height, EntityTagHeaderValue etag = null) + { + return GetImageFileOperation("ReleaseThumbnail", + data.Release.CacheRegionUrn(id), + id, + width, + height, + async () => await ReleaseImageAction(id, etag).ConfigureAwait(false), + etag); + } + + public Task> ReleaseSecondaryImageAsync(Guid id, int imageId, int? width, int? height, EntityTagHeaderValue etag = null) + { + return GetImageFileOperation($"ReleaseSecondaryThumbnail-{imageId}", + data.Release.CacheRegionUrn(id), + id, + width, + height, + async () => { return await ReleaseSecondaryImageAction(id, imageId, etag).ConfigureAwait(false); }, + etag); + } + + public async Task>> SearchAsync(string query, int resultsCount = 10) + { + var sw = new Stopwatch(); + sw.Start(); + var errors = new List(); + IEnumerable searchResults = null; + try + { + searchResults = await ImageSearchManager.ImageSearch(query).ConfigureAwait(false); + } + catch (Exception ex) + { + Logger.LogError(ex); + errors.Add(ex); + } + + sw.Stop(); + return new OperationResult> + { + Data = searchResults, + IsSuccess = errors.Count == 0, + OperationTime = sw.ElapsedMilliseconds, + Errors = errors + }; + } + + public Task> TrackImageAsync(Guid id, int? width, int? height, EntityTagHeaderValue etag = null) + { + return GetImageFileOperation("TrackThumbnail", + data.Track.CacheRegionUrn(id), + id, + width, + height, + async () => await TrackImageAction(id, width, height, etag).ConfigureAwait(false), + etag); + } + + public Task> UserImageAsync(Guid id, int? width, int? height, EntityTagHeaderValue etag = null) + { + return GetImageFileOperation("UserById", + User.CacheRegionUrn(id), + id, + width, + height, + async () => await UserImageAction(id, etag).ConfigureAwait(false), + etag); + } } } \ No newline at end of file diff --git a/Roadie.Api.Services/LabelService.cs b/Roadie.Api.Services/LabelService.cs index dbcda76..e612211 100644 --- a/Roadie.Api.Services/LabelService.cs +++ b/Roadie.Api.Services/LabelService.cs @@ -9,7 +9,6 @@ using Roadie.Library.Data.Context; using Roadie.Library.Encoding; using Roadie.Library.Enums; using Roadie.Library.Extensions; -using Roadie.Library.Identity; using Roadie.Library.Imaging; using Roadie.Library.Models; using Roadie.Library.Models.Pagination; @@ -43,318 +42,6 @@ namespace Roadie.Api.Services BookmarkService = bookmarkService; } - public async Task> ById(Library.Models.Users.User roadieUser, Guid id, IEnumerable includes = null) - { - var sw = Stopwatch.StartNew(); - sw.Start(); - var cacheKey = string.Format("urn:label_by_id_operation:{0}:{1}", id, - includes == null ? "0" : string.Join("|", includes)); - var result = await CacheManager.GetAsync(cacheKey, - async () => { return await LabelByIdAction(id, includes); }, data.Label.CacheRegionUrn(id)); - sw.Stop(); - if (result?.Data != null && roadieUser != null) - { - var userBookmarkResult = await BookmarkService.List(roadieUser, new PagedRequest(), false, BookmarkType.Label); - if (userBookmarkResult.IsSuccess) - { - result.Data.UserBookmarked = userBookmarkResult?.Rows?.FirstOrDefault(x => x?.Bookmark?.Value == result?.Data?.Id?.ToString()) != null; - } - if (result.Data.Comments.Any()) - { - var commentIds = result.Data.Comments.Select(x => x.DatabaseId).ToArray(); - var userCommentReactions = (from cr in DbContext.CommentReactions - where commentIds.Contains(cr.CommentId) - where cr.UserId == roadieUser.Id - select cr).ToArray(); - foreach (var comment in result.Data.Comments) - { - var userCommentReaction = userCommentReactions.FirstOrDefault(x => x.CommentId == comment.DatabaseId); - comment.IsDisliked = userCommentReaction?.ReactionValue == CommentReaction.Dislike; - comment.IsLiked = userCommentReaction?.ReactionValue == CommentReaction.Like; - } - } - } - - return new OperationResult