From f385c8f6fce2cf3a26e5ce7cab606e574b60ec00 Mon Sep 17 00:00:00 2001 From: Steven Hildreth Date: Sun, 3 Nov 2019 21:19:04 -0600 Subject: [PATCH] resolves #29 --- .../ConfigurationTests.cs | 2 +- .../EncryptionHelperTests.cs | 2 +- Roadie.Api.Library.Tests/ExtensionTests.cs | 16 +- .../ID3TagsHelperTests.cs | 30 +- Roadie.Api.Library.Tests/ImageHasherTests.cs | 2 +- Roadie.Api.Library.Tests/ImageHelperTests.cs | 221 ++++++++++++++- Roadie.Api.Library.Tests/InspectorTests.cs | 18 +- Roadie.Api.Library.Tests/RenumberTests.cs | 64 ++--- .../Roadie.Library.Tests.csproj | 4 + Roadie.Api.Library.Tests/SafeParserTests.cs | 20 +- .../StringExtensionTests.cs | 6 +- Roadie.Api.Library/Data/Artist.cs | 3 +- Roadie.Api.Library/Data/ArtistPartial.cs | 5 + Roadie.Api.Library/Data/Genre.cs | 4 +- Roadie.Api.Library/Data/Image.cs | 4 +- Roadie.Api.Library/Data/ImagePartial.cs | 34 --- Roadie.Api.Library/Data/NamedEntityBase.cs | 4 +- Roadie.Api.Library/Data/Release.cs | 8 +- Roadie.Api.Library/Data/Track.cs | 1 + .../Engines/ArtistLookupEngine.cs | 27 +- .../Engines/ReleaseLookupEngine.cs | 123 +++++---- .../Identity/ApplicationUser.cs | 4 +- .../Imaging/DefaultNotFoundImages.cs | 50 ++-- .../Imaging/IDefaultNotFoundImages.cs | 16 +- Roadie.Api.Library/Imaging/IImage.cs | 21 ++ Roadie.Api.Library/Imaging/Image.cs | 56 ++++ Roadie.Api.Library/Imaging/ImageHasher.cs | 24 +- Roadie.Api.Library/Imaging/ImageHelper.cs | 8 +- Roadie.Api.Library/Models/Image.cs | 9 +- Roadie.Api.Library/Utility/WebHelper.cs | 5 +- Roadie.Api.Services/AdminService.cs | 219 +++++++++++++++ Roadie.Api.Services/ArtistService.cs | 69 +---- Roadie.Api.Services/CollectionService.cs | 6 +- Roadie.Api.Services/GenreService.cs | 15 +- Roadie.Api.Services/IAdminService.cs | 2 + Roadie.Api.Services/IImageService.cs | 25 +- Roadie.Api.Services/ImageService.cs | 261 ++++++++---------- Roadie.Api.Services/LabelService.cs | 19 +- Roadie.Api.Services/PlaylistService.cs | 2 - Roadie.Api.Services/ReleaseService.cs | 68 ++--- Roadie.Api.Services/TrackService.cs | 7 +- Roadie.Api.Services/UserService.cs | 6 - Roadie.Api/Controllers/AdminController.cs | 17 ++ Roadie.Api/Controllers/ImageController.cs | 214 +++++++++----- 44 files changed, 1085 insertions(+), 636 deletions(-) delete mode 100644 Roadie.Api.Library/Data/ImagePartial.cs create mode 100644 Roadie.Api.Library/Imaging/IImage.cs create mode 100644 Roadie.Api.Library/Imaging/Image.cs diff --git a/Roadie.Api.Library.Tests/ConfigurationTests.cs b/Roadie.Api.Library.Tests/ConfigurationTests.cs index 800714a..df5c61b 100644 --- a/Roadie.Api.Library.Tests/ConfigurationTests.cs +++ b/Roadie.Api.Library.Tests/ConfigurationTests.cs @@ -45,7 +45,7 @@ public ConfigurationTests() } [Fact] - public void Load_Root_Level_Configuration() + public void LoadRootLevelConfiguration() { var inboundFolder = @"C:\roadie_dev_root\inbound"; var configInboundFolder = this.Settings.InboundFolder; diff --git a/Roadie.Api.Library.Tests/EncryptionHelperTests.cs b/Roadie.Api.Library.Tests/EncryptionHelperTests.cs index f67713e..9f3966d 100644 --- a/Roadie.Api.Library.Tests/EncryptionHelperTests.cs +++ b/Roadie.Api.Library.Tests/EncryptionHelperTests.cs @@ -9,7 +9,7 @@ namespace Roadie.Library.Tests public class EncryptionHelperTests { [Fact] - public void Encrypt_And_Decrypt() + public void EncryptAndDecrypt() { var key = Guid.NewGuid().ToString(); var value = Guid.NewGuid().ToString(); diff --git a/Roadie.Api.Library.Tests/ExtensionTests.cs b/Roadie.Api.Library.Tests/ExtensionTests.cs index 0690b30..6ea71a6 100644 --- a/Roadie.Api.Library.Tests/ExtensionTests.cs +++ b/Roadie.Api.Library.Tests/ExtensionTests.cs @@ -18,7 +18,7 @@ public class ExtensionTests { [Fact] - public void Shuffle_Unique_Order() + public void ShuffleUniqueOrder() { var tracks = new List { @@ -225,12 +225,12 @@ public void Shuffle_Unique_Order() lastTrack = track; } - Assert.Equal(tracks.Count(), shuffledTracks.Count()); + Assert.Equal(tracks.Count, shuffledTracks.Count()); } [Fact] - public void From_Unix_Time() + public void FromUnixTime() { var dateTime = new DateTime(2015, 05, 24, 10, 2, 0, DateTimeKind.Utc); var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); @@ -240,7 +240,7 @@ public void From_Unix_Time() } [Fact] - public void To_Unix_Time() + public void ToUnixTime() { var dateTime = new DateTime(2015, 05, 24, 10, 2, 0, DateTimeKind.Utc); var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); @@ -254,14 +254,14 @@ public void To_Unix_Time() [InlineData(150, 0)] [InlineData(65000, 65)] [InlineData(143000, 143)] - public void To_Seconds_From_Milliseconds_Decimal(int input, int shouldBe) + public void ToSecondsFromMillisecondsDecimal(int input, int shouldBe) { var d = ((decimal?)input).ToSecondsFromMilliseconds(); Assert.Equal(shouldBe, d); } [Fact] - public void To_Time_Span() + public void ToTimeSpan() { var dateTime = new DateTime(2015, 05, 24, 10, 2, 0, DateTimeKind.Utc); var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); @@ -273,7 +273,7 @@ public void To_Time_Span() } [Fact] - public void Or_Integers() + public void OrIntegers() { int? test = null; int? shouldBe = 5; @@ -285,7 +285,7 @@ public void Or_Integers() [InlineData(150, 0)] [InlineData(65000, 65)] [InlineData(143000, 143)] - public void To_Seconds_From_Milliseconds_Int(int? input, int? shouldBe) + public void ToSecondsFromMillisecondsInt(int? input, int? shouldBe) { var d = input.ToSecondsFromMilliseconds(); Assert.Equal(shouldBe, d); diff --git a/Roadie.Api.Library.Tests/ID3TagsHelperTests.cs b/Roadie.Api.Library.Tests/ID3TagsHelperTests.cs index 759a422..2ce2211 100644 --- a/Roadie.Api.Library.Tests/ID3TagsHelperTests.cs +++ b/Roadie.Api.Library.Tests/ID3TagsHelperTests.cs @@ -32,7 +32,7 @@ private ILogger Logger public ID3TagsHelperTests() { this.MessageLogger = new EventMessageLogger(); - this.MessageLogger.Messages += MessageLogger_Messages; + this.MessageLogger.Messages += MessageLoggerMessages; var settings = new RoadieSettings(); IConfigurationBuilder configurationBuilder = new ConfigurationBuilder(); @@ -43,11 +43,11 @@ public ID3TagsHelperTests() this.Configuration = settings; this.CacheManager = new DictionaryCacheManager(this.Logger, new CachePolicy(TimeSpan.FromHours(4))); var tagHelperLooper = new EventMessageLogger(); - tagHelperLooper.Messages += MessageLogger_Messages; + tagHelperLooper.Messages += MessageLoggerMessages; this.TagsHelper = new ID3TagsHelper(this.Configuration, this.CacheManager, tagHelperLooper); } - private void MessageLogger_Messages(object sender, EventMessage e) + private void MessageLoggerMessages(object sender, EventMessage e) { Console.WriteLine($"Log Level [{ e.Level }] Log Message [{ e.Message }] "); } @@ -205,7 +205,7 @@ public void DetermineTrackNumber(string filename) } [Fact] - public void ReadTotalTrackNumbersFromCue_Should_Be_Five() + public void ReadTotalTrackNumbersFromCueShouldBeFive() { var cuesDir = @"C:\roadie_dev_root\test_cue_and_playlists\cues1"; var directory = new DirectoryInfo(cuesDir); @@ -224,7 +224,7 @@ public void ReadTotalTrackNumbersFromCue_Should_Be_Five() } [Fact] - public void ReadTotalTrackNumbersFromCue_Should_Be_Eight() + public void ReadTotalTrackNumbersFromCueShouldBeEight() { var cuesDir = @"C:\roadie_dev_root\test_cue_and_playlists\cues2"; var directory = new DirectoryInfo(cuesDir); @@ -243,7 +243,7 @@ public void ReadTotalTrackNumbersFromCue_Should_Be_Eight() } [Fact] - public void ReadTotalTrackNumbersFromCue_Should_Be_Six() + public void ReadTotalTrackNumbersFromCueShouldBeSix() { var cuesDir = @"C:\roadie_dev_root\test_cue_and_playlists\cues3"; var directory = new DirectoryInfo(cuesDir); @@ -262,7 +262,7 @@ public void ReadTotalTrackNumbersFromCue_Should_Be_Six() } [Fact] - public void ReadTotalTrackNumbersFromCue_Should_Be_Nine() + public void ReadTotalTrackNumbersFromCueShouldBeNine() { var cuesDir = @"C:\roadie_dev_root\test_cue_and_playlists\cues4"; var directory = new DirectoryInfo(cuesDir); @@ -281,7 +281,7 @@ public void ReadTotalTrackNumbersFromCue_Should_Be_Nine() } [Fact] - public void ReadTotalTrackNumbersFromM3u_Should_Be_Eleven() + public void ReadTotalTrackNumbersFromM3uShouldBeEleven() { var cuesDir = @"C:\roadie_dev_root\test_cue_and_playlists\m3u1"; var directory = new DirectoryInfo(cuesDir); @@ -300,7 +300,7 @@ public void ReadTotalTrackNumbersFromM3u_Should_Be_Eleven() } [Fact] - public void ReadTotalTrackNumbersFromM3u_Should_Be_Four() + public void ReadTotalTrackNumbersFromM3uShouldBeFour() { var cuesDir = @"C:\roadie_dev_root\test_cue_and_playlists\m3u2"; var directory = new DirectoryInfo(cuesDir); @@ -319,7 +319,7 @@ public void ReadTotalTrackNumbersFromM3u_Should_Be_Four() } [Fact] - public void ReadTotalTrackNumbersFromM3u_Should_Be_Eight() + public void ReadTotalTrackNumbersFromM3uShouldBeEight() { var cuesDir = @"C:\roadie_dev_root\test_cue_and_playlists\m3u3"; var directory = new DirectoryInfo(cuesDir); @@ -338,7 +338,7 @@ public void ReadTotalTrackNumbersFromM3u_Should_Be_Eight() } [Fact] - public void ReadTotalTrackNumbersFromM3u_Should_Be_Fourteen() + public void ReadTotalTrackNumbersFromM3uShouldBeFourteen() { var cuesDir = @"C:\roadie_dev_root\test_cue_and_playlists\m3u4"; var directory = new DirectoryInfo(cuesDir); @@ -545,7 +545,7 @@ public void ReadID3TagsFromFileWithAlbumNoTrackSet() } [Fact] - public void Read_File_Test_Is_valid() + public void ReadFileTestIsvalid() { 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) @@ -572,7 +572,7 @@ public void Read_File_Test_Is_valid() } [Fact] - public void Read_File_Test_Is_valid2() + public void ReadFileTestIsvalid2() { var file = new FileInfo(@"M:\library_old\Perverse\[2014] Champion Dub\01 Champion Dub (Original Mix).mp3"); if (file.Exists) @@ -599,7 +599,7 @@ public void Read_File_Test_Is_valid2() } [Fact] - public void Read_File_Test_Is_valid3() + public void ReadFileTestIsvalid3() { var file = new FileInfo(@"C:\roadie_dev_root\inbound\Dreadful Fate - Vengeance (2018)\01-dreadful_fate-vengeance.mp3"); if (file.Exists) @@ -685,7 +685,7 @@ public void ReadID3TagsFromFileWithArtistAndTrackArtist() } [Fact] - public void Write_Tags() + public void WriteTags() { var file = new FileInfo(@"C:\roadie_dev_root\inbound\temp\01. Re1nstall 0verture.mp3"); if (file.Exists) diff --git a/Roadie.Api.Library.Tests/ImageHasherTests.cs b/Roadie.Api.Library.Tests/ImageHasherTests.cs index 684469e..8ce56c9 100644 --- a/Roadie.Api.Library.Tests/ImageHasherTests.cs +++ b/Roadie.Api.Library.Tests/ImageHasherTests.cs @@ -7,7 +7,7 @@ namespace Roadie.Library.Tests public class ImageHasherTests { [Fact] - public void Generate_Image_Hash() + public void GenerateImageHash() { var imageFilename = @"C:\temp\image_tests\1.jpg"; var secondImagFilename = @"C:\temp\image_tests\2.jpg"; diff --git a/Roadie.Api.Library.Tests/ImageHelperTests.cs b/Roadie.Api.Library.Tests/ImageHelperTests.cs index 3f3d770..17c1e17 100644 --- a/Roadie.Api.Library.Tests/ImageHelperTests.cs +++ b/Roadie.Api.Library.Tests/ImageHelperTests.cs @@ -1,5 +1,11 @@ -using Roadie.Library.FilePlugins; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Roadie.Library.Configuration; +using Roadie.Library.Data; +using Roadie.Library.FilePlugins; using Roadie.Library.Imaging; +using System; +using System.Diagnostics; using System.IO; using System.Linq; using Xunit; @@ -20,7 +26,7 @@ public class ImageHelperTests [InlineData("GrOup.jpg")] [InlineData("photo.jpg")] [InlineData("aRtist.jpg")] - public void Test_Should_Be_Artist_Images(string input) + public void TestShouldBeArtistImages(string input) { Assert.True(ImageHelper.IsArtistImage(new FileInfo(input))); } @@ -42,7 +48,7 @@ public void Test_Should_Be_Artist_Images(string input) [InlineData("band 1.jpg")] [InlineData("photo 1.jpg")] [InlineData("photo1.jpg")] - public void Test_Should_Be_Artist_Secondary_Images(string input) + public void TestShouldBeArtistSecondaryImages(string input) { Assert.True(ImageHelper.IsArtistSecondaryImage(new FileInfo(input))); } @@ -74,7 +80,7 @@ public void Test_Should_Be_Artist_Secondary_Images(string input) [InlineData("BIG.JPg")] [InlineData("bigart.JPg")] [InlineData("BIG.PNG")] - public void Test_Should_Be_Release_Images(string input) + public void TestShouldBeReleaseImages(string input) { Assert.True(ImageHelper.IsReleaseImage(new FileInfo(input))); } @@ -102,7 +108,7 @@ public void Test_Should_Be_Release_Images(string input) [InlineData("artist 1.jpg")] [InlineData("artist_01.jpg")] [InlineData("artist 03.jpg")] - public void Test_Should_Not_Be_Artist_Images(string input) + public void TestShouldNotBeArtistImages(string input) { var t = ImageHelper.IsArtistImage(new FileInfo(input)); Assert.False(t); @@ -127,7 +133,7 @@ public void Test_Should_Not_Be_Artist_Images(string input) [InlineData("cover_01.jpg")] [InlineData("cover 03.jpg")] [InlineData("Dixieland-Front1.jpg")] - public void Test_Should_Not_Be_Release_Images(string input) + public void TestShouldNotBeReleaseImages(string input) { Assert.False(ImageHelper.IsReleaseImage(new FileInfo(input))); } @@ -142,7 +148,7 @@ public void Test_Should_Not_Be_Release_Images(string input) [InlineData("record_label.jpg")] [InlineData("RecordLabel.jpg")] [InlineData("RECORDLABEL.JPG")] - public void Test_Should_Be_Label_Images(string input) + public void TestShouldBeLabelImages(string input) { Assert.True(ImageHelper.IsLabelImage(new FileInfo(input))); } @@ -165,7 +171,7 @@ public void Test_Should_Be_Label_Images(string input) [InlineData("Release.JPG")] [InlineData("front.jpg")] [InlineData("FrOnt.jpg")] - public void Test_Should_NotBe_Label_Images(string input) + public void TestShouldNotBeLabelImages(string input) { Assert.False(ImageHelper.IsLabelImage(new FileInfo(input))); } @@ -234,7 +240,7 @@ public void Test_Should_NotBe_Label_Images(string input) [InlineData("Matrix-1.jpg")] [InlineData("Matrix 1.jpg")] [InlineData("IMG_20160921_0004.jpg")] - public void Test_Should_Be_Release_Secondary_Images(string input) + public void TestShouldBeReleaseSecondaryImages(string input) { Assert.True(ImageHelper.IsReleaseSecondaryImage(new FileInfo(input))); } @@ -265,13 +271,13 @@ public void Test_Should_Be_Release_Secondary_Images(string input) [InlineData("record_label.jpg")] [InlineData("RecordLabel.jpg")] [InlineData("RECORDLABEL.JPG")] - public void Test_Should_Not_Be_Release_Secondary_Images(string input) + public void TestShouldNotBeReleaseSecondaryImages(string input) { Assert.False(ImageHelper.IsReleaseSecondaryImage(new FileInfo(input))); } [Fact] - public void Get_Release_Image_In_Folder() + public void GetReleaseImageInFolder() { var folder = new DirectoryInfo(@"C:\roadie_dev_root\image_tests"); if(!folder.Exists) @@ -287,7 +293,7 @@ public void Get_Release_Image_In_Folder() } [Fact] - public void Get_Artist_Image_In_Folder() + public void GetArtistImageInFolder() { var folder = new DirectoryInfo(@"C:\roadie_dev_root\image_tests)"); if (!folder.Exists) @@ -300,5 +306,196 @@ public void Get_Artist_Image_In_Folder() Assert.Single(artist); Assert.Equal("artist.jpg", artist.First().Name); } + + [Fact] + public void ExtractImagesFromDatabase() + { +#pragma warning disable CS0618 // Type or member is obsolete + var now = DateTime.UtcNow; + var optionsBuilder = new DbContextOptionsBuilder(); + optionsBuilder.UseMySql("server=viking;userid=roadie;password=MenAtW0rk668;persistsecurityinfo=True;database=roadie_dev;ConvertZeroDateTime=true"); + + var settings = new RoadieSettings(); + IConfigurationBuilder configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddJsonFile("appsettings.test.json"); + IConfiguration configuration = configurationBuilder.Build(); + configuration.GetSection("RoadieSettings").Bind(settings); + settings.ConnectionString = configuration.GetConnectionString("RoadieDatabaseConnection"); + + using (var context = new RoadieDbContext(optionsBuilder.Options)) + { + foreach (var artist in context.Artists.Where(x => x.Thumbnail != null).OrderBy(x => x.SortName ?? x.Name)) + { + var artistFolder = artist.ArtistFileFolder(settings); + if (!Directory.Exists(artistFolder)) + { + Directory.CreateDirectory(artistFolder); + } + var artistImage = Path.Combine(artistFolder, ImageHelper.ArtistImageFilename); + if (!File.Exists(artistImage)) + { + File.WriteAllBytes(artistImage, ImageHelper.ConvertToJpegFormat(artist.Thumbnail)); + } + artist.Thumbnail = null; + artist.LastUpdated = now; + Trace.WriteLine($"Saved Artist Image `{artist}` path [{ artistImage }]"); + } + context.SaveChanges(); + + var artistImages = (from i in context.Images + join a in context.Artists on i.ArtistId equals a.Id + select new { i, a}); + foreach(var artistImage in artistImages) + { + var looper = 0; + var artistFolder = artistImage.a.ArtistFileFolder(settings); + var artistImageFilename = Path.Combine(artistFolder, string.Format(ImageHelper.ArtistSecondaryImageFilename, looper.ToString("00"))); + while (File.Exists(artistImageFilename)) + { + looper++; + artistImageFilename = Path.Combine(artistFolder, string.Format(ImageHelper.ArtistSecondaryImageFilename, looper.ToString("00"))); + } + File.WriteAllBytes(artistImageFilename, ImageHelper.ConvertToJpegFormat(artistImage.i.Bytes)); + context.Images.Remove(artistImage.i); + Trace.WriteLine($"Saved Artist Secondary Image `{artistImage.a}` path [{ artistImageFilename }]"); + } + context.SaveChanges(); + + foreach (var collection in context.Collections.Where(x => x.Thumbnail != null).OrderBy(x => x.SortName ?? x.Name)) + { + var image = collection.PathToImage(settings); + if (!File.Exists(image)) + { + File.WriteAllBytes(image, ImageHelper.ConvertToJpegFormat(collection.Thumbnail)); + } + collection.Thumbnail = null; + collection.LastUpdated = now; + Trace.WriteLine($"Saved Collection Image `{collection}` path [{ image }]"); + } + context.SaveChanges(); + + foreach (var genre in context.Genres.Where(x => x.Thumbnail != null).OrderBy(x => x.Name)) + { + var image = genre.PathToImage(settings); + if (!File.Exists(image)) + { + File.WriteAllBytes(image, ImageHelper.ConvertToJpegFormat(genre.Thumbnail)); + } + genre.Thumbnail = null; + genre.LastUpdated = now; + Trace.WriteLine($"Saved Genre Image `{genre}` path [{ image }]"); + } + context.SaveChanges(); + + foreach (var label in context.Labels.Where(x => x.Thumbnail != null).OrderBy(x => x.SortName ?? x.Name)) + { + var image = label.PathToImage(settings); + if (!File.Exists(image)) + { + File.WriteAllBytes(image, ImageHelper.ConvertToJpegFormat(label.Thumbnail)); + } + label.Thumbnail = null; + label.LastUpdated = now; + Trace.WriteLine($"Saved Label Image `{label}` path [{ image }]"); + } + context.SaveChanges(); + + foreach (var playlist in context.Playlists.Where(x => x.Thumbnail != null).OrderBy(x => x.Name)) + { + var image = playlist.PathToImage(settings); + if (!File.Exists(image)) + { + File.WriteAllBytes(image, ImageHelper.ConvertToJpegFormat(playlist.Thumbnail)); + } + playlist.Thumbnail = null; + playlist.LastUpdated = now; + Trace.WriteLine($"Saved Playlist Image `{playlist}` path [{ image }]"); + } + context.SaveChanges(); + + foreach (var release in context.Releases.Include(x => x.Artist).Where(x => x.Thumbnail != null).OrderBy(x => x.Title)) + { + var artistFolder = release.Artist.ArtistFileFolder(settings); + var releaseFolder = release.ReleaseFileFolder(artistFolder); + if (!Directory.Exists(releaseFolder)) + { + Directory.CreateDirectory(artistFolder); + } + var releaseImage = Path.Combine(releaseFolder, "cover.jpg"); + if (!File.Exists(releaseImage)) + { + File.WriteAllBytes(releaseImage, ImageHelper.ConvertToJpegFormat(release.Thumbnail)); + } + release.Thumbnail = null; + release.LastUpdated = now; + Trace.WriteLine($"Saved Release Image `{release}` path [{ releaseImage }]"); + } + context.SaveChanges(); + + var releaseImages = (from i in context.Images + join r in context.Releases.Include(x => x.Artist) on i.ReleaseId equals r.Id + select new { i, r }); + foreach (var releaseImage in releaseImages) + { + var looper = 0; + var artistFolder = releaseImage.r.Artist.ArtistFileFolder(settings); + var releaseFolder = releaseImage.r.ReleaseFileFolder(artistFolder); + var releaseImageFilename = Path.Combine(artistFolder, string.Format(ImageHelper.ReleaseSecondaryImageFilename, looper.ToString("00"))); + while (File.Exists(releaseImageFilename)) + { + looper++; + releaseImageFilename = Path.Combine(artistFolder, string.Format(ImageHelper.ReleaseSecondaryImageFilename, looper.ToString("00"))); + } + File.WriteAllBytes(releaseImageFilename, ImageHelper.ConvertToJpegFormat(releaseImage.i.Bytes)); + context.Images.Remove(releaseImage.i); + Trace.WriteLine($"Saved Release Secondary Image `{releaseImage.r}` path [{ releaseImageFilename }]"); + } + context.SaveChanges(); + + + foreach (var track in context.Tracks.Include(x => x.ReleaseMedia) + .Include(x => x.ReleaseMedia.Release) + .Include(x => x.ReleaseMedia.Release.Artist) + .Where(x => x.Thumbnail != null).OrderBy(x => x.Title)) + { + var artistFolder = track.ReleaseMedia.Release.Artist.ArtistFileFolder(settings); + if (!Directory.Exists(artistFolder)) + { + Directory.CreateDirectory(artistFolder); + } + var releaseFolder = track.ReleaseMedia.Release.ReleaseFileFolder(artistFolder); + if (!Directory.Exists(releaseFolder)) + { + Directory.CreateDirectory(releaseFolder); + } + var trackImage = track.PathToTrackThumbnail(settings); + if (!File.Exists(trackImage)) + { + File.WriteAllBytes(trackImage, ImageHelper.ConvertToJpegFormat(track.Thumbnail)); + } + track.Thumbnail = null; + track.LastUpdated = now; + Trace.WriteLine($"Saved Track Image `{track}` path [{ trackImage }]"); + } + context.SaveChanges(); + + foreach (var user in context.Users.Where(x => x.Avatar != null).OrderBy(x => x.UserName)) + { + var image = user.PathToImage(settings); + if (!File.Exists(image)) + { + File.WriteAllBytes(image, ImageHelper.ConvertToJpegFormat(user.Avatar)); + } + user.Avatar = null; + user.LastUpdated = now; + Trace.WriteLine($"Saved User Image `{user}` path [{ image }]"); + } + context.SaveChanges(); + + + } +#pragma warning restore CS0618 // Type or member is obsolete + + } } } \ No newline at end of file diff --git a/Roadie.Api.Library.Tests/InspectorTests.cs b/Roadie.Api.Library.Tests/InspectorTests.cs index 59e8ac0..013a0b7 100644 --- a/Roadie.Api.Library.Tests/InspectorTests.cs +++ b/Roadie.Api.Library.Tests/InspectorTests.cs @@ -36,7 +36,7 @@ private ILogger Logger public InspectorTests() { this.MessageLogger = new EventMessageLogger(); - this.MessageLogger.Messages += MessageLogger_Messages; + this.MessageLogger.Messages += MessageLoggerMessages; var settings = new configuration.RoadieSettings(); IConfigurationBuilder configurationBuilder = new ConfigurationBuilder(); @@ -47,11 +47,11 @@ public InspectorTests() this.Configuration = settings; this.CacheManager = new DictionaryCacheManager(this.Logger, new CachePolicy(TimeSpan.FromHours(4))); var tagHelperLooper = new EventMessageLogger(); - tagHelperLooper.Messages += MessageLogger_Messages; + tagHelperLooper.Messages += MessageLoggerMessages; this.TagsHelper = new ID3TagsHelper(this.Configuration, this.CacheManager, tagHelperLooper); } - private void MessageLogger_Messages(object sender, EventMessage e) + private void MessageLoggerMessages(object sender, EventMessage e) { Console.WriteLine($"Log Level [{ e.Level }] Log Message [{ e.Message }] "); } @@ -67,7 +67,7 @@ private void MessageLogger_Messages(object sender, EventMessage e) [InlineData("Bob Jones ", "Bob Jones")] [InlineData(" BoB Jones ", "Bob Jones")] [InlineData("Ain't NO THING", "Ain't No Thing")] - public void Clean_Artist_Plugin(string artist, string shouldBe) + public void CleanArtistPlugin(string artist, string shouldBe) { var plugin = new CleanUpArtists(Configuration, CacheManager, Logger, TagsHelper); Assert.Equal(shouldBe, plugin.CleanArtist(artist)); @@ -75,7 +75,7 @@ public void Clean_Artist_Plugin(string artist, string shouldBe) [Theory] [InlineData("Ain't NO THING", "Ain't No Thing")] - public void Clean_TrackTitle_Plugin(string title, string shouldBe) + public void CleanTrackTitlePlugin(string title, string shouldBe) { var plugin = new CleanUpTrackTitle(Configuration, CacheManager, Logger, TagsHelper); var result = plugin.Process(new MetaData.Audio.AudioMetaData { Title = title}); @@ -84,7 +84,7 @@ public void Clean_TrackTitle_Plugin(string title, string shouldBe) } [Fact] - public void Clean_Artist_Plugin_Remove_Artist_From_Track_Artist() + public void CleanArtistPluginRemoveArtistFromTrackArtist() { var artist = "Bob Jones"; var trackArtist = "Bob Jones;Mary Jones"; @@ -98,14 +98,14 @@ public void Clean_Artist_Plugin_Remove_Artist_From_Track_Artist() [Theory] [InlineData("Bob Jones")] [InlineData("Nancy Jones")] - public void Generate_Inspector_Artist_Token(string artist) + public void GenerateInspectorArtistToken(string artist) { var token = Inspector.ArtistInspectorToken(new MetaData.Audio.AudioMetaData { Artist = artist }); Assert.NotNull(token); } [Fact] - public void Should_Generate_Same_Token_Value() + public void ShouldGenerateSameTokenValue() { var md = new MetaData.Audio.AudioMetaData { Artist = "Omniversum Fractum", Release = "Paradigm Of The Elementals Essence" }; var artistToken = Inspector.ArtistInspectorToken(md); @@ -122,7 +122,7 @@ public void Should_Generate_Same_Token_Value() } [Fact] - public void Generate_Inspector_Tokens_Artist_And_Release_Unique() + public void GenerateInspectorTokensArtistAndReleaseUnique() { var md = new MetaData.Audio.AudioMetaData { Artist = "Bob Jones", Release = "Bob's First Release" }; var artistToken = Inspector.ArtistInspectorToken(md); diff --git a/Roadie.Api.Library.Tests/RenumberTests.cs b/Roadie.Api.Library.Tests/RenumberTests.cs index d1ef6fa..960ac7c 100644 --- a/Roadie.Api.Library.Tests/RenumberTests.cs +++ b/Roadie.Api.Library.Tests/RenumberTests.cs @@ -20,7 +20,7 @@ public class RenumberTests [InlineData(@"2003 - Accelerated Evolution\CD 001\01 - Depth Charge.mp3")] [InlineData(@"Accelerated Evolution CD01\22 - Depth Charge.mp3")] [InlineData(@"Accelerated Evolution CD1\22 - Depth Charge.mp3")] - public void Find_Disc_Number_Should_Be_One(string filename) + public void FindDiscNumberShouldBeOne(string filename) { var n = ID3TagsHelper.DetermineDiscNumber(new AudioMetaData { Filename = filename }); Assert.Equal(1, n); @@ -35,24 +35,24 @@ public void Find_Disc_Number_Should_Be_One(string filename) [InlineData(@"2003 - Accelerated Evolution\CD 002\22 - Depth Charge.mp3")] [InlineData(@"Accelerated Evolution CD2\22 - Depth Charge.mp3")] [InlineData(@"Accelerated Evolution CD02\22 - Depth Charge.mp3")] - public void Find_Disc_Number_Should_Be_Two(string filename) + public void FindDiscNumberShouldBeTwo(string filename) { var n = ID3TagsHelper.DetermineDiscNumber(new AudioMetaData { Filename = filename }); Assert.Equal(2, n); } [Fact] - public void Find_Total_Discs_Should_Be_One() + public void FindTotalDiscsShouldBeOne() { var three = new List { new AudioMetaData { - Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\01 - Depth Charge.mp3" + Filename = @"C:\roadiedevroot\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\01 - Depth Charge.mp3" }, new AudioMetaData { - Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\02 - Not A Depth Charge.mp3" + Filename = @"C:\roadiedevroot\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\02 - Not A Depth Charge.mp3" } }; var n = ID3TagsHelper.DetermineTotalDiscNumbers(three); @@ -62,11 +62,11 @@ public void Find_Total_Discs_Should_Be_One() { new AudioMetaData { - Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD1\01 - Depth Charge.mp3" + Filename = @"C:\roadiedevroot\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD1\01 - Depth Charge.mp3" }, new AudioMetaData { - Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD1\02 - Not A Depth Charge.mp3" + Filename = @"C:\roadiedevroot\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD1\02 - Not A Depth Charge.mp3" } }; n = ID3TagsHelper.DetermineTotalDiscNumbers(three); @@ -74,29 +74,29 @@ public void Find_Total_Discs_Should_Be_One() } [Fact] - public void Find_Total_Discs_Should_Be_Ten() + public void FindTotalDiscsShouldBeTen() { var three = new List { new AudioMetaData { - Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 01\01 - Depth Charge.mp3" + Filename = @"C:\roadiedevroot\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 01\01 - Depth Charge.mp3" }, new AudioMetaData { - Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 02\02 - Not A Depth Charge.mp3" + Filename = @"C:\roadiedevroot\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 02\02 - Not A Depth Charge.mp3" }, new AudioMetaData { - Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 04\01 - First.mp3" + Filename = @"C:\roadiedevroot\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 04\01 - First.mp3" }, new AudioMetaData { - Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 06\02 - Second.mp3" + Filename = @"C:\roadiedevroot\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 06\02 - Second.mp3" }, new AudioMetaData { - Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 10\01 - Depth Charge.mp3" + Filename = @"C:\roadiedevroot\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 10\01 - Depth Charge.mp3" } }; var n = ID3TagsHelper.DetermineTotalDiscNumbers(three); @@ -104,41 +104,41 @@ public void Find_Total_Discs_Should_Be_Ten() } [Fact] - public void Find_Total_Discs_Should_Be_Three() + public void FindTotalDiscsShouldBeThree() { var three = new List { new AudioMetaData { - Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 01\01 - Depth Charge.mp3" + Filename = @"C:\roadiedevroot\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 01\01 - Depth Charge.mp3" }, new AudioMetaData { - Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 01\02 - Not A Depth Charge.mp3" + Filename = @"C:\roadiedevroot\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 01\02 - Not A Depth Charge.mp3" }, new AudioMetaData { - Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 02\01 - First.mp3" + Filename = @"C:\roadiedevroot\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 02\01 - First.mp3" }, new AudioMetaData { - Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 02\02 - Second.mp3" + Filename = @"C:\roadiedevroot\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 02\02 - Second.mp3" }, new AudioMetaData { - Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 03\01 - Depth Charge.mp3" + Filename = @"C:\roadiedevroot\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 03\01 - Depth Charge.mp3" }, new AudioMetaData { - Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 03\02 - Depth Charge.mp3" + Filename = @"C:\roadiedevroot\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 03\02 - Depth Charge.mp3" }, new AudioMetaData { - Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 03\03 - Depth Charge.mp3" + Filename = @"C:\roadiedevroot\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 03\03 - Depth Charge.mp3" }, new AudioMetaData { - Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 03\04 - Depth Charge.mp3" + Filename = @"C:\roadiedevroot\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 03\04 - Depth Charge.mp3" } }; var n = ID3TagsHelper.DetermineTotalDiscNumbers(three); @@ -146,29 +146,29 @@ public void Find_Total_Discs_Should_Be_Three() } [Fact] - public void Find_Total_Discs_Should_Be_Two() + public void FindTotalDiscsShouldBeTwo() { var three = new List { new AudioMetaData { - Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 01\01 - Depth Charge.mp3" + Filename = @"C:\roadiedevroot\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 01\01 - Depth Charge.mp3" }, new AudioMetaData { - Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 01\02 - Not A Depth Charge.mp3" + Filename = @"C:\roadiedevroot\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 01\02 - Not A Depth Charge.mp3" }, new AudioMetaData { - Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 01\01 - First.mp3" + Filename = @"C:\roadiedevroot\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 01\01 - First.mp3" }, new AudioMetaData { - Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 02\02 - Second.mp3" + Filename = @"C:\roadiedevroot\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 02\02 - Second.mp3" }, new AudioMetaData { - Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 01\01 - Depth Charge.mp3" + Filename = @"C:\roadiedevroot\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 01\01 - Depth Charge.mp3" } }; var n = ID3TagsHelper.DetermineTotalDiscNumbers(three); @@ -178,11 +178,11 @@ public void Find_Total_Discs_Should_Be_Two() { new AudioMetaData { - Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD0\01 - Depth Charge.mp3" + Filename = @"C:\roadiedevroot\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD0\01 - Depth Charge.mp3" }, new AudioMetaData { - Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD2\02 - Not A Depth Charge.mp3" + Filename = @"C:\roadiedevroot\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD2\02 - Not A Depth Charge.mp3" } }; n = ID3TagsHelper.DetermineTotalDiscNumbers(three); @@ -192,11 +192,11 @@ public void Find_Total_Discs_Should_Be_Two() { new AudioMetaData { - Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 1\01 - Depth Charge.mp3" + Filename = @"C:\roadiedevroot\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 1\01 - Depth Charge.mp3" }, new AudioMetaData { - Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 2\02 - Not A Depth Charge.mp3" + Filename = @"C:\roadiedevroot\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 2\02 - Not A Depth Charge.mp3" } }; n = ID3TagsHelper.DetermineTotalDiscNumbers(three); diff --git a/Roadie.Api.Library.Tests/Roadie.Library.Tests.csproj b/Roadie.Api.Library.Tests/Roadie.Library.Tests.csproj index 26c775c..f580f23 100644 --- a/Roadie.Api.Library.Tests/Roadie.Library.Tests.csproj +++ b/Roadie.Api.Library.Tests/Roadie.Library.Tests.csproj @@ -19,6 +19,10 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/Roadie.Api.Library.Tests/SafeParserTests.cs b/Roadie.Api.Library.Tests/SafeParserTests.cs index f756a26..562d922 100644 --- a/Roadie.Api.Library.Tests/SafeParserTests.cs +++ b/Roadie.Api.Library.Tests/SafeParserTests.cs @@ -12,7 +12,7 @@ public class SafeParserTests [InlineData("1")] [InlineData("01")] [InlineData("001")] - public void Parse_Int_ShouldBeOne(string input) + public void ParseIntShouldBeOne(string input) { var parsed = SafeParser.ToNumber(input); Assert.Equal(1, parsed); @@ -22,7 +22,7 @@ public void Parse_Int_ShouldBeOne(string input) [InlineData("")] [InlineData("0")] [InlineData("00")] - public void Parse_Int_ShouldBeZero(string input) + public void ParseIntShouldBeZero(string input) { var parsed = SafeParser.ToNumber(input); Assert.Equal(0, parsed); @@ -31,7 +31,7 @@ public void Parse_Int_ShouldBeZero(string input) [Fact] - public void Parse_Larger_Int() + public void ParseLargerInt() { var n = "12345"; int n1 = 12345; @@ -40,7 +40,7 @@ public void Parse_Larger_Int() } [Fact] - public void Parse_NullablleInt() + public void ParseNullablleInt() { var one = "1"; var parsed = SafeParser.ToNumber(one); @@ -56,7 +56,7 @@ public void Parse_NullablleInt() } [Fact] - public void Parse_Short() + public void ParseShort() { var n = "23"; short n1 = 23; @@ -84,7 +84,7 @@ public void Parse_Short() [InlineData("88")] [InlineData("1988-06-15T07:00:00Z")] [InlineData("1988-07-14T07:00:00Z")] - public void Parse_Datetime_ShouldBe1988(string input) + public void ParseDatetimeShouldBe1988(string input) { var parsed = SafeParser.ToDateTime(input); Assert.NotNull(parsed); @@ -99,7 +99,7 @@ public void Parse_Datetime_ShouldBe1988(string input) [InlineData("2004////2004")] [InlineData("2004\\2004")] [InlineData("2004\\\\2004")] - public void Parse_Datetime_ShouldBe2004(string input) + public void ParseDatetimeShouldBe2004(string input) { var parsed = SafeParser.ToDateTime(input); Assert.NotNull(parsed); @@ -111,7 +111,7 @@ public void Parse_Datetime_ShouldBe2004(string input) [InlineData("12d65c61-1b7d-4c43-9aab-7d398a1a880e")] [InlineData("A:8a951bc1-5ee5-4961-b72a-99d91d84c147")] [InlineData("R:0327eea7-b1cb-4ae9-9eb1-b74b4416aefb")] - public void Parse_Guid(string input) + public void ParseGuid(string input) { var parsed = SafeParser.ToGuid(input); Assert.NotNull(parsed); @@ -125,7 +125,7 @@ public void Parse_Guid(string input) [InlineData("Yes")] [InlineData("YES")] [InlineData("1")] - public void Parse_Boolean_ShouldBeTrue(string input) + public void ParseBooleanShouldBeTrue(string input) { var parsed = SafeParser.ToBoolean(input); Assert.True(parsed); @@ -142,7 +142,7 @@ public void Parse_Boolean_ShouldBeTrue(string input) [InlineData("No")] [InlineData("NO")] [InlineData("0")] - public void Parse_Boolean_ShouldBeFalse(string input) + public void ParseBooleanShouldBeFalse(string input) { var parsed = SafeParser.ToBoolean(input); Assert.False(parsed); diff --git a/Roadie.Api.Library.Tests/StringExtensionTests.cs b/Roadie.Api.Library.Tests/StringExtensionTests.cs index e4472c5..df07174 100644 --- a/Roadie.Api.Library.Tests/StringExtensionTests.cs +++ b/Roadie.Api.Library.Tests/StringExtensionTests.cs @@ -99,7 +99,7 @@ public void CleanString(string input, string shouldBe) [InlineData("Angie (2004 Remastered)")] [InlineData("Angie (Japan Ltd Dig")] [InlineData("Angie (Japan Release)")] - public void CleanString_Release_Should_Be_Angie(string input) + 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); @@ -117,7 +117,7 @@ public void CleanString_Release_Should_Be_Angie(string input) [InlineData("Love.Mp3")] [InlineData("LOVE.Mp3")] [InlineData("love.mp3")] - public void CleanString_Track(string input) + public void CleanStringTrack(string input) { Assert.Equal("Love.Mp3", input.CleanString(Configuration, Configuration.Processing.TrackRemoveStringsRegex).ToTitleCase()); } @@ -226,7 +226,7 @@ public void StripStartingWithNumber(string input, string shouldBe) [InlineData("49 Batman Loves Robin")] [InlineData("54 Batman Loves Robin")] [InlineData("348 Batman Loves Robin")] - public void Test_Regex_String(string input) + public void TestRegexString(string input) { var t1 = Regex.Replace(input, "^([0-9]+)(\\.|-|\\s)*", ""); Assert.NotNull(t1); diff --git a/Roadie.Api.Library/Data/Artist.cs b/Roadie.Api.Library/Data/Artist.cs index 35f11b0..d9910ad 100644 --- a/Roadie.Api.Library/Data/Artist.cs +++ b/Roadie.Api.Library/Data/Artist.cs @@ -1,4 +1,5 @@ using Roadie.Library.Enums; +using Roadie.Library.Imaging; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; @@ -32,8 +33,6 @@ public partial class Artist : BeginAndEndNamedEntityBase public ICollection Genres { get; set; } - public ICollection Images { get; set; } - [Column("isniList", TypeName = "text")] [MaxLength(65535)] public string ISNI { get; set; } diff --git a/Roadie.Api.Library/Data/ArtistPartial.cs b/Roadie.Api.Library/Data/ArtistPartial.cs index b4dac8c..253f870 100644 --- a/Roadie.Api.Library/Data/ArtistPartial.cs +++ b/Roadie.Api.Library/Data/ArtistPartial.cs @@ -1,6 +1,8 @@ using Roadie.Library.Configuration; using Roadie.Library.Utility; using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using System.Security.Cryptography; @@ -20,6 +22,9 @@ public string Etag } } + [NotMapped] + public ICollection Images { get; set; } + public bool IsNew => Id < 1; public bool IsValid => !string.IsNullOrEmpty(Name); diff --git a/Roadie.Api.Library/Data/Genre.cs b/Roadie.Api.Library/Data/Genre.cs index 92a6997..e277819 100644 --- a/Roadie.Api.Library/Data/Genre.cs +++ b/Roadie.Api.Library/Data/Genre.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; @@ -25,6 +26,7 @@ public partial class Genre : EntityBase [MaxLength(65535)] public string Tags { get; set; } + [Obsolete("Images moved to file system")] [Column("thumbnail", TypeName = "blob")] [MaxLength(65535)] public byte[] Thumbnail { get; set; } diff --git a/Roadie.Api.Library/Data/Image.cs b/Roadie.Api.Library/Data/Image.cs index debaa72..7ccbb83 100644 --- a/Roadie.Api.Library/Data/Image.cs +++ b/Roadie.Api.Library/Data/Image.cs @@ -1,8 +1,10 @@ -using System.ComponentModel.DataAnnotations; +using System; +using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace Roadie.Library.Data { + [Obsolete("Only here for transition. Will be removed in future release. Use Library.Imaging.Image")] [Table("image")] public partial class Image : EntityBase { diff --git a/Roadie.Api.Library/Data/ImagePartial.cs b/Roadie.Api.Library/Data/ImagePartial.cs deleted file mode 100644 index 69a4d02..0000000 --- a/Roadie.Api.Library/Data/ImagePartial.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Roadie.Library.Imaging; -using System; -using System.Linq; - -namespace Roadie.Library.Data -{ - public partial class Image - { - public string CacheKey => Artist.CacheUrn(RoadieId); - - public string CacheRegion => CacheRegionUrn(RoadieId); - - public static string CacheRegionUrn(Guid Id) - { - return string.Format("urn:image:{0}", Id); - } - - public static string CacheUrn(Guid Id) - { - return $"urn:image_by_id:{Id}"; - } - - public string GenerateSignature() - { - if (Bytes == null || !Bytes.Any()) return null; - return ImageHasher.AverageHash(Bytes).ToString(); - } - - public override string ToString() - { - return $"Id [{Id}], RoadieId [{RoadieId}]"; - } - } -} \ No newline at end of file diff --git a/Roadie.Api.Library/Data/NamedEntityBase.cs b/Roadie.Api.Library/Data/NamedEntityBase.cs index 8faaffb..4c0dea5 100644 --- a/Roadie.Api.Library/Data/NamedEntityBase.cs +++ b/Roadie.Api.Library/Data/NamedEntityBase.cs @@ -1,4 +1,5 @@ -using System.ComponentModel.DataAnnotations; +using System; +using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace Roadie.Library.Data @@ -17,6 +18,7 @@ public abstract class NamedEntityBase : EntityBase [MaxLength(65535)] public string Tags { get; set; } + [Obsolete("Images moved to file system")] [Column("thumbnail", TypeName = "blob")] [MaxLength(65535)] public byte[] Thumbnail { get; set; } diff --git a/Roadie.Api.Library/Data/Release.cs b/Roadie.Api.Library/Data/Release.cs index c9dabe2..007d1c5 100644 --- a/Roadie.Api.Library/Data/Release.cs +++ b/Roadie.Api.Library/Data/Release.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; namespace Roadie.Library.Data { @@ -29,7 +30,10 @@ public partial class Release : EntityBase public ICollection Genres { get; set; } - public ICollection Images { get; set; } + [NotMapped] + public IEnumerable Images { get; set; } = Enumerable.Empty(); + + public Imaging.IImage ThumbnailImage => Images.OrderBy(x => x.SortOrder).FirstOrDefault(); [Column("isVirtual")] public bool? IsVirtual { get; set; } @@ -77,6 +81,7 @@ public partial class Release : EntityBase [MaxLength(65535)] public string Tags { get; set; } + [Obsolete("Images moved to file system")] [Column("thumbnail", TypeName = "blob")] [MaxLength(65535)] public byte[] Thumbnail { get; set; } @@ -96,7 +101,6 @@ public Release() { Rating = 0; ReleaseType = Enums.ReleaseType.Release; - Images = new HashSet(); Medias = new HashSet(); Labels = new HashSet(); Collections = new HashSet(); diff --git a/Roadie.Api.Library/Data/Track.cs b/Roadie.Api.Library/Data/Track.cs index 80ed025..423ad70 100644 --- a/Roadie.Api.Library/Data/Track.cs +++ b/Roadie.Api.Library/Data/Track.cs @@ -56,6 +56,7 @@ public partial class Track : EntityBase [MaxLength(65535)] public string Tags { get; set; } + [Obsolete("Images moved to file system")] [Column("thumbnail", TypeName = "blob")] public byte[] Thumbnail { get; set; } diff --git a/Roadie.Api.Library/Engines/ArtistLookupEngine.cs b/Roadie.Api.Library/Engines/ArtistLookupEngine.cs index 371d915..e030d22 100644 --- a/Roadie.Api.Library/Engines/ArtistLookupEngine.cs +++ b/Roadie.Api.Library/Engines/ArtistLookupEngine.cs @@ -139,18 +139,19 @@ public async Task> Add(Artist artist) } } - if (ArtistImages != null && ArtistImages.Any(x => x.Status == Statuses.New)) - { - foreach (var ArtistImage in ArtistImages) - DbContext.Images.Add(new Image - { - ArtistId = artist.Id, - Url = ArtistImage.Url, - Signature = ArtistImage.Signature, - Bytes = ArtistImage.Bytes - }); - inserted = await DbContext.SaveChangesAsync(); - } + // TODO #29 save images to folder + + //if (ArtistImages != null && ArtistImages.Any(x => x.Status == Statuses.New)) + //{ + // foreach (var ArtistImage in ArtistImages) + // DbContext.Images.Add(new Library.Imaging.Image(artist.RoadieId) + // { + // Url = ArtistImage.Url, + // Signature = ArtistImage.Signature, + // Bytes = ArtistImage.Bytes + // }); + // inserted = await DbContext.SaveChangesAsync(); + //} sw.Stop(); Logger.LogTrace($"Added New Artist: Elapsed Time [{ sw.ElapsedMilliseconds }], Artist `{ artist }`"); } @@ -674,7 +675,7 @@ from g in gg.DefaultIfEmpty() if (artistImageUrls.Any()) { var sw2 = Stopwatch.StartNew(); - var imageBag = new ConcurrentBag(); + var imageBag = new ConcurrentBag(); var i = artistImageUrls.Select(async url => { imageBag.Add(await WebHelper.GetImageFromUrlAsync(url)); diff --git a/Roadie.Api.Library/Engines/ReleaseLookupEngine.cs b/Roadie.Api.Library/Engines/ReleaseLookupEngine.cs index 13a4a90..9f450ca 100644 --- a/Roadie.Api.Library/Engines/ReleaseLookupEngine.cs +++ b/Roadie.Api.Library/Engines/ReleaseLookupEngine.cs @@ -78,17 +78,16 @@ public async Task> Add(Release release, bool doAddTrack try { var releaseGenreTables = release.Genres; - var releaseImages = release.Images; var releaseMedias = release.Medias; var releaseLabels = release.Labels; var now = DateTime.UtcNow; release.AlternateNames = release.AlternateNames.AddToDelimitedList(new[] { release.Title.ToAlphanumericName() }); - release.Images = null; release.Labels = null; release.Medias = null; release.Genres = null; release.LibraryStatus = LibraryStatus.Incomplete; release.Status = Statuses.New; + var releaseImages = new List(); if (!release.IsValid) { return new OperationResult @@ -96,10 +95,6 @@ public async Task> Add(Release release, bool doAddTrack Errors = new Exception[1] { new Exception("Release is Invalid") } }; } - if (release.Thumbnail != null) - { - release.Thumbnail = ImageHelper.ResizeToThumbnail(release.Thumbnail, Configuration); - } DbContext.Releases.Add(release); var inserted = 0; try @@ -153,27 +148,29 @@ public async Task> Add(Release release, bool doAddTrack } } - if (releaseImages != null && releaseImages.Any(x => x.Status == Statuses.New)) - { - foreach (var releaseImage in releaseImages) - { - DbContext.Images.Add(new Image - { - ReleaseId = release.Id, - Url = releaseImage.Url, - Signature = releaseImage.Signature, - Bytes = releaseImage.Bytes - }); - } - try - { - await DbContext.SaveChangesAsync(); - } - catch (Exception ex) - { - Logger.LogError(ex); - } - } + // TODO #29 save release images to release folder + + //if (releaseImages != null && releaseImages.Any(x => x.Status == Statuses.New)) + //{ + // foreach (var releaseImage in releaseImages) + // { + // DbContext.Images.Add(new Image + // { + // ReleaseId = release.Id, + // Url = releaseImage.Url, + // Signature = releaseImage.Signature, + // Bytes = releaseImage.Bytes + // }); + // } + // try + // { + // await DbContext.SaveChangesAsync(); + // } + // catch (Exception ex) + // { + // Logger.LogError(ex); + // } + //} if (releaseLabels != null && releaseLabels.Any(x => x.Status == Statuses.New)) { @@ -418,6 +415,7 @@ public async Task> PerformMetaDataProvidersReleaseSearc if (metaData.Genres != null) releaseGenres.AddRange(metaData.Genres); var releaseLabels = new List(); var releaseMedias = new List(); + var releaseImages = new List(); var releaseImageUrls = new List(); var dontDoMetaDataProvidersSearchArtists = Configuration.DontDoMetaDataProvidersSearchArtists; @@ -442,6 +440,13 @@ public async Task> PerformMetaDataProvidersReleaseSearc if (i.Urls != null) result.URLs = result.URLs.AddToDelimitedList(i.Urls); if (i.ImageUrls != null) releaseImageUrls.AddRange(i.ImageUrls); if (i.ReleaseGenres != null) releaseGenres.AddRange(i.ReleaseGenres); + if(!string.IsNullOrEmpty(i.ReleaseThumbnailUrl)) + { + releaseImages.Add(new Imaging.Image() + { + Bytes = WebHelper.BytesForImageUrl(i.ReleaseThumbnailUrl) + }); + } result.CopyTo(new Release { ReleaseDate = result.ReleaseDate ?? i.ReleaseDate, @@ -449,9 +454,6 @@ public async Task> PerformMetaDataProvidersReleaseSearc Profile = i.Profile, ITunesId = i.iTunesId, Title = result.Title ?? i.ReleaseTitle, - Thumbnail = i.ReleaseThumbnailUrl != null - ? WebHelper.BytesForImageUrl(i.ReleaseThumbnailUrl) - : null, ReleaseType = result.ReleaseType == ReleaseType.Unknown ? SafeParser.ToEnum(i.ReleaseType) : result.ReleaseType @@ -486,6 +488,13 @@ public async Task> PerformMetaDataProvidersReleaseSearc if (!string.IsNullOrEmpty(mb.ReleaseTitle) && !mb.ReleaseTitle.Equals(result.Title, StringComparison.OrdinalIgnoreCase)) result.AlternateNames.AddToDelimitedList(new[] { mb.ReleaseTitle }); + if (!string.IsNullOrEmpty(mb.ReleaseThumbnailUrl)) + { + releaseImages.Add(new Imaging.Image() + { + Bytes = WebHelper.BytesForImageUrl(mb.ReleaseThumbnailUrl) + }); + } result.CopyTo(new Release { ReleaseDate = result.ReleaseDate ?? mb.ReleaseDate, @@ -497,9 +506,6 @@ public async Task> PerformMetaDataProvidersReleaseSearc MusicBrainzId = mb.MusicBrainzId, ITunesId = mb.iTunesId, Title = result.Title ?? mb.ReleaseTitle, - Thumbnail = mb.ReleaseThumbnailUrl != null - ? WebHelper.BytesForImageUrl(mb.ReleaseThumbnailUrl) - : null, ReleaseType = result.ReleaseType == ReleaseType.Unknown ? SafeParser.ToEnum(mb.ReleaseType) : result.ReleaseType @@ -535,6 +541,13 @@ public async Task> PerformMetaDataProvidersReleaseSearc if (!string.IsNullOrEmpty(l.ReleaseTitle) && !l.ReleaseTitle.Equals(result.Title, StringComparison.OrdinalIgnoreCase)) result.AlternateNames.AddToDelimitedList(new[] { l.ReleaseTitle }); + if (!string.IsNullOrEmpty(l.ReleaseThumbnailUrl)) + { + releaseImages.Add(new Imaging.Image() + { + Bytes = WebHelper.BytesForImageUrl(l.ReleaseThumbnailUrl) + }); + } result.CopyTo(new Release { ReleaseDate = result.ReleaseDate ?? l.ReleaseDate, @@ -545,9 +558,6 @@ public async Task> PerformMetaDataProvidersReleaseSearc MusicBrainzId = l.MusicBrainzId, ITunesId = l.iTunesId, Title = result.Title ?? l.ReleaseTitle, - Thumbnail = l.ReleaseThumbnailUrl != null - ? WebHelper.BytesForImageUrl(l.ReleaseThumbnailUrl) - : null, ReleaseType = result.ReleaseType == ReleaseType.Unknown ? SafeParser.ToEnum(l.ReleaseType) : result.ReleaseType @@ -580,6 +590,13 @@ public async Task> PerformMetaDataProvidersReleaseSearc if (!string.IsNullOrEmpty(s.ReleaseTitle) && !s.ReleaseTitle.Equals(result.Title, StringComparison.OrdinalIgnoreCase)) result.AlternateNames.AddToDelimitedList(new[] { s.ReleaseTitle }); + if (!string.IsNullOrEmpty(s.ReleaseThumbnailUrl)) + { + releaseImages.Add(new Imaging.Image() + { + Bytes = WebHelper.BytesForImageUrl(s.ReleaseThumbnailUrl) + }); + } result.CopyTo(new Release { ReleaseDate = result.ReleaseDate ?? s.ReleaseDate, @@ -589,9 +606,6 @@ public async Task> PerformMetaDataProvidersReleaseSearc MusicBrainzId = s.MusicBrainzId, ITunesId = s.iTunesId, Title = result.Title ?? s.ReleaseTitle, - Thumbnail = s.ReleaseThumbnailUrl != null - ? WebHelper.BytesForImageUrl(s.ReleaseThumbnailUrl) - : null, ReleaseType = result.ReleaseType == ReleaseType.Unknown ? SafeParser.ToEnum(s.ReleaseType) : result.ReleaseType @@ -624,14 +638,18 @@ public async Task> PerformMetaDataProvidersReleaseSearc if (!string.IsNullOrEmpty(d.ReleaseTitle) && !d.ReleaseTitle.Equals(result.Title, StringComparison.OrdinalIgnoreCase)) result.AlternateNames.AddToDelimitedList(new[] { d.ReleaseTitle }); + if (!string.IsNullOrEmpty(d.ReleaseThumbnailUrl)) + { + releaseImages.Add(new Imaging.Image() + { + Bytes = WebHelper.BytesForImageUrl(d.ReleaseThumbnailUrl) + }); + } result.CopyTo(new Release { Profile = HttpEncoder.HtmlEncode(d.Profile), DiscogsId = d.DiscogsId, Title = result.Title ?? d.ReleaseTitle, - Thumbnail = d.ReleaseThumbnailUrl != null - ? WebHelper.BytesForImageUrl(d.ReleaseThumbnailUrl) - : null, ReleaseType = result.ReleaseType == ReleaseType.Unknown ? SafeParser.ToEnum(d.ReleaseType) : result.ReleaseType @@ -700,29 +718,22 @@ public async Task> PerformMetaDataProvidersReleaseSearc if (releaseImageUrls.Any()) { var sw2 = Stopwatch.StartNew(); - var imageBag = new ConcurrentBag(); + var imageBag = new ConcurrentBag(); var i = releaseImageUrls.Select(async url => { imageBag.Add(await WebHelper.GetImageFromUrlAsync(url)); }); await Task.WhenAll(i); - // If the release has images merge any fetched images - var existingImages = result.Images != null ? result.Images.ToList() : new List(); - existingImages.AddRange(imageBag.ToList()); - // Now set release images to be unique image based on image hash - result.Images = existingImages - .Where(x => x != null && x.Bytes != null) - .GroupBy(x => x.Signature) - .Select(x => x.First()).Take(Configuration.Processing.MaximumReleaseImagesToAdd) - .ToList(); - if (result.Thumbnail == null && result.Images != null) - { - result.Thumbnail = result.Images.First().Bytes; - } + releaseImages.AddRange(imageBag.ToList()); sw2.Stop(); Logger.LogTrace($"PerformMetaDataProvidersReleaseSearch: Image Url Processing Complete [{ sw2.ElapsedMilliseconds }]"); } + result.Images = releaseImages.Where(x => x.Bytes != null) + .GroupBy(x => x.Signature) + .Select(x => x.First()).Take(Configuration.Processing.MaximumReleaseImagesToAdd) + .ToList(); + if (releaseLabels.Any()) { var sw2 = Stopwatch.StartNew(); diff --git a/Roadie.Api.Library/Identity/ApplicationUser.cs b/Roadie.Api.Library/Identity/ApplicationUser.cs index 96646bb..e748f95 100644 --- a/Roadie.Api.Library/Identity/ApplicationUser.cs +++ b/Roadie.Api.Library/Identity/ApplicationUser.cs @@ -24,7 +24,9 @@ public partial class ApplicationUser : IdentityUser public ICollection ArtistRatings { get; set; } - [Column("avatar", TypeName = "blob")] public byte[] Avatar { get; set; } + [Obsolete("Images moved to file system")] + [Column("avatar", TypeName = "blob")] + public byte[] Avatar { get; set; } public ICollection Bookmarks { get; set; } diff --git a/Roadie.Api.Library/Imaging/DefaultNotFoundImages.cs b/Roadie.Api.Library/Imaging/DefaultNotFoundImages.cs index e91997d..687cc43 100644 --- a/Roadie.Api.Library/Imaging/DefaultNotFoundImages.cs +++ b/Roadie.Api.Library/Imaging/DefaultNotFoundImages.cs @@ -1,6 +1,5 @@ using Microsoft.Extensions.Logging; using Roadie.Library.Configuration; -using Roadie.Library.Data; using System; using System.IO; @@ -8,31 +7,31 @@ namespace Roadie.Library.Imaging { public class DefaultNotFoundImages : IDefaultNotFoundImages { - private Image _artist; - private Image _collection; - private Image _label; - private Image _genre; - private Image _playlist; - private Image _release; - private Image _track; - private Image _user; + private IImage _artist; + private IImage _collection; + private IImage _label; + private IImage _genre; + private IImage _playlist; + private IImage _release; + private IImage _track; + private IImage _user; - public Image Artist => _artist ?? (_artist = MakeImageFromFile(MakeImagePath(@"images/artist.jpg"))); + public IImage Artist => _artist ?? (_artist = MakeImageFromFile(MakeImagePath(@"images/artist.jpg"))); - public Image Collection => + public IImage Collection => _collection ?? (_collection = MakeImageFromFile(MakeImagePath(@"images/collection.jpg"))); - public Image Label => _label ?? (_label = MakeImageFromFile(MakeImagePath(@"images/label.jpg"))); + public IImage Label => _label ?? (_label = MakeImageFromFile(MakeImagePath(@"images/label.jpg"))); - public Image Genre => _genre ?? (_genre = MakeImageFromFile(MakeImagePath(@"images/genre.jpg"))); + public IImage Genre => _genre ?? (_genre = MakeImageFromFile(MakeImagePath(@"images/genre.jpg"))); - public Image Playlist => _playlist ?? (_playlist = MakeImageFromFile(MakeImagePath(@"images/playlist.jpg"))); + public IImage Playlist => _playlist ?? (_playlist = MakeImageFromFile(MakeImagePath(@"images/playlist.jpg"))); - public Image Release => _release ?? (_release = MakeImageFromFile(MakeImagePath(@"images/release.jpg"))); + public IImage Release => _release ?? (_release = MakeImageFromFile(MakeImagePath(@"images/release.jpg"))); - public Image Track => _track ?? (_track = MakeImageFromFile(MakeImagePath(@"images/track.jpg"))); + public IImage Track => _track ?? (_track = MakeImageFromFile(MakeImagePath(@"images/track.jpg"))); - public Image User => _user ?? (_user = MakeImageFromFile(MakeImagePath(@"images/user.jpg"))); + public IImage User => _user ?? (_user = MakeImageFromFile(MakeImagePath(@"images/user.jpg"))); private IRoadieSettings Configuration { get; } @@ -50,24 +49,31 @@ public DefaultNotFoundImages(ILogger logger, IRoadieSettings configuration) Logger = logger; } - private static Image MakeImageFromFile(string filename) + private static IImage MakeImageFromFile(string filename) { - if (!File.Exists(filename)) return new Image(); + if (!File.Exists(filename)) + { + return new Image(); + } var bytes = File.ReadAllBytes(filename); return new Image { Bytes = bytes, - CreatedDate = DateTime.UtcNow, - LastUpdated = DateTime.UtcNow + CreatedDate = DateTime.UtcNow }; } private string MakeImagePath(string filename) { - if (string.IsNullOrEmpty(filename)) return null; + if (string.IsNullOrEmpty(filename)) + { + return null; + } var path = Path.Combine(Configuration.ContentPath, filename); if (!File.Exists(path)) + { Logger.LogWarning("Unable To Find Path [{0}], ContentPath [{1}]", path, Configuration.ContentPath); + } return path; } } diff --git a/Roadie.Api.Library/Imaging/IDefaultNotFoundImages.cs b/Roadie.Api.Library/Imaging/IDefaultNotFoundImages.cs index d1f60eb..a1d6be9 100644 --- a/Roadie.Api.Library/Imaging/IDefaultNotFoundImages.cs +++ b/Roadie.Api.Library/Imaging/IDefaultNotFoundImages.cs @@ -4,13 +4,13 @@ namespace Roadie.Library.Imaging { public interface IDefaultNotFoundImages { - Image Artist { get; } - Image Collection { get; } - Image Label { get; } - Image Genre { get; } - Image Playlist { get; } - Image Release { get; } - Image Track { get; } - Image User { get; } + IImage Artist { get; } + IImage Collection { get; } + IImage Label { get; } + IImage Genre { get; } + IImage Playlist { get; } + IImage Release { get; } + IImage Track { get; } + IImage User { get; } } } \ No newline at end of file diff --git a/Roadie.Api.Library/Imaging/IImage.cs b/Roadie.Api.Library/Imaging/IImage.cs new file mode 100644 index 0000000..0701587 --- /dev/null +++ b/Roadie.Api.Library/Imaging/IImage.cs @@ -0,0 +1,21 @@ +using Roadie.Library.Enums; +using System; + +namespace Roadie.Library.Imaging +{ + public interface IImage + { + short SortOrder { get; set; } + byte[] Bytes { get; set; } + DateTime CreatedDate { get; set; } + Guid Id { get; } + DateTime LastUpdated { get; set; } + string Signature { get; set; } + Statuses Status { get; set; } + string Url { get; set; } + string CacheKey { get; } + string CacheRegion { get; } + string GenerateSignature(); + string ToString(); + } +} \ No newline at end of file diff --git a/Roadie.Api.Library/Imaging/Image.cs b/Roadie.Api.Library/Imaging/Image.cs new file mode 100644 index 0000000..2950a14 --- /dev/null +++ b/Roadie.Api.Library/Imaging/Image.cs @@ -0,0 +1,56 @@ +using Roadie.Library.Enums; +using System; +using System.Linq; + +namespace Roadie.Library.Imaging +{ + /// + /// Image class used internally. + /// + [Serializable] + public class Image : IImage + { + public Guid Id { get; } + public Statuses Status { get; set; } + public short SortOrder { get; set; } + public byte[] Bytes { get; set; } + public DateTime CreatedDate { get; set; } + public DateTime LastUpdated { get; set; } + public string Signature { get; set; } + public string Url { get; set; } + + public string GenerateSignature() + { + if (Bytes == null || !Bytes.Any()) + { + return null; + } + return ImageHasher.AverageHash(Bytes).ToString(); + } + + public Image() + { + } + + public Image(Guid id) + { + Id = id; + } + + public override string ToString() => $"Id [{Id}]"; + + public string CacheKey => CacheUrn(Id); + + public string CacheRegion => CacheRegionUrn(Id); + + public static string CacheRegionUrn(Guid Id) + { + return $"urn:artist:{Id}"; + } + + public static string CacheUrn(Guid Id) + { + return $"urn:artist_by_id:{Id}"; + } + } +} \ No newline at end of file diff --git a/Roadie.Api.Library/Imaging/ImageHasher.cs b/Roadie.Api.Library/Imaging/ImageHasher.cs index 3bac1ba..5f11e85 100644 --- a/Roadie.Api.Library/Imaging/ImageHasher.cs +++ b/Roadie.Api.Library/Imaging/ImageHasher.cs @@ -1,4 +1,4 @@ -using SixLabors.ImageSharp; +using sl = SixLabors.ImageSharp; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Processing; using System.IO; @@ -63,7 +63,7 @@ private static uint BitCount(ulong num) /// Hash of Image public static ulong AverageHash(byte[] bytes) { - using (var image = Image.Load(bytes)) + using (var image = sl.Image.Load(bytes)) { image.Mutate(ctx => ctx.Resize(8, 8).Grayscale()); using (var ms = new MemoryStream()) @@ -100,20 +100,11 @@ public static ulong AverageHash(byte[] bytes) /// /// Path to the input file. /// The hash of the input file's image content. - public static ulong AverageHash(string path) - { - return AverageHash(File.ReadAllBytes(path)); - } + public static ulong AverageHash(string path) => AverageHash(File.ReadAllBytes(path)); - public static bool ImagesAreSame(string path1, string path2) - { - return Similarity(path1, path2) == 100; - } + public static bool ImagesAreSame(string path1, string path2) => Similarity(path1, path2) == 100; - public static bool ImagesAreSame(byte[] image1, byte[] image2) - { - return Similarity(image1, image2) == 100; - } + public static bool ImagesAreSame(byte[] image1, byte[] image2) => Similarity(image1, image2) == 100; /// /// Returns a percentage-based similarity value between the two given hashes. The higher @@ -122,10 +113,7 @@ public static bool ImagesAreSame(byte[] image1, byte[] image2) /// The first hash. /// The second hash. /// The similarity percentage. - public static double Similarity(ulong hash1, ulong hash2) - { - return (64 - BitCount(hash1 ^ hash2)) * 100 / 64.0; - } + public static double Similarity(ulong hash1, ulong hash2) => (64 - BitCount(hash1 ^ hash2)) * 100 / 64.0; /// /// Returns a percentage-based similarity value between the image content of the two given diff --git a/Roadie.Api.Library/Imaging/ImageHelper.cs b/Roadie.Api.Library/Imaging/ImageHelper.cs index 1a0a0c3..6eb1d9d 100644 --- a/Roadie.Api.Library/Imaging/ImageHelper.cs +++ b/Roadie.Api.Library/Imaging/ImageHelper.cs @@ -39,7 +39,7 @@ public static byte[] ConvertToJpegFormat(byte[] imageBytes) using (var outStream = new MemoryStream()) { IImageFormat imageFormat = null; - using (var image = Image.Load(imageBytes, out imageFormat)) + using (var image = SixLabors.ImageSharp.Image.Load(imageBytes, out imageFormat)) { image.Save(outStream, ImageFormats.Jpeg); } @@ -63,7 +63,7 @@ public static byte[] ConvertToGifFormat(byte[] imageBytes) using (var outStream = new MemoryStream()) { IImageFormat imageFormat = null; - using (var image = Image.Load(imageBytes, out imageFormat)) + using (var image = SixLabors.ImageSharp.Image.Load(imageBytes, out imageFormat)) { image.Save(outStream, ImageFormats.Gif); } @@ -184,7 +184,7 @@ public static ImageSearchResult ImageSearchResultForImageUrl(string imageUrl) var result = new ImageSearchResult(); var imageBytes = WebHelper.BytesForImageUrl(imageUrl); IImageFormat imageFormat = null; - using (var image = 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(); @@ -254,7 +254,7 @@ public static Tuple ResizeImage(byte[] imageBytes, int width, int { var resized = false; IImageFormat imageFormat = null; - using (var image = 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) diff --git a/Roadie.Api.Library/Models/Image.cs b/Roadie.Api.Library/Models/Image.cs index 85f8288..dffcd2e 100644 --- a/Roadie.Api.Library/Models/Image.cs +++ b/Roadie.Api.Library/Models/Image.cs @@ -3,17 +3,20 @@ namespace Roadie.Library.Models { + /// + /// Image class served to API consumers. + /// [Serializable] public class Image : EntityModelBase { - public Guid? ArtistId { get; set; } - public byte[] Bytes { get; set; } [MaxLength(100)] public string Caption { get; set; } + [Obsolete("Only here for transition. Will be removed in future release.")] + public Guid? ArtistId { get; set; } + [Obsolete("Only here for transition. Will be removed in future release.")] public Guid? ReleaseId { get; set; } - [MaxLength(50)] public string Signature { get; set; } [MaxLength(500)] public string ThumbnailUrl { get; set; } diff --git a/Roadie.Api.Library/Utility/WebHelper.cs b/Roadie.Api.Library/Utility/WebHelper.cs index ce46433..9ca56b2 100644 --- a/Roadie.Api.Library/Utility/WebHelper.cs +++ b/Roadie.Api.Library/Utility/WebHelper.cs @@ -1,5 +1,4 @@ using HtmlAgilityPack; -using Roadie.Library.Data; using Roadie.Library.Enums; using Roadie.Library.Imaging; using System; @@ -59,7 +58,7 @@ public static byte[] BytesForImageUrl(string url) return null; } - public static async Task GetImageFromUrlAsync(string url) + public static async Task GetImageFromUrlAsync(string url) { byte[] imageBytes = null; try @@ -80,7 +79,7 @@ public static async Task GetImageFromUrlAsync(string url) { var signature = ImageHasher.AverageHash(imageBytes).ToString(); var ib = ImageHelper.ConvertToJpegFormat(imageBytes); - return new Image + return new Image(Guid.NewGuid()) { Url = url, Status = Statuses.New, diff --git a/Roadie.Api.Services/AdminService.cs b/Roadie.Api.Services/AdminService.cs index 84d34d2..108565c 100644 --- a/Roadie.Api.Services/AdminService.cs +++ b/Roadie.Api.Services/AdminService.cs @@ -1012,6 +1012,225 @@ public async Task> UpdateInviteTokenUsed(Guid? tokenId) }; } + /// + /// Migrate images from Images table and Thumbnails to file storage. + /// + public async Task> MigrateImages(ApplicationUser user) + { + var sw = new Stopwatch(); + sw.Start(); + var errors = new List(); + var now = DateTime.UtcNow; + foreach (var artist in DbContext.Artists.Where(x => x.Thumbnail != null).OrderBy(x => x.SortName ?? x.Name)) + { + var artistFolder = artist.ArtistFileFolder(Configuration); + if (!Directory.Exists(artistFolder)) + { + Directory.CreateDirectory(artistFolder); + } + var artistImage = Path.Combine(artistFolder, ImageHelper.ArtistImageFilename); + if (!File.Exists(artistImage)) + { + File.WriteAllBytes(artistImage, ImageHelper.ConvertToJpegFormat(artist.Thumbnail)); + } + artist.Thumbnail = null; + artist.LastUpdated = now; + + Logger.LogInformation($"Saved Artist Image `{artist}` path [{ artistImage }]"); + } + await DbContext.SaveChangesAsync(); + + var artistImages = (from i in DbContext.Images + join a in DbContext.Artists on i.ArtistId equals a.Id + select new { i, a }); + foreach (var artistImage in artistImages) + { + var looper = 0; + var artistFolder = artistImage.a.ArtistFileFolder(Configuration); + var artistImageFilename = Path.Combine(artistFolder, string.Format(ImageHelper.ArtistSecondaryImageFilename, looper.ToString("00"))); + while (File.Exists(artistImageFilename)) + { + looper++; + artistImageFilename = Path.Combine(artistFolder, string.Format(ImageHelper.ArtistSecondaryImageFilename, looper.ToString("00"))); + } + File.WriteAllBytes(artistImageFilename, ImageHelper.ConvertToJpegFormat(artistImage.i.Bytes)); + DbContext.Images.Remove(artistImage.i); + Logger.LogInformation($"Saved Artist Secondary Image `{artistImage.a}` path [{ artistImageFilename }]"); + } + await DbContext.SaveChangesAsync(); + + foreach (var collection in DbContext.Collections.Where(x => x.Thumbnail != null).OrderBy(x => x.SortName ?? x.Name)) + { + var image = collection.PathToImage(Configuration); + if (!File.Exists(image)) + { + File.WriteAllBytes(image, ImageHelper.ConvertToJpegFormat(collection.Thumbnail)); + } + collection.Thumbnail = null; + collection.LastUpdated = now; + Logger.LogInformation($"Saved Collection Image `{collection}` path [{ image }]"); + } + await DbContext.SaveChangesAsync(); + + foreach (var genre in DbContext.Genres.Where(x => x.Thumbnail != null).OrderBy(x => x.Name)) + { + var image = genre.PathToImage(Configuration); + if (!File.Exists(image)) + { + File.WriteAllBytes(image, ImageHelper.ConvertToJpegFormat(genre.Thumbnail)); + } + genre.Thumbnail = null; + genre.LastUpdated = now; + Logger.LogInformation($"Saved Genre Image `{genre}` path [{ image }]"); + } + await DbContext.SaveChangesAsync(); + + foreach (var label in DbContext.Labels.Where(x => x.Thumbnail != null).OrderBy(x => x.SortName ?? x.Name)) + { + var image = label.PathToImage(Configuration); + if (!File.Exists(image)) + { + File.WriteAllBytes(image, ImageHelper.ConvertToJpegFormat(label.Thumbnail)); + } + label.Thumbnail = null; + label.LastUpdated = now; + Logger.LogInformation($"Saved Label Image `{label}` path [{ image }]"); + } + await DbContext.SaveChangesAsync(); + + foreach (var playlist in DbContext.Playlists.Where(x => x.Thumbnail != null).OrderBy(x => x.Name)) + { + var image = playlist.PathToImage(Configuration); + if (!File.Exists(image)) + { + File.WriteAllBytes(image, ImageHelper.ConvertToJpegFormat(playlist.Thumbnail)); + } + playlist.Thumbnail = null; + playlist.LastUpdated = now; + Logger.LogInformation($"Saved Playlist Image `{playlist}` path [{ image }]"); + } + await DbContext.SaveChangesAsync(); + + foreach (var release in DbContext.Releases.Include(x => x.Artist).Where(x => x.Thumbnail != null).OrderBy(x => x.Title)) + { + var artistFolder = release.Artist.ArtistFileFolder(Configuration); + if (!Directory.Exists(artistFolder)) + { + Directory.CreateDirectory(artistFolder); + } + var releaseFolder = release.ReleaseFileFolder(artistFolder); + try + { + if (!Directory.Exists(releaseFolder)) + { + Directory.CreateDirectory(releaseFolder); + } + var releaseImage = Path.Combine(releaseFolder, "cover.jpg"); + if (!File.Exists(releaseImage)) + { + File.WriteAllBytes(releaseImage, ImageHelper.ConvertToJpegFormat(release.Thumbnail)); + } + release.Thumbnail = null; + release.LastUpdated = now; + Logger.LogInformation($"Saved Release Image `{release}` path [{ releaseImage }]"); + } + catch (Exception ex) + { + Logger.LogError(ex, $"Error saving Release Image `{release}` folder [{ releaseFolder }]"); + } + } + await DbContext.SaveChangesAsync(); + + var releaseImages = (from i in DbContext.Images + join r in DbContext.Releases.Include(x => x.Artist) on i.ReleaseId equals r.Id + select new { i, r }); + foreach (var releaseImage in releaseImages) + { + var looper = 0; + var artistFolder = releaseImage.r.Artist.ArtistFileFolder(Configuration); + if (!Directory.Exists(artistFolder)) + { + Directory.CreateDirectory(artistFolder); + } + var releaseFolder = releaseImage.r.ReleaseFileFolder(artistFolder); + if (!Directory.Exists(releaseFolder)) + { + Directory.CreateDirectory(releaseFolder); + } + var releaseImageFilename = Path.Combine(releaseFolder, string.Format(ImageHelper.ReleaseSecondaryImageFilename, looper.ToString("00"))); + try + { + while (File.Exists(releaseImageFilename)) + { + looper++; + releaseImageFilename = Path.Combine(releaseFolder, string.Format(ImageHelper.ReleaseSecondaryImageFilename, looper.ToString("00"))); + } + File.WriteAllBytes(releaseImageFilename, ImageHelper.ConvertToJpegFormat(releaseImage.i.Bytes)); + DbContext.Images.Remove(releaseImage.i); + Logger.LogInformation($"Saved Release Secondary Image `{releaseImage.r}` path [{ releaseImageFilename }]"); + } + catch (Exception ex) + { + Logger.LogError(ex, $"Error saving Release Secondary Image [{releaseImageFilename}] folder [{ releaseFolder }]"); + } + } + await DbContext.SaveChangesAsync(); + + foreach (var track in DbContext.Tracks.Include(x => x.ReleaseMedia) + .Include(x => x.ReleaseMedia.Release) + .Include(x => x.ReleaseMedia.Release.Artist) + .Where(x => x.Thumbnail != null).OrderBy(x => x.Title)) + { + var artistFolder = track.ReleaseMedia.Release.Artist.ArtistFileFolder(Configuration); + if (!Directory.Exists(artistFolder)) + { + Directory.CreateDirectory(artistFolder); + } + var releaseFolder = track.ReleaseMedia.Release.ReleaseFileFolder(artistFolder); + if (!Directory.Exists(releaseFolder)) + { + Directory.CreateDirectory(releaseFolder); + } + var trackImage = track.PathToTrackThumbnail(Configuration); + try + { + if (!File.Exists(trackImage)) + { + File.WriteAllBytes(trackImage, ImageHelper.ConvertToJpegFormat(track.Thumbnail)); + } + track.Thumbnail = null; + track.LastUpdated = now; + Logger.LogInformation($"Saved Track Image `{track}` path [{ trackImage }]"); + } + catch (Exception ex) + { + Logger.LogError(ex, $"Error saving Track Image [{trackImage}] folder [{ releaseFolder }]"); + } + } + await DbContext.SaveChangesAsync(); + + foreach (var usr in DbContext.Users.Where(x => x.Avatar != null).OrderBy(x => x.UserName)) + { + var image = usr.PathToImage(Configuration); + if (!File.Exists(image)) + { + File.WriteAllBytes(image, ImageHelper.ConvertToJpegFormat(usr.Avatar)); + } + usr.Avatar = null; + usr.LastUpdated = now; + Logger.LogInformation($"Saved User Image `{user}` path [{ image }]"); + } + await DbContext.SaveChangesAsync(); + + return new OperationResult + { + IsSuccess = !errors.Any(), + Data = true, + OperationTime = sw.ElapsedMilliseconds, + Errors = errors + }; + } + public async Task> ValidateInviteToken(Guid? tokenId) { var sw = new Stopwatch(); diff --git a/Roadie.Api.Services/ArtistService.cs b/Roadie.Api.Services/ArtistService.cs index 40a69d2..24162af 100644 --- a/Roadie.Api.Services/ArtistService.cs +++ b/Roadie.Api.Services/ArtistService.cs @@ -552,23 +552,6 @@ orderby d { Services.FileDirectoryProcessorService.DeleteEmptyFolders(new DirectoryInfo(artistFolder), Logger); } - // Always update artist image if artist image is found on an artist rescan - var imageFiles = ImageHelper.ImageFilesInFolder(artistFolder, SearchOption.AllDirectories); - if (imageFiles != null && imageFiles.Any()) - { - var i = new FileInfo(imageFiles.First()); - var iName = i.Name.ToLower().Trim(); - if (ImageHelper.IsArtistImage(i)) - { - // Read image and convert to jpeg - artist.Thumbnail = ImageHelper.ResizeToThumbnail(File.ReadAllBytes(i.FullName), Configuration); - artist.LastUpdated = DateTime.UtcNow; - await DbContext.SaveChangesAsync(); - CacheManager.ClearRegion(artist.CacheRegion); - Logger.LogTrace("Update Thumbnail using Artist File [{0}]", iName); - } - } - sw.Stop(); CacheManager.ClearRegion(artist.CacheRegion); Logger.LogInformation("Scanned Artist [{0}], Releases Scanned [{1}], OperationTime [{2}]", artist.ToString(), releaseScannedCount, sw.ElapsedMilliseconds); @@ -588,7 +571,7 @@ orderby d }; } - public async Task> SetReleaseImageByUrl(ApplicationUser user, Guid id, string imageUrl) + public async Task> SetReleaseImageByUrl(ApplicationUser user, Guid id, string imageUrl) { return await SaveImageBytes(user, id, WebHelper.BytesForImageUrl(imageUrl)); } @@ -596,7 +579,6 @@ public async Task> SetReleaseImageByUrl(ApplicationUser u public async Task> UpdateArtist(ApplicationUser user, Artist model) { var didRenameArtist = false; - var didChangeThumbnail = false; var sw = new Stopwatch(); sw.Start(); var errors = new List(); @@ -689,10 +671,6 @@ public async Task> UpdateArtist(ApplicationUser user, Arti // Save unaltered image to cover file var artistImageName = Path.Combine(newArtistFolder, ImageHelper.ArtistImageFilename); File.WriteAllBytes(artistImageName, ImageHelper.ConvertToJpegFormat(artistImage)); - - // Resize to store in database as thumbnail - artist.Thumbnail = ImageHelper.ResizeToThumbnail(artistImage, Configuration); - didChangeThumbnail = true; } if (model.NewSecondaryImagesData != null && model.NewSecondaryImagesData.Any()) @@ -855,7 +833,7 @@ public async Task> UpdateArtist(ApplicationUser user, Arti } CacheManager.ClearRegion(artist.CacheRegion); - Logger.LogInformation($"UpdateArtist `{artist}` By User `{user}`: Renamed Artist [{didRenameArtist}], Uploaded new image [{didChangeThumbnail}]"); + Logger.LogInformation($"UpdateArtist `{artist}` By User `{user}`: Renamed Artist [{didRenameArtist}]"); } catch (Exception ex) { @@ -874,7 +852,7 @@ public async Task> UpdateArtist(ApplicationUser user, Arti }; } - public async Task> UploadArtistImage(ApplicationUser user, Guid id, IFormFile file) + public async Task> UploadArtistImage(ApplicationUser user, Guid id, IFormFile file) { var bytes = new byte[0]; using (var ms = new MemoryStream()) @@ -1022,15 +1000,12 @@ join rm in DbContext.ReleaseMedias on r.Id equals rm.ReleaseId if (includes.Contains("images")) { tsw.Restart(); - result.Images = DbContext.Images.Where(x => x.ArtistId == artist.Id) - .Select(x => MakeFullsizeImage(Configuration, HttpContext, x.RoadieId, x.Caption)).ToArray(); - var artistFolder = artist.ArtistFileFolder(Configuration); - var artistImagesInFolder = ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(artistFolder), - ImageType.ArtistSecondary, SearchOption.TopDirectoryOnly); + var artistImagesInFolder = ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(artistFolder), ImageType.ArtistSecondary, SearchOption.TopDirectoryOnly); if (artistImagesInFolder.Any()) - result.Images = result.Images.Concat(artistImagesInFolder.Select((x, i) => - MakeFullsizeSecondaryImage(Configuration, HttpContext, id, ImageType.ArtistSecondary, i))); + { + result.Images = artistImagesInFolder.Select((x, i) => MakeFullsizeSecondaryImage(Configuration, HttpContext, id, ImageType.ArtistSecondary, i)); + } tsw.Stop(); timings.Add("images", tsw.ElapsedMilliseconds); @@ -1313,7 +1288,6 @@ select LabelList.FromDataLabel(l, MakeLabelThumbnailImage(Configuration, HttpCon artistToMergeInto.ITunesId = artistToMergeInto.ITunesId ?? artistToMerge.ITunesId; artistToMergeInto.AmgId = artistToMergeInto.AmgId ?? artistToMerge.AmgId; artistToMergeInto.SpotifyId = artistToMergeInto.SpotifyId ?? artistToMerge.SpotifyId; - artistToMergeInto.Thumbnail = artistToMergeInto.Thumbnail ?? artistToMerge.Thumbnail; artistToMergeInto.Profile = artistToMergeInto.Profile ?? artistToMerge.Profile; artistToMergeInto.BirthDate = artistToMergeInto.BirthDate ?? artistToMerge.BirthDate; artistToMergeInto.BeginDate = artistToMergeInto.BeginDate ?? artistToMerge.BeginDate; @@ -1353,15 +1327,6 @@ select LabelList.FromDataLabel(l, MakeLabelThumbnailImage(Configuration, HttpCon } } } - var artistImages = DbContext.Images.Where(x => x.ArtistId == artistToMerge.Id).ToArray(); - if (artistImages != null) - { - foreach (var artistImage in artistImages) - { - artistImage.ArtistId = artistToMergeInto.Id; - } - } - try { // Move any Artist and Artist Secondary images from ArtistToMerge into ArtistToMergeInto folder @@ -1407,13 +1372,6 @@ select LabelList.FromDataLabel(l, MakeLabelThumbnailImage(Configuration, HttpCon } var userArtists = DbContext.UserArtists.Where(x => x.ArtistId == artistToMerge.Id).ToArray(); - if (artistImages != null) - { - foreach (var userArtist in userArtists) - { - userArtist.ArtistId = artistToMergeInto.Id; - } - } var artistTracks = DbContext.Tracks.Where(x => x.ArtistId == artistToMerge.Id).ToArray(); if (artistTracks != null) { @@ -1465,18 +1423,17 @@ select LabelList.FromDataLabel(l, MakeLabelThumbnailImage(Configuration, HttpCon }; } - private async Task> SaveImageBytes(ApplicationUser user, Guid id, byte[] imageBytes) + private async Task> SaveImageBytes(ApplicationUser user, Guid id, byte[] imageBytes) { var sw = new Stopwatch(); sw.Start(); var errors = new List(); var artist = DbContext.Artists.FirstOrDefault(x => x.RoadieId == id); - if (artist == null) return new OperationResult(true, $"Artist Not Found [{id}]"); + if (artist == null) return new OperationResult(true, $"Artist Not Found [{id}]"); try { var now = DateTime.UtcNow; - artist.Thumbnail = imageBytes; - if (artist.Thumbnail != null) + if (imageBytes != null) { // Ensure artist folder exists var artistFolder = artist.ArtistFileFolder(Configuration); @@ -1489,11 +1446,7 @@ private async Task> SaveImageBytes(ApplicationUser user, // Save unaltered image to artist file var artistImage = Path.Combine(artistFolder, ImageHelper.ArtistImageFilename); File.WriteAllBytes(artistImage, ImageHelper.ConvertToJpegFormat(imageBytes)); - - // Resize to store in database as thumbnail - artist.Thumbnail = ImageHelper.ResizeToThumbnail(artist.Thumbnail, Configuration); } - artist.LastUpdated = now; await DbContext.SaveChangesAsync(); CacheManager.ClearRegion(artist.CacheRegion); @@ -1507,7 +1460,7 @@ private async Task> SaveImageBytes(ApplicationUser user, sw.Stop(); - return new OperationResult + return new OperationResult { IsSuccess = !errors.Any(), Data = MakeThumbnailImage(Configuration, HttpContext, id, "artist", Configuration.MediumImageSize.Width, Configuration.MediumImageSize.Height, true), diff --git a/Roadie.Api.Services/CollectionService.cs b/Roadie.Api.Services/CollectionService.cs index 8fda108..0dc198d 100644 --- a/Roadie.Api.Services/CollectionService.cs +++ b/Roadie.Api.Services/CollectionService.cs @@ -302,14 +302,10 @@ public async Task> UpdateCollection(User user, Collection { // Save unaltered collection image File.WriteAllBytes(collection.PathToImage(Configuration), ImageHelper.ConvertToJpegFormat(collectionImage)); - // Update Thumbnail - collection.Thumbnail = ImageHelper.ResizeToThumbnail(collectionImage, Configuration); } - if (model.Maintainer?.Value != null) { - var maintainer = - DbContext.Users.FirstOrDefault(x => x.RoadieId == SafeParser.ToGuid(model.Maintainer.Value)); + var maintainer = DbContext.Users.FirstOrDefault(x => x.RoadieId == SafeParser.ToGuid(model.Maintainer.Value)); if (maintainer != null) collection.MaintainerId = maintainer.Id; } diff --git a/Roadie.Api.Services/GenreService.cs b/Roadie.Api.Services/GenreService.cs index f412138..bc86b23 100644 --- a/Roadie.Api.Services/GenreService.cs +++ b/Roadie.Api.Services/GenreService.cs @@ -159,12 +159,12 @@ order BY RIGHT( HEX( (1<<24) * (1+RAND()) ), 6) }); } - public async Task> SetGenreImageByUrl(User user, Guid id, string imageUrl) + public async Task> SetGenreImageByUrl(User user, Guid id, string imageUrl) { return await SaveImageBytes(user, id, WebHelper.BytesForImageUrl(imageUrl)); } - public async Task> UploadGenreImage(User user, Guid id, IFormFile file) + public async Task> UploadGenreImage(User user, Guid id, IFormFile file) { var bytes = new byte[0]; using (var ms = new MemoryStream()) @@ -230,24 +230,21 @@ private Task> GenreByIdAction(Guid id, IEnumerable> SaveImageBytes(User user, Guid id, byte[] imageBytes) + private async Task> SaveImageBytes(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)); + if (genre == null) return new OperationResult(true, string.Format("Genre Not Found [{0}]", id)); try { var now = DateTime.UtcNow; - genre.Thumbnail = imageBytes; - if (genre.Thumbnail != null) + if (imageBytes != null) { // Save unaltered label image File.WriteAllBytes(genre.PathToImage(Configuration), ImageHelper.ConvertToJpegFormat(imageBytes)); - genre.Thumbnail = ImageHelper.ResizeToThumbnail(genre.Thumbnail, Configuration); } - genre.LastUpdated = now; await DbContext.SaveChangesAsync(); CacheManager.ClearRegion(genre.CacheRegion); @@ -261,7 +258,7 @@ private async Task> SaveImageBytes(User user, Guid id, by sw.Stop(); - return new OperationResult + return new OperationResult { IsSuccess = !errors.Any(), Data = MakeThumbnailImage(Configuration, HttpContext, id, "genre", Configuration.MediumImageSize.Width, Configuration.MediumImageSize.Height, true), diff --git a/Roadie.Api.Services/IAdminService.cs b/Roadie.Api.Services/IAdminService.cs index 582aa59..910f0d3 100644 --- a/Roadie.Api.Services/IAdminService.cs +++ b/Roadie.Api.Services/IAdminService.cs @@ -52,5 +52,7 @@ public interface IAdminService Task> UpdateInviteTokenUsed(Guid? tokenId); Task> ValidateInviteToken(Guid? tokenId); + + Task> MigrateImages(ApplicationUser user); } } \ No newline at end of file diff --git a/Roadie.Api.Services/IImageService.cs b/Roadie.Api.Services/IImageService.cs index 34b8dda..1e1e42f 100644 --- a/Roadie.Api.Services/IImageService.cs +++ b/Roadie.Api.Services/IImageService.cs @@ -1,6 +1,7 @@ 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; @@ -15,30 +16,28 @@ public interface IImageService string RequestIp { get; set; } - Task> ArtistImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); + Task> ArtistImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); - Task> ArtistSecondaryImage(Guid id, int imageId, int? width, int? height, EntityTagHeaderValue etag = null); + Task> ArtistSecondaryImage(Guid id, int imageId, int? width, int? height, EntityTagHeaderValue etag = null); - Task> ById(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); + Task> ById(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); - Task> CollectionImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); + Task> CollectionImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); - Task> Delete(ApplicationUser user, Guid id); + Task> GenreImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); - Task> GenreImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); + Task> LabelImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); - Task> LabelImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); + Task> PlaylistImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); - Task> PlaylistImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); + Task> ReleaseImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); - Task> ReleaseImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); - - Task> ReleaseSecondaryImage(Guid id, int imageId, int? width, int? height, EntityTagHeaderValue etag = null); + Task> ReleaseSecondaryImage(Guid id, int imageId, int? width, int? height, EntityTagHeaderValue etag = null); Task>> Search(string query, int resultsCount = 10); - Task> TrackImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); + Task> TrackImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); - Task> UserImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); + Task> UserImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); } } \ No newline at end of file diff --git a/Roadie.Api.Services/ImageService.cs b/Roadie.Api.Services/ImageService.cs index b2559dd..b5bcaec 100644 --- a/Roadie.Api.Services/ImageService.cs +++ b/Roadie.Api.Services/ImageService.cs @@ -1,5 +1,4 @@ -using Mapster; -using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Microsoft.Net.Http.Headers; using Roadie.Library; @@ -51,7 +50,7 @@ public ImageService(IRoadieSettings configuration, data.IRoadieDbContext dbConte DefaultNotFoundImages = defaultNotFoundImages; } - public async Task> ArtistImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null) + public async Task> ArtistImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null) { return await GetImageFileOperation("ArtistImage", data.Artist.CacheRegionUrn(id), @@ -62,10 +61,10 @@ public async Task> ArtistImage(Guid id, int? width, i etag); } - public async Task> ArtistSecondaryImage(Guid id, int imageId, int? width, int? height, EntityTagHeaderValue etag = null) + public async Task> ArtistSecondaryImage(Guid id, int imageId, int? width, int? height, EntityTagHeaderValue etag = null) { - return await GetImageFileOperation($"ArtistSecondaryThumbnail-{imageId}", - data.Release.CacheRegionUrn(id), + return await GetImageFileOperation($"ArtistSecondaryThumbnail.{imageId}", + data.Artist.CacheRegionUrn(id), id, width, height, @@ -73,10 +72,10 @@ public async Task> ArtistSecondaryImage(Guid id, int etag); } - public async Task> ById(Guid id, int? width, int? height, EntityTagHeaderValue etag = null) + public async Task> ById(Guid id, int? width, int? height, EntityTagHeaderValue etag = null) { return await GetImageFileOperation("ImageById", - data.Image.CacheRegionUrn(id), + Library.Imaging.Image.CacheRegionUrn(id), id, width, height, @@ -84,7 +83,7 @@ public async Task> ById(Guid id, int? width, int? hei etag); } - public async Task> CollectionImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null) + public async Task> CollectionImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null) { return await GetImageFileOperation("CollectionThumbnail", data.Collection.CacheRegionUrn(id), @@ -95,33 +94,10 @@ public async Task> CollectionImage(Guid id, int? widt etag); } - public async Task> Delete(ApplicationUser user, Guid id) - { - var sw = Stopwatch.StartNew(); - var image = DbContext.Images - .Include("Release") - .Include("Artist") - .FirstOrDefault(x => x.RoadieId == id); - if (image == null) return new OperationResult(true, string.Format("Image Not Found [{0}]", id)); - DbContext.Images.Remove(image); - await DbContext.SaveChangesAsync(); - if (image.ArtistId.HasValue) CacheManager.ClearRegion(data.Artist.CacheRegionUrn(image.Artist.RoadieId)); - if (image.ReleaseId.HasValue) CacheManager.ClearRegion(data.Release.CacheRegionUrn(image.Release.RoadieId)); - CacheManager.ClearRegion(data.Image.CacheRegionUrn(id)); - Logger.LogWarning("User `{0}` deleted Image `{1}]`", user, image); - sw.Stop(); - return new OperationResult - { - Data = true, - IsSuccess = true, - OperationTime = sw.ElapsedMilliseconds - }; - } - - public async Task> GenreImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null) + public async Task> GenreImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null) { return await GetImageFileOperation("GenreThumbnail", - data.Label.CacheRegionUrn(id), + data.Genre.CacheRegionUrn(id), id, width, height, @@ -129,7 +105,7 @@ public async Task> GenreImage(Guid id, int? width, in etag); } - public async Task> LabelImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null) + public async Task> LabelImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null) { return await GetImageFileOperation("LabelThumbnail", data.Label.CacheRegionUrn(id), @@ -140,7 +116,7 @@ public async Task> LabelImage(Guid id, int? width, in etag); } - public async Task> PlaylistImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null) + public async Task> PlaylistImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null) { return await GetImageFileOperation("PlaylistThumbnail", data.Playlist.CacheRegionUrn(id), @@ -151,7 +127,7 @@ public async Task> PlaylistImage(Guid id, int? width, etag); } - public async Task> ReleaseImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null) + public async Task> ReleaseImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null) { return await GetImageFileOperation("ReleaseThumbnail", data.Release.CacheRegionUrn(id), @@ -162,7 +138,7 @@ public async Task> ReleaseImage(Guid id, int? width, etag); } - public async Task> ReleaseSecondaryImage(Guid id, int imageId, int? width, int? height, EntityTagHeaderValue etag = null) + public async Task> ReleaseSecondaryImage(Guid id, int imageId, int? width, int? height, EntityTagHeaderValue etag = null) { return await GetImageFileOperation($"ReleaseSecondaryThumbnail-{imageId}", data.Release.CacheRegionUrn(id), @@ -199,7 +175,7 @@ public async Task>> Search(string }; } - public async Task> TrackImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null) + public async Task> TrackImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null) { return await GetImageFileOperation("TrackThumbnail", data.Track.CacheRegionUrn(id), @@ -210,7 +186,7 @@ public async Task> TrackImage(Guid id, int? width, in etag); } - public async Task> UserImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null) + public async Task> UserImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null) { return await GetImageFileOperation("UserById", ApplicationUser.CacheRegionUrn(id), @@ -224,14 +200,14 @@ public async Task> UserImage(Guid id, int? width, int /// /// 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. /// - private Task> ArtistImageAction(Guid id, EntityTagHeaderValue etag = null) + private Task> ArtistImageAction(Guid id, EntityTagHeaderValue etag = null) { try { var artist = GetArtist(id); if (artist == null) { - return Task.FromResult(new FileOperationResult(true, string.Format("Artist Not Found [{0}]", id))); + return Task.FromResult(new FileOperationResult(true, string.Format("Artist Not Found [{0}]", id))); } byte[] imageBytes = null; string artistFolder = null; @@ -257,12 +233,9 @@ private Task> ArtistImageAction(Guid id, EntityTagHea Logger.LogError(ex, $"Error Reading Folder [{artistFolder}] For Artist [{artist.Id}]"); } - imageBytes = imageBytes ?? artist.Thumbnail; - var image = new data.Image + IImage image = new Library.Imaging.Image(id) { Bytes = imageBytes, - CreatedDate = artist.CreatedDate, - LastUpdated = artist.LastUpdated }; if (imageBytes == null || !imageBytes.Any()) { @@ -275,17 +248,18 @@ private Task> ArtistImageAction(Guid id, EntityTagHea Logger.LogError($"Error fetching Artist Thumbnail [{id}]", ex); } - return Task.FromResult(new FileOperationResult(OperationMessages.ErrorOccured)); + return Task.FromResult(new FileOperationResult(OperationMessages.ErrorOccured)); } - private Task> ArtistSecondaryImageAction(Guid id, int imageId, EntityTagHeaderValue etag = null) + private Task> ArtistSecondaryImageAction(Guid id, int imageId, EntityTagHeaderValue etag = null) { try { var artist = GetArtist(id); if (artist == null) - return Task.FromResult(new FileOperationResult(true, - string.Format("Release Not Found [{0}]", id))); + { + return Task.FromResult(new FileOperationResult(true, string.Format("Release Not Found [{0}]", id))); + } byte[] imageBytes = null; string artistFolder = null; try @@ -310,11 +284,10 @@ private Task> ArtistSecondaryImageAction(Guid id, int Logger.LogError(ex, $"Error Reading Artist Folder [{artistFolder}] For Artist `{artist}`"); } - var image = new data.Image + var image = new Library.Imaging.Image(id) { Bytes = imageBytes, - CreatedDate = artist.CreatedDate, - LastUpdated = artist.LastUpdated + CreatedDate = artist.CreatedDate }; return Task.FromResult(GenerateFileOperationResult(id, image, etag)); } @@ -323,23 +296,21 @@ private Task> ArtistSecondaryImageAction(Guid id, int Logger.LogError($"Error fetching Release Thumbnail [{id}]", ex); } - return Task.FromResult(new FileOperationResult(OperationMessages.ErrorOccured)); + return Task.FromResult(new FileOperationResult(OperationMessages.ErrorOccured)); } - private Task> CollectionImageAction(Guid id, EntityTagHeaderValue etag = null) + private Task> CollectionImageAction(Guid id, EntityTagHeaderValue etag = null) { try { var collection = GetCollection(id); if (collection == null) { - return Task.FromResult(new FileOperationResult(true, string.Format("Collection Not Found [{0}]", id))); + return Task.FromResult(new FileOperationResult(true, string.Format("Collection Not Found [{0}]", id))); } - var image = new data.Image + IImage image = new Library.Imaging.Image(id) { - Bytes = collection.Thumbnail, - CreatedDate = collection.CreatedDate, - LastUpdated = collection.LastUpdated + CreatedDate = collection.CreatedDate }; var collectionImageFilename = collection.PathToImage(Configuration); try @@ -353,7 +324,7 @@ private Task> CollectionImageAction(Guid id, EntityTa { Logger.LogError(ex, $"Error Reading Image File [{collectionImageFilename}]"); } - if (collection.Thumbnail == null || !collection.Thumbnail.Any()) + if (image.Bytes == null || !image.Bytes.Any()) { image = DefaultNotFoundImages.Collection; } @@ -364,46 +335,44 @@ private Task> CollectionImageAction(Guid id, EntityTa Logger.LogError($"Error fetching Collection Thumbnail [{id}]", ex); } - return Task.FromResult(new FileOperationResult(OperationMessages.ErrorOccured)); + return Task.FromResult(new FileOperationResult(OperationMessages.ErrorOccured)); } - private FileOperationResult GenerateFileOperationResult(Guid id, data.Image image, EntityTagHeaderValue etag = null, string contentType = "image/jpeg") + private FileOperationResult GenerateFileOperationResult(Guid id, IImage image, EntityTagHeaderValue etag = null, string contentType = "image/jpeg") { var imageEtag = EtagHelper.GenerateETag(HttpEncoder, image.Bytes); if (EtagHelper.CompareETag(HttpEncoder, etag, imageEtag)) { - return new FileOperationResult(OperationMessages.NotModified); + return new FileOperationResult(OperationMessages.NotModified); } if (image?.Bytes?.Any() != true) { - return new FileOperationResult(string.Format("ImageById Not Set [{0}]", id)); + return new FileOperationResult(string.Format("ImageById Not Set [{0}]", id)); } - return new FileOperationResult(image?.Bytes?.Any() ?? false + return new FileOperationResult(image?.Bytes?.Any() ?? false ? OperationMessages.OkMessage : OperationMessages.NoImageDataFound) { IsSuccess = true, - Data = image.Adapt(), + Data = image, ContentType = contentType, - LastModified = image.LastUpdated ?? image.CreatedDate, + LastModified = image.CreatedDate, ETag = imageEtag }; } - private Task> GenreImageAction(Guid id, EntityTagHeaderValue etag = null) + private Task> GenreImageAction(Guid id, EntityTagHeaderValue etag = null) { try { var genre = GetGenre(id); if (genre == null) { - return Task.FromResult(new FileOperationResult(true, string.Format("Genre Not Found [{0}]", id))); + return Task.FromResult(new FileOperationResult(true, string.Format("Genre Not Found [{0}]", id))); } - var image = new data.Image + IImage image = new Library.Imaging.Image(id) { - Bytes = genre.Thumbnail, - CreatedDate = genre.CreatedDate, - LastUpdated = genre.LastUpdated + CreatedDate = genre.CreatedDate }; var genreImageFilename = genre.PathToImage(Configuration); try @@ -417,7 +386,7 @@ private Task> GenreImageAction(Guid id, EntityTagHead { Logger.LogError(ex, $"Error Reading Image File [{genreImageFilename}]"); } - if (genre.Thumbnail == null || !genre.Thumbnail.Any()) + if (image.Bytes == null || !image.Bytes.Any()) { image = DefaultNotFoundImages.Genre; } @@ -428,22 +397,23 @@ private Task> GenreImageAction(Guid id, EntityTagHead Logger.LogError($"Error fetching Label Thumbnail [{id}]", ex); } - return Task.FromResult(new FileOperationResult(OperationMessages.ErrorOccured)); + return Task.FromResult(new FileOperationResult(OperationMessages.ErrorOccured)); } - private async Task> GetImageFileOperation(string type, string regionUrn, Guid id, int? width, int? height, Func>> action, EntityTagHeaderValue etag = null) + private async Task> GetImageFileOperation(string type, string regionUrn, Guid id, int? width, int? height, Func>> action, EntityTagHeaderValue etag = null) { try { var sw = Stopwatch.StartNew(); - var result = (await CacheManager.GetAsync($"urn:{type}_by_id_operation:{id}", action, regionUrn)).Adapt>(); + var sizeHash = (width ?? 0) + (height ?? 0); + var result = await CacheManager.GetAsync($"urn:{type}_by_id_operation:{id}:{sizeHash}", action, regionUrn); if (!result.IsSuccess) { - return new FileOperationResult(result.IsNotFoundResult, result.Messages); + return new FileOperationResult(result.IsNotFoundResult, result.Messages); } if (result.ETag == etag && etag != null) { - return new FileOperationResult(OperationMessages.NotModified); + return new FileOperationResult(OperationMessages.NotModified); } var force = width.HasValue || height.HasValue; var newWidth = width ?? Configuration.MaximumImageSize.Width; @@ -468,7 +438,7 @@ private async Task> GetImageFileOperation(string type } sw.Stop(); - return new FileOperationResult(result.Messages) + return new FileOperationResult(result.Messages) { Data = result.Data, ETag = result.ETag, @@ -484,43 +454,46 @@ private async Task> GetImageFileOperation(string type Logger.LogError(ex, $"GetImageFileOperation Error, Type [{type}], id [{id}]"); } - return new FileOperationResult("System Error"); + return new FileOperationResult("System Error"); } - private Task> ImageByIdAction(Guid id, EntityTagHeaderValue etag = null) + private Task> ImageByIdAction(Guid id, EntityTagHeaderValue etag = null) { try { - var image = DbContext.Images - .Include("Release") - .Include("Artist") - .FirstOrDefault(x => x.RoadieId == id); - if (image == null) - return Task.FromResult(new FileOperationResult(true, - string.Format("ImageById Not Found [{0}]", id))); - return Task.FromResult(GenerateFileOperationResult(id, image, etag)); + // TODO #29; fetch image by ID + + throw new NotImplementedException(); + + //var image = DbContext.Images + // .Include("Release") + // .Include("Artist") + // .FirstOrDefault(x => x.RoadieId == id); + //if (image == null) + //{ + // return Task.FromResult(new FileOperationResult(true, string.Format("ImageById Not Found [{0}]", id))); + //} + //return Task.FromResult(GenerateFileOperationResult(id, image, etag)); } catch (Exception ex) { Logger.LogError($"Error fetching Image [{id}]", ex); } - return Task.FromResult(new FileOperationResult(OperationMessages.ErrorOccured)); + return Task.FromResult(new FileOperationResult(OperationMessages.ErrorOccured)); } - private Task> LabelImageAction(Guid id, EntityTagHeaderValue etag = null) + private Task> LabelImageAction(Guid id, EntityTagHeaderValue etag = null) { try { var label = GetLabel(id); if (label == null) - return Task.FromResult(new FileOperationResult(true, + return Task.FromResult(new FileOperationResult(true, string.Format("Label Not Found [{0}]", id))); - var image = new data.Image + IImage image = new Library.Imaging.Image(id) { - Bytes = label.Thumbnail, - CreatedDate = label.CreatedDate, - LastUpdated = label.LastUpdated + CreatedDate = label.CreatedDate }; var labelImageFilename = label.PathToImage(Configuration); try @@ -534,7 +507,7 @@ private Task> LabelImageAction(Guid id, EntityTagHead { Logger.LogError(ex, $"Error Reading Image File [{labelImageFilename}]"); } - if (label.Thumbnail == null || !label.Thumbnail.Any()) + if (image.Bytes == null || !image.Bytes.Any()) { image = DefaultNotFoundImages.Label; } @@ -545,22 +518,20 @@ private Task> LabelImageAction(Guid id, EntityTagHead Logger.LogError($"Error fetching Label Thumbnail [{id}]", ex); } - return Task.FromResult(new FileOperationResult(OperationMessages.ErrorOccured)); + return Task.FromResult(new FileOperationResult(OperationMessages.ErrorOccured)); } - private Task> PlaylistImageAction(Guid id, EntityTagHeaderValue etag = null) + private Task> PlaylistImageAction(Guid id, EntityTagHeaderValue etag = null) { try { var playlist = GetPlaylist(id); if (playlist == null) - return Task.FromResult(new FileOperationResult(true, + return Task.FromResult(new FileOperationResult(true, string.Format("Playlist Not Found [{0}]", id))); - var image = new data.Image + IImage image = new Library.Imaging.Image(id) { - Bytes = playlist.Thumbnail, - CreatedDate = playlist.CreatedDate, - LastUpdated = playlist.LastUpdated + CreatedDate = playlist.CreatedDate }; var playlistImageFilename = playlist.PathToImage(Configuration); try @@ -574,7 +545,7 @@ private Task> PlaylistImageAction(Guid id, EntityTagH { Logger.LogError(ex, $"Error Reading Image File [{playlistImageFilename}]"); } - if (playlist.Thumbnail == null || !playlist.Thumbnail.Any()) + if (image.Bytes == null || !image.Bytes.Any()) { image = DefaultNotFoundImages.Playlist; } @@ -585,20 +556,20 @@ private Task> PlaylistImageAction(Guid id, EntityTagH Logger.LogError($"Error fetching Playlist Thumbnail [{id}]", ex); } - return Task.FromResult(new FileOperationResult(OperationMessages.ErrorOccured)); + return Task.FromResult(new FileOperationResult(OperationMessages.ErrorOccured)); } /// /// Get image for Release from Release folder, if not exists then use Release.Thumbnail if that is not set then use Release DefaultNotFound image. /// - private Task> ReleaseImageAction(Guid id, EntityTagHeaderValue etag = null) + private Task> ReleaseImageAction(Guid id, EntityTagHeaderValue etag = null) { try { var release = GetRelease(id); if (release == null) { - return Task.FromResult(new FileOperationResult(true, string.Format("Release Not Found [{0}]", id))); + return Task.FromResult(new FileOperationResult(true, string.Format("Release Not Found [{0}]", id))); } byte[] imageBytes = null; string artistFolder = null; @@ -632,15 +603,12 @@ private Task> ReleaseImageAction(Guid id, EntityTagHe { Logger.LogError(ex, $"Error Reading Release Folder [{releaseFolder}] Artist Folder [{artistFolder}] For Artist `{release.Artist.Id}`"); } - - imageBytes = imageBytes ?? release.Thumbnail; - var image = new data.Image + IImage image = new Library.Imaging.Image(id) { Bytes = imageBytes, - CreatedDate = release.CreatedDate, - LastUpdated = release.LastUpdated + CreatedDate = release.CreatedDate }; - if (release.Thumbnail?.Any() != true) + if (imageBytes?.Any() != true) { image = DefaultNotFoundImages.Release; } @@ -651,18 +619,19 @@ private Task> ReleaseImageAction(Guid id, EntityTagHe Logger.LogError($"Error fetching Release Thumbnail [{id}]", ex); } - return Task.FromResult(new FileOperationResult(OperationMessages.ErrorOccured)); + return Task.FromResult(new FileOperationResult(OperationMessages.ErrorOccured)); } - private Task> ReleaseSecondaryImageAction(Guid id, int imageId, + private Task> ReleaseSecondaryImageAction(Guid id, int imageId, EntityTagHeaderValue etag = null) { try { var release = GetRelease(id); if (release == null) - return Task.FromResult(new FileOperationResult(true, - string.Format("Release Not Found [{0}]", id))); + { + return Task.FromResult(new FileOperationResult(true, string.Format("Release Not Found [{0}]", id))); + } byte[] imageBytes = null; string artistFolder = null; string releaseFolder = null; @@ -697,11 +666,10 @@ private Task> ReleaseSecondaryImageAction(Guid id, in $"Error Reading Release Folder [{releaseFolder}] Artist Folder [{artistFolder}] For Artist `{release.Artist.Id}`"); } - var image = new data.Image + IImage image = new Library.Imaging.Image(id) { Bytes = imageBytes, - CreatedDate = release.CreatedDate, - LastUpdated = release.LastUpdated + CreatedDate = release.CreatedDate }; return Task.FromResult(GenerateFileOperationResult(id, image, etag)); } @@ -710,31 +678,32 @@ private Task> ReleaseSecondaryImageAction(Guid id, in Logger.LogError($"Error fetching Release Thumbnail [{id}]", ex); } - return Task.FromResult(new FileOperationResult(OperationMessages.ErrorOccured)); + return Task.FromResult(new FileOperationResult(OperationMessages.ErrorOccured)); } - private async Task> TrackImageAction(Guid id, int? width, int? height, - EntityTagHeaderValue etag = null) + private async Task> TrackImageAction(Guid id, int? width, int? height, EntityTagHeaderValue etag = null) { try { var track = GetTrack(id); if (track == null) - return new FileOperationResult(true, string.Format("Track Not Found [{0}]", id)); - var imageBytes = track.Thumbnail; - var trackThumbnailImages = ImageHelper.FindImageTypeInDirectory( - new DirectoryInfo(track.PathToTrackThumbnail(Configuration)), - ImageType.Track, SearchOption.TopDirectoryOnly); - if (trackThumbnailImages.Any()) imageBytes = File.ReadAllBytes(trackThumbnailImages.First().FullName); - var image = new data.Image - { - Bytes = track.Thumbnail, - CreatedDate = track.CreatedDate, - LastUpdated = track.LastUpdated + { + return new FileOperationResult(true, string.Format("Track Not Found [{0}]", id)); + } + IImage image = new Library.Imaging.Image(id) + { + CreatedDate = track.CreatedDate }; - if (track.Thumbnail == null || !track.Thumbnail.Any()) + var trackThumbnailImages = ImageHelper.FindImageTypeInDirectory( new DirectoryInfo(track.PathToTrackThumbnail(Configuration)), ImageType.Track, SearchOption.TopDirectoryOnly); + if (trackThumbnailImages.Any()) + { + image.Bytes = File.ReadAllBytes(trackThumbnailImages.First().FullName); + } + if (image.Bytes == null || !image.Bytes.Any()) + { // If no track image is found then return image for release return await ReleaseImage(track.ReleaseMedia.Release.RoadieId, width, height, etag); + } return GenerateFileOperationResult(id, image, etag); } catch (Exception ex) @@ -742,23 +711,21 @@ private async Task> TrackImageAction(Guid id, int? wi Logger.LogError($"Error fetching Track Thumbnail [{id}]", ex); } - return new FileOperationResult(OperationMessages.ErrorOccured); + return new FileOperationResult(OperationMessages.ErrorOccured); } - private Task> UserImageAction(Guid id, EntityTagHeaderValue etag = null) + private Task> UserImageAction(Guid id, EntityTagHeaderValue etag = null) { try { var user = GetUser(id); if (user == null) { - return Task.FromResult(new FileOperationResult(true, string.Format("User Not Found [{0}]", id))); + return Task.FromResult(new FileOperationResult(true, string.Format("User Not Found [{0}]", id))); } - var image = new data.Image + IImage image = new Library.Imaging.Image(id) { - Bytes = user.Avatar, - CreatedDate = user.CreatedDate.Value, - LastUpdated = user.LastUpdated + CreatedDate = user.CreatedDate.Value }; var userImageFilename = user.PathToImage(Configuration); try @@ -772,7 +739,7 @@ private Task> UserImageAction(Guid id, EntityTagHeade { Logger.LogError(ex, $"Error Reading Image File [{userImageFilename}]"); } - if (!(image?.Bytes?.Any() ?? false)) + if (image.Bytes == null || !image.Bytes.Any()) { image = DefaultNotFoundImages.User; } @@ -783,7 +750,7 @@ private Task> UserImageAction(Guid id, EntityTagHeade Logger.LogError($"Error fetching User Thumbnail [{id}]", ex); } - return Task.FromResult(new FileOperationResult(OperationMessages.ErrorOccured)); + return Task.FromResult(new FileOperationResult(OperationMessages.ErrorOccured)); } } } \ No newline at end of file diff --git a/Roadie.Api.Services/LabelService.cs b/Roadie.Api.Services/LabelService.cs index dec038f..0173bd2 100644 --- a/Roadie.Api.Services/LabelService.cs +++ b/Roadie.Api.Services/LabelService.cs @@ -218,7 +218,6 @@ join ltm in labelIdsToMerge on l.RoadieId equals ltm { label.MusicBrainzId = label.MusicBrainzId ?? labelToMerge.MusicBrainzId; label.SortName = label.SortName ?? labelToMerge.SortName; - label.Thumbnail = label.Thumbnail ?? labelToMerge.Thumbnail; label.Profile = label.Profile ?? labelToMerge.Profile; label.BeginDate = label.BeginDate ?? labelToMerge.BeginDate; label.EndDate = label.EndDate ?? labelToMerge.EndDate; @@ -259,7 +258,7 @@ join ltm in labelIdsToMerge on l.RoadieId equals ltm }; } - public async Task> SetLabelImageByUrl(User user, Guid id, string imageUrl) + public async Task> SetLabelImageByUrl(User user, Guid id, string imageUrl) { return await SaveImageBytes(user, id, WebHelper.BytesForImageUrl(imageUrl)); } @@ -312,15 +311,12 @@ public async Task> UpdateLabel(User user, Label model) File.Move(oldPathToImage, label.PathToImage(Configuration)); } } - var labelImage = ImageHelper.ImageDataFromUrl(model.NewThumbnailData); if (labelImage != null) { // Save unaltered label image File.WriteAllBytes(label.PathToImage(Configuration), ImageHelper.ConvertToJpegFormat(labelImage)); - label.Thumbnail = ImageHelper.ResizeToThumbnail(labelImage, Configuration); } - label.LastUpdated = now; await DbContext.SaveChangesAsync(); @@ -344,7 +340,7 @@ public async Task> UpdateLabel(User user, Label model) }; } - public async Task> UploadLabelImage(User user, Guid id, IFormFile file) + public async Task> UploadLabelImage(User user, Guid id, IFormFile file) { var bytes = new byte[0]; using (var ms = new MemoryStream()) @@ -452,24 +448,21 @@ where commentIds.Contains(cr.CommentId) }); } - private async Task> SaveImageBytes(User user, Guid id, byte[] imageBytes) + private async Task> SaveImageBytes(User user, Guid id, byte[] imageBytes) { var sw = new Stopwatch(); sw.Start(); var errors = new List(); var label = DbContext.Labels.FirstOrDefault(x => x.RoadieId == id); - if (label == null) return new OperationResult(true, string.Format("Label Not Found [{0}]", id)); + if (label == null) return new OperationResult(true, string.Format("Label Not Found [{0}]", id)); try { var now = DateTime.UtcNow; - label.Thumbnail = imageBytes; - if (label.Thumbnail != null) + if (imageBytes != null) { // Save unaltered label image File.WriteAllBytes(label.PathToImage(Configuration), ImageHelper.ConvertToJpegFormat(imageBytes)); - label.Thumbnail = ImageHelper.ResizeToThumbnail(label.Thumbnail, Configuration); } - label.LastUpdated = now; await DbContext.SaveChangesAsync(); CacheManager.ClearRegion(label.CacheRegion); @@ -483,7 +476,7 @@ private async Task> SaveImageBytes(User user, Guid id, by sw.Stop(); - return new OperationResult + return new OperationResult { IsSuccess = !errors.Any(), Data = MakeThumbnailImage(Configuration, HttpContext, id, "label", Configuration.MediumImageSize.Width, Configuration.MediumImageSize.Height, true), diff --git a/Roadie.Api.Services/PlaylistService.cs b/Roadie.Api.Services/PlaylistService.cs index 72741bb..e801752 100644 --- a/Roadie.Api.Services/PlaylistService.cs +++ b/Roadie.Api.Services/PlaylistService.cs @@ -353,8 +353,6 @@ public async Task> UpdatePlaylist(User user, Playlist mode { // Save unaltered playlist image File.WriteAllBytes(playlist.PathToImage(Configuration), ImageHelper.ConvertToJpegFormat(playlistImage)); - // Update thumbnail - playlist.Thumbnail = ImageHelper.ResizeToThumbnail(playlistImage, Configuration); } playlist.LastUpdated = now; await DbContext.SaveChangesAsync(); diff --git a/Roadie.Api.Services/ReleaseService.cs b/Roadie.Api.Services/ReleaseService.cs index 84457de..4696f9c 100644 --- a/Roadie.Api.Services/ReleaseService.cs +++ b/Roadie.Api.Services/ReleaseService.cs @@ -91,7 +91,7 @@ public async Task> ById(User roadieUser, Guid id, IEnum var userBookmarkResult = await BookmarkService.List(roadieUser, new PagedRequest(), false, BookmarkType.Release); if (userBookmarkResult.IsSuccess) { - result.Data.UserBookmarked = userBookmarkResult?.Rows?.FirstOrDefault(x => x.Bookmark.Value == release.RoadieId.ToString()) != null; + result.Data.UserBookmarked = userBookmarkResult?.Rows?.FirstOrDefault(x => x.Bookmark?.Value == release.RoadieId.ToString()) != null; } if (result.Data.Medias != null) { @@ -297,9 +297,21 @@ join t in DbContext.Tracks on rm.Id equals t.ReleaseMediaId } 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}` Exception [{ex}]"); + } + } + + // Delete all image files for Release + foreach(var file in ImageHelper.ImageFilesInFolder(release.ReleaseFileFolder(release.Artist.ArtistFileFolder(Configuration)), SearchOption.AllDirectories)) + { + try + { + File.Delete(file); + Logger.LogWarning("For Release [{0}], Deleted File [{1}]", release.Id, file); + } + catch (Exception ex) + { + Logger.LogError(ex, $"Error Deleting File [{file}] Exception [{ex}]"); } } @@ -898,7 +910,6 @@ public async Task> MergeReleases(ApplicationUser user, dat if (mergeTrack.LastPlayed.HasValue && existingTrack.LastPlayed.HasValue && mergeTrack.LastPlayed > existingTrack.LastPlayed) existingTrack.LastPlayed = mergeTrack.LastPlayed; - existingTrack.Thumbnail = existingTrack.Thumbnail ?? mergeTrack.Thumbnail; existingTrack.MusicBrainzId = existingTrack.MusicBrainzId ?? mergeTrack.MusicBrainzId; existingTrack.Tags = existingTrack.Tags.AddToDelimitedList(mergeTrack.Tags.ToListFromDelimited()); if (!mergeTrack.Title.Equals(existingTrack.Title, @@ -962,7 +973,6 @@ public async Task> MergeReleases(ApplicationUser user, dat releaseToMergeInto.LastFMId = releaseToMergeInto.LastFMId ?? releaseToMerge.LastFMId; releaseToMergeInto.LastFMSummary = releaseToMergeInto.LastFMSummary ?? releaseToMerge.LastFMSummary; releaseToMergeInto.SpotifyId = releaseToMergeInto.SpotifyId ?? releaseToMerge.SpotifyId; - releaseToMergeInto.Thumbnail = releaseToMergeInto.Thumbnail ?? releaseToMerge.Thumbnail; if (releaseToMergeInto.ReleaseType == ReleaseType.Unknown && releaseToMerge.ReleaseType != ReleaseType.Unknown) releaseToMergeInto.ReleaseType = releaseToMerge.ReleaseType; @@ -1466,29 +1476,15 @@ join rm in DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id #endregion Scan Folder and Add or Update Existing Tracks from Files - if (release.Thumbnail == null) - { - var imageFiles = ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(releasePath), ImageType.Release, SearchOption.TopDirectoryOnly); - if (imageFiles != null && imageFiles.Any()) - { - // Read image and convert to jpeg - var i = imageFiles.First(); - release.Thumbnail = ImageHelper.ResizeToThumbnail(File.ReadAllBytes(i.FullName), Configuration); - - release.LastUpdated = now; - await DbContext.SaveChangesAsync(); - CacheManager.ClearRegion(release.Artist.CacheRegion); - CacheManager.ClearRegion(release.CacheRegion); - Logger.LogInformation("Update Thumbnail using Release Cover File [{0}]", i.Name); - } - } - sw.Stop(); await UpdateReleaseCounts(release.Id, now); await UpdateArtistCountsForRelease(release.Id, now); await UpdateReleaseRank(release.Id); + CacheManager.ClearRegion(release.Artist.CacheRegion); + CacheManager.ClearRegion(release.CacheRegion); + if (release.Labels != null && release.Labels.Any()) { foreach (var label in release.Labels) @@ -1522,7 +1518,6 @@ public async Task> SetReleaseImageByUrl(ApplicationUser u public async Task> UpdateRelease(ApplicationUser user, Release model, string originalReleaseFolder = null) { var didChangeArtist = false; - var didChangeThumbnail = false; var sw = new Stopwatch(); sw.Start(); var errors = new List(); @@ -1595,10 +1590,6 @@ public async Task> UpdateRelease(ApplicationUser user, Rel // Save unaltered image to cover file var coverFileName = Path.Combine(release.ReleaseFileFolder(release.Artist.ArtistFileFolder(Configuration)), "cover.jpg"); File.WriteAllBytes(coverFileName, ImageHelper.ConvertToJpegFormat(releaseImage)); - - // Resize to store in database as thumbnail - release.Thumbnail = ImageHelper.ResizeToThumbnail(releaseImage, Configuration); - didChangeThumbnail = true; } var releaseFolder = release.ReleaseFileFolder(release.Artist.ArtistFileFolder(Configuration)); if (model.NewSecondaryImagesData != null && model.NewSecondaryImagesData.Any()) @@ -1667,11 +1658,6 @@ public async Task> UpdateRelease(ApplicationUser user, Rel // TODO } - if (model.Images != null && model.Images.Any()) - { - // TODO - } - release.LastUpdated = now; await DbContext.SaveChangesAsync(); await CheckAndChangeReleaseTitle(release, originalReleaseFolder); @@ -1692,7 +1678,7 @@ public async Task> UpdateRelease(ApplicationUser user, Rel } } CacheManager.ClearRegion(release.CacheRegion); - Logger.LogInformation($"UpdateRelease `{release}` By User `{user}`: Edited Artist [{didChangeArtist}], Uploaded new image [{didChangeThumbnail}]"); + Logger.LogInformation($"UpdateRelease `{release}` By User `{user}`: Edited Artist [{didChangeArtist}]"); } catch (Exception ex) { @@ -1839,17 +1825,12 @@ join rm in DbContext.ReleaseMedias on r.Id equals rm.ReleaseId if (includes.Contains("images")) { tsw.Restart(); - var releaseImages = DbContext.Images.Where(x => x.ReleaseId == release.Id).Select(x => MakeFullsizeImage(Configuration, HttpContext, x.RoadieId, x.Caption)).ToArray(); - if (releaseImages != null && releaseImages.Any()) - { - result.Images = releaseImages; - } var artistFolder = release.Artist.ArtistFileFolder(Configuration); var releaseFolder = release.ReleaseFileFolder(artistFolder); var releaseImagesInFolder = ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(releaseFolder), ImageType.ReleaseSecondary, SearchOption.TopDirectoryOnly); if (releaseImagesInFolder.Any()) { - result.Images = result.Images.Concat(releaseImagesInFolder.Select((x, i) => MakeFullsizeSecondaryImage(Configuration, HttpContext, id, ImageType.ReleaseSecondary, i))); + result.Images = releaseImagesInFolder.Select((x, i) => MakeFullsizeSecondaryImage(Configuration, HttpContext, id, ImageType.ReleaseSecondary, i)); } tsw.Stop(); timings.Add("images", tsw.ElapsedMilliseconds); @@ -2045,17 +2026,12 @@ private async Task> SaveImageBytes(ApplicationUser user, try { var now = DateTime.UtcNow; - release.Thumbnail = imageBytes; - if (release.Thumbnail != null) + if (imageBytes != null) { // Save unaltered image to cover file var coverFileName = Path.Combine(release.ReleaseFileFolder(release.Artist.ArtistFileFolder(Configuration)), "cover.jpg"); File.WriteAllBytes(coverFileName, ImageHelper.ConvertToJpegFormat(imageBytes)); - - // Resize to store in database as thumbnail - release.Thumbnail = ImageHelper.ResizeToThumbnail(imageBytes, Configuration); } - release.LastUpdated = now; await DbContext.SaveChangesAsync(); CacheManager.ClearRegion(release.CacheRegion); diff --git a/Roadie.Api.Services/TrackService.cs b/Roadie.Api.Services/TrackService.cs index 7c75e92..ac7c780 100644 --- a/Roadie.Api.Services/TrackService.cs +++ b/Roadie.Api.Services/TrackService.cs @@ -825,7 +825,6 @@ await AdminService.ScanRelease(new ApplicationUser public async Task> UpdateTrack(User user, Track model) { var didChangeTrack = false; - var didChangeThumbnail = false; var sw = new Stopwatch(); sw.Start(); var errors = new List(); @@ -883,10 +882,6 @@ public async Task> UpdateTrack(User user, Track model) // Save unaltered image to cover file var trackThumbnailName = track.PathToTrackThumbnail(Configuration); File.WriteAllBytes(trackThumbnailName, ImageHelper.ConvertToJpegFormat(trackImage)); - - // Resize to store in database as thumbnail - track.Thumbnail = ImageHelper.ResizeToThumbnail(trackImage, Configuration); - didChangeThumbnail = true; } // See if Title was changed if so then modify DB Filename and rename track @@ -911,7 +906,7 @@ public async Task> UpdateTrack(User user, Track model) AudioMetaDataHelper.WriteTags(audioMetaData, trackFileInfo); } CacheManager.ClearRegion(track.CacheRegion); - Logger.LogInformation($"UpdateTrack `{track}` By User `{user}`: Edited Track [{didChangeTrack}], Uploaded new image [{didChangeThumbnail}]"); + Logger.LogInformation($"UpdateTrack `{track}` By User `{user}`: Edited Track [{didChangeTrack}]"); } catch (Exception ex) { diff --git a/Roadie.Api.Services/UserService.cs b/Roadie.Api.Services/UserService.cs index fe68fc2..bdfa99b 100644 --- a/Roadie.Api.Services/UserService.cs +++ b/Roadie.Api.Services/UserService.cs @@ -504,12 +504,6 @@ public async Task> UpdateProfile(User userPerformingUpdate // Save unaltered user image File.WriteAllBytes(user.PathToImage(Configuration), imageData); - // Update thumbnail - user.Avatar = ImageHelper.ResizeImage(imageData, Configuration.ThumbnailImageSize.Width, Configuration.ThumbnailImageSize.Height); - if (user.Avatar.Length >= ImageHelper.MaximumThumbnailByteSize) - { - user.Avatar = null; - } } } diff --git a/Roadie.Api/Controllers/AdminController.cs b/Roadie.Api/Controllers/AdminController.cs index 0d4c900..74f497b 100644 --- a/Roadie.Api/Controllers/AdminController.cs +++ b/Roadie.Api/Controllers/AdminController.cs @@ -337,5 +337,22 @@ public async Task ScanReleases(IEnumerable ids) } return Ok(result); } + + [HttpPost("migrateimages")] + [ProducesResponseType(200)] + public async Task MigrateImages() + { + var result = await AdminService.MigrateImages(await UserManager.GetUserAsync(User)); + if (!result.IsSuccess) + { + if (result.Messages?.Any() ?? false) + { + return StatusCode((int)HttpStatusCode.BadRequest, result.Messages); + } + return StatusCode((int)HttpStatusCode.InternalServerError); + } + return Ok(result); + } + } } \ No newline at end of file diff --git a/Roadie.Api/Controllers/ImageController.cs b/Roadie.Api/Controllers/ImageController.cs index cf7c2b4..af990fe 100644 --- a/Roadie.Api/Controllers/ImageController.cs +++ b/Roadie.Api/Controllers/ImageController.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Authorization; +using Mapster; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; @@ -33,13 +34,19 @@ public ImageController(IImageService imageService, ILogger logg [ProducesResponseType(404)] public async Task ArtistImage(Guid id, int? width, int? height) { - var result = await ImageService.ArtistImage(id, width ?? RoadieSettings.ThumbnailImageSize.Width, - height ?? RoadieSettings.ThumbnailImageSize.Height); - if (result == null || result.IsNotFoundResult) return NotFound(); - if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError); - return File(result.Data.Bytes, + var result = await ImageService.ArtistImage(id, width ?? RoadieSettings.ThumbnailImageSize.Width, height ?? RoadieSettings.ThumbnailImageSize.Height); + if (result == null || result.IsNotFoundResult) + { + return NotFound(); + } + if (!result.IsSuccess) + { + return StatusCode((int)HttpStatusCode.InternalServerError); + } + var model = result.Data.Adapt(); + return File(model.Bytes, result.ContentType, - $"{result.Data.Caption ?? id.ToString()}.jpg", + $"{model.Caption ?? id.ToString()}.jpg", result.LastModified, result.ETag); } @@ -50,11 +57,18 @@ public async Task ArtistImage(Guid id, int? width, int? height) public async Task ArtistSecondaryImage(Guid id, int imageId, int? width, int? height) { var result = await ImageService.ArtistSecondaryImage(id, imageId, width, height); - if (result == null || result.IsNotFoundResult) return NotFound(); - if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError); - return File(result.Data.Bytes, + if (result == null || result.IsNotFoundResult) + { + return NotFound(); + } + if (!result.IsSuccess) + { + return StatusCode((int)HttpStatusCode.InternalServerError); + } + var model = result.Data.Adapt(); + return File(model.Bytes, result.ContentType, - $"{result.Data.Caption ?? id.ToString()}.jpg", + $"{model.Caption ?? id.ToString()}.jpg", result.LastModified, result.ETag); } @@ -64,39 +78,41 @@ public async Task ArtistSecondaryImage(Guid id, int imageId, int? [ProducesResponseType(404)] public async Task CollectionImage(Guid id, int? width, int? height) { - var result = await ImageService.CollectionImage(id, width ?? RoadieSettings.ThumbnailImageSize.Width, - height ?? RoadieSettings.ThumbnailImageSize.Height); - if (result == null || result.IsNotFoundResult) return NotFound(); - if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError); - return File(result.Data.Bytes, + var result = await ImageService.CollectionImage(id, width ?? RoadieSettings.ThumbnailImageSize.Width, height ?? RoadieSettings.ThumbnailImageSize.Height); + if (result == null || result.IsNotFoundResult) + { + return NotFound(); + } + if (!result.IsSuccess) + { + return StatusCode((int)HttpStatusCode.InternalServerError); + } + var model = result.Data.Adapt(); + return File(model.Bytes, result.ContentType, - $"{result.Data.Caption ?? id.ToString()}.jpg", + $"{model.Caption ?? id.ToString()}.jpg", result.LastModified, result.ETag); } - [HttpPost("delete/{id}")] - [Authorize(Policy = "Editor")] - [ProducesResponseType(200)] - public async Task Delete(Guid id) - { - var result = await ImageService.Delete(await UserManager.GetUserAsync(User), id); - if (result == null || result.IsNotFoundResult) return NotFound(); - if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError); - return Ok(result); - } - [HttpGet("{id}/{width:int?}/{height:int?}/{cacheBuster?}")] [ProducesResponseType(200)] [ProducesResponseType(404)] public async Task Get(Guid id, int? width, int? height) { var result = await ImageService.ById(id, width, height); - if (result == null) return NotFound(); - if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError); - return File(result.Data.Bytes, + if (result == null) + { + return NotFound(); + } + if (!result.IsSuccess) + { + return StatusCode((int)HttpStatusCode.InternalServerError); + } + var model = result.Data.Adapt(); + return File(model.Bytes, result.ContentType, - $"{result.Data.Caption ?? id.ToString()}.jpg", + $"{model.Caption ?? id.ToString()}.jpg", result.LastModified, result.ETag); } @@ -106,13 +122,19 @@ public async Task Get(Guid id, int? width, int? height) [ProducesResponseType(404)] public async Task LabelImage(Guid id, int? width, int? height) { - var result = await ImageService.LabelImage(id, width ?? RoadieSettings.ThumbnailImageSize.Width, - height ?? RoadieSettings.ThumbnailImageSize.Height); - if (result == null || result.IsNotFoundResult) return NotFound(); - if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError); - return File(result.Data.Bytes, + var result = await ImageService.LabelImage(id, width ?? RoadieSettings.ThumbnailImageSize.Width, height ?? RoadieSettings.ThumbnailImageSize.Height); + if (result == null || result.IsNotFoundResult) + { + return NotFound(); + } + if (!result.IsSuccess) + { + return StatusCode((int)HttpStatusCode.InternalServerError); + } + var model = result.Data.Adapt(); + return File(model.Bytes, result.ContentType, - $"{result.Data.Caption ?? id.ToString()}.jpg", + $"{model.Caption ?? id.ToString()}.jpg", result.LastModified, result.ETag); } @@ -123,11 +145,18 @@ public async Task LabelImage(Guid id, int? width, int? height) public async Task GenreImage(Guid id, int? width, int? height) { var result = await ImageService.GenreImage(id, width ?? RoadieSettings.ThumbnailImageSize.Width, height ?? RoadieSettings.ThumbnailImageSize.Height); - if (result == null || result.IsNotFoundResult) return NotFound(); - if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError); - return File(result.Data.Bytes, + if (result == null || result.IsNotFoundResult) + { + return NotFound(); + } + if (!result.IsSuccess) + { + return StatusCode((int)HttpStatusCode.InternalServerError); + } + var model = result.Data.Adapt(); + return File(model.Bytes, result.ContentType, - $"{result.Data.Caption ?? id.ToString()}.jpg", + $"{model.Caption ?? id.ToString()}.jpg", result.LastModified, result.ETag); } @@ -137,13 +166,19 @@ public async Task GenreImage(Guid id, int? width, int? height) [ProducesResponseType(404)] public async Task PlaylistImage(Guid id, int? width, int? height) { - var result = await ImageService.PlaylistImage(id, width ?? RoadieSettings.ThumbnailImageSize.Width, - height ?? RoadieSettings.ThumbnailImageSize.Height); - if (result == null || result.IsNotFoundResult) return NotFound(); - if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError); - return File(result.Data.Bytes, + var result = await ImageService.PlaylistImage(id, width ?? RoadieSettings.ThumbnailImageSize.Width, height ?? RoadieSettings.ThumbnailImageSize.Height); + if (result == null || result.IsNotFoundResult) + { + return NotFound(); + } + if (!result.IsSuccess) + { + return StatusCode((int)HttpStatusCode.InternalServerError); + } + var model = result.Data.Adapt(); + return File(model.Bytes, result.ContentType, - $"{result.Data.Caption ?? id.ToString()}.jpg", + $"{model.Caption ?? id.ToString()}.jpg", result.LastModified, result.ETag); } @@ -154,11 +189,18 @@ public async Task PlaylistImage(Guid id, int? width, int? height) public async Task ReleaseImage(Guid id, int? width, int? height) { var result = await ImageService.ReleaseImage(id, width ?? RoadieSettings.ThumbnailImageSize.Width, height ?? RoadieSettings.ThumbnailImageSize.Height); - if (result == null || result.IsNotFoundResult) return NotFound(); - if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError); - return File(result.Data.Bytes, + if (result == null || result.IsNotFoundResult) + { + return NotFound(); + } + if (!result.IsSuccess) + { + return StatusCode((int)HttpStatusCode.InternalServerError); + } + var model = result.Data.Adapt(); + return File(model.Bytes, result.ContentType, - $"{result.Data.Caption ?? id.ToString()}.jpg", + $"{model.Caption ?? id.ToString()}.jpg", result.LastModified, result.ETag); } @@ -168,13 +210,19 @@ public async Task ReleaseImage(Guid id, int? width, int? height) [ProducesResponseType(404)] public async Task ReleaseSecondaryImage(Guid id, int imageId, int? width, int? height) { - var result = await ImageService.ReleaseSecondaryImage(id, imageId, - width ?? RoadieSettings.MaximumImageSize.Width, height ?? RoadieSettings.MaximumImageSize.Height); - if (result == null || result.IsNotFoundResult) return NotFound(); - if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError); - return File(result.Data.Bytes, + var result = await ImageService.ReleaseSecondaryImage(id, imageId, width ?? RoadieSettings.MaximumImageSize.Width, height ?? RoadieSettings.MaximumImageSize.Height); + if (result == null || result.IsNotFoundResult) + { + return NotFound(); + } + if (!result.IsSuccess) + { + return StatusCode((int)HttpStatusCode.InternalServerError); + } + var model = result.Data.Adapt(); + return File(model.Bytes, result.ContentType, - $"{result.Data.Caption ?? id.ToString()}.jpg", + $"{model.Caption ?? id.ToString()}.jpg", result.LastModified, result.ETag); } @@ -185,8 +233,14 @@ public async Task ReleaseSecondaryImage(Guid id, int imageId, int public async Task SearchForArtistImage(string query, int? resultsCount) { var result = await ImageService.Search(query, resultsCount ?? 10); - if (result == null || result.IsNotFoundResult) return NotFound(); - if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError); + if (result == null || result.IsNotFoundResult) + { + return NotFound(); + } + if (!result.IsSuccess) + { + return StatusCode((int)HttpStatusCode.InternalServerError); + } return Ok(result); } @@ -196,8 +250,14 @@ public async Task SearchForArtistImage(string query, int? results public async Task SearchForLabelImage(string query, int? resultsCount) { var result = await ImageService.Search(query, resultsCount ?? 10); - if (result == null || result.IsNotFoundResult) return NotFound(); - if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError); + if (result == null || result.IsNotFoundResult) + { + return NotFound(); + } + if (!result.IsSuccess) + { + return StatusCode((int)HttpStatusCode.InternalServerError); + } return Ok(result); } @@ -229,11 +289,18 @@ public async Task SearchForReleaseCover(string query, int? result public async Task TrackImage(Guid id, int? width, int? height) { var result = await ImageService.TrackImage(id, width ?? RoadieSettings.ThumbnailImageSize.Width, height ?? RoadieSettings.ThumbnailImageSize.Height); - if (result == null || result.IsNotFoundResult) return NotFound(); - if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError); - return File(result.Data.Bytes, + if (result == null || result.IsNotFoundResult) + { + return NotFound(); + } + if (!result.IsSuccess) + { + return StatusCode((int)HttpStatusCode.InternalServerError); + } + var model = result.Data.Adapt(); + return File(model.Bytes, result.ContentType, - $"{result.Data.Caption ?? id.ToString()}.jpg", + $"{model.Caption ?? id.ToString()}.jpg", result.LastModified, result.ETag); } @@ -247,11 +314,18 @@ public async Task TrackImage(Guid id, int? width, int? height) public async Task UserImage(Guid id, int? width, int? height) { var result = await ImageService.UserImage(id, width ?? RoadieSettings.ThumbnailImageSize.Width, height ?? RoadieSettings.ThumbnailImageSize.Height); - if (result == null || result.IsNotFoundResult) return NotFound(); - if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError); - return File(result.Data.Bytes, + if (result == null || result.IsNotFoundResult) + { + return NotFound(); + } + if (!result.IsSuccess) + { + return StatusCode((int)HttpStatusCode.InternalServerError); + } + var model = result.Data.Adapt(); + return File(model.Bytes, result.ContentType, - $"{result.Data.Caption ?? id.ToString()}.gif", + $"{model.Caption ?? id.ToString()}.gif", result.LastModified, result.ETag); }