2
0
Fork 0
mirror of https://github.com/sphildreth/roadie synced 2025-02-17 21:48:27 +00:00

Beta v20180209.1

This commit is contained in:
Steven Hildreth 2019-02-09 18:19:26 -06:00
parent 577dd3515d
commit 31e676392c
22 changed files with 775 additions and 86 deletions

View file

@ -395,6 +395,34 @@ namespace Roadie.Library.Tests
}
}
[Fact]
public void Read_File_Test_Is_valid3()
{
var file = new FileInfo(@"C:\roadie_dev_root\inbound\Dreadful Fate - Vengeance (2018)\01-dreadful_fate-vengeance.mp3");
if (file.Exists)
{
var tagLib = this.TagsHelper.MetaDataForFile(file.FullName);
Assert.True(tagLib.IsSuccess);
var metaData = tagLib.Data;
Assert.NotNull(metaData.Artist);
Assert.Null(metaData.TrackArtist);
Assert.False(metaData.TrackArtists.Any());
Assert.NotNull(metaData.Release);
Assert.NotNull(metaData.Title);
Assert.True(metaData.Year > 0);
Assert.NotNull(metaData.TrackNumber);
Assert.True(metaData.TotalSeconds > 0);
Assert.True(metaData.ValidWeight > 30);
Assert.True(metaData.IsValid);
}
else
{
Console.WriteLine($"skipping { file}");
Assert.True(true);
}
}
[Fact]
public void ReadID3TagsFromFileWithTrackAndArtistTheSame()
{

View file

@ -0,0 +1,250 @@
using Roadie.Library.FilePlugins;
using Roadie.Library.Imaging;
using System.IO;
using System.Linq;
using Xunit;
namespace Roadie.Library.Tests
{
public class ImageHelperTests
{
[Theory]
[InlineData("artist.jpeg")]
[InlineData("artist.jpg")]
[InlineData("artist.png")]
[InlineData("Artist.Jpg")]
[InlineData("Artist.JPG")]
[InlineData("band.jpg")]
[InlineData("group.jpg")]
[InlineData("ARTIST.JPG")]
[InlineData("GrOup.jpg")]
[InlineData("aRtist.jpg")]
public void Test_Should_Be_Artist_Images(string input)
{
Assert.True(ImageHelper.IsArtistImage(new FileInfo(input)));
}
[Theory]
[InlineData("logo.jpeg")]
[InlineData("logo.jpg")]
[InlineData("logo.png")]
[InlineData("Logo.Jpg")]
[InlineData("artist_logo.jpg")]
[InlineData("Artist_logo.jpg")]
[InlineData("ARTIST_LOGO.JPG")]
[InlineData("artist 1.jpg")]
[InlineData("artist_01.jpg")]
[InlineData("artist 03.jpg")]
public void Test_Should_Be_Artist_Secondary_Images(string input)
{
Assert.True(ImageHelper.IsArtistSecondaryImage(new FileInfo(input)));
}
[Theory]
[InlineData("cover.jpeg")]
[InlineData("cover.jpg")]
[InlineData("cover.png")]
[InlineData("Cover.Jpg")]
[InlineData("Release.JPG")]
[InlineData("front.jpg")]
[InlineData("FrOnt.jpg")]
[InlineData("Art - front.jpg")]
[InlineData("Art - Front.jpg")]
[InlineData("Art-Front.jpg")]
[InlineData("Art- Front.jpg")]
[InlineData("Art -Front.jpg")]
[InlineData("F1.jpg")]
[InlineData("F 1.jpg")]
[InlineData("F-1.jpg")]
public void Test_Should_Be_Release_Images(string input)
{
Assert.True(ImageHelper.IsReleaseImage(new FileInfo(input)));
}
[Theory]
[InlineData("cover.png")]
[InlineData("Cover.jpg")]
[InlineData("batman.txt")]
[InlineData("Song.mp3")]
[InlineData("batman.jpg")]
[InlineData("logo.jpg")]
[InlineData("Release.JPG")]
[InlineData("front.jpg")]
[InlineData("F1.jpg")]
[InlineData("logo.jpeg")]
[InlineData("logo.png")]
[InlineData("Logo.Jpg")]
[InlineData("artist_logo.jpg")]
[InlineData("Artist_logo.jpg")]
[InlineData("ARTIST_LOGO.JPG")]
[InlineData("Artist - Front.jpg")]
[InlineData("Artist Front.jpg")]
[InlineData("artist 1.jpg")]
[InlineData("artist_01.jpg")]
[InlineData("artist 03.jpg")]
public void Test_Should_Not_Be_Artist_Images(string input)
{
var t = ImageHelper.IsArtistImage(new FileInfo(input));
Assert.False(t);
}
[Theory]
[InlineData("artist.jpeg")]
[InlineData("artist.jpg")]
[InlineData("artist.png")]
[InlineData("Artist.Jpg")]
[InlineData("Artist.JPG")]
[InlineData("band.jpg")]
[InlineData("group.jpg")]
[InlineData("ARTIST.JPG")]
[InlineData("GrOup.jpg")]
[InlineData("aRtist.jpg")]
[InlineData("batman.txt")]
[InlineData("Song.mp3")]
[InlineData("batman.jpg")]
[InlineData("logo.jpg")]
[InlineData("cover 1.jpg")]
[InlineData("cover_01.jpg")]
[InlineData("cover 03.jpg")]
public void Test_Should_Not_Be_Release_Images(string input)
{
Assert.False(ImageHelper.IsReleaseImage(new FileInfo(input)));
}
[Theory]
[InlineData("label.jpeg")]
[InlineData("label.jpg")]
[InlineData("label.png")]
[InlineData("Label.Jpg")]
[InlineData("label.JPG")]
[InlineData("record_label.jpg")]
[InlineData("RecordLabel.jpg")]
[InlineData("RECORDLABEL.JPG")]
public void Test_Should_Be_Label_Images(string input)
{
Assert.True(ImageHelper.IsLabelImage(new FileInfo(input)));
}
[Theory]
[InlineData("artist.jpeg")]
[InlineData("artist.jpg")]
[InlineData("artist.png")]
[InlineData("Artist.Jpg")]
[InlineData("Artist.JPG")]
[InlineData("band.jpg")]
[InlineData("group.jpg")]
[InlineData("ARTIST.JPG")]
[InlineData("GrOup.jpg")]
[InlineData("aRtist.jpg")]
[InlineData("cover.jpeg")]
[InlineData("cover.jpg")]
[InlineData("cover.png")]
[InlineData("Cover.Jpg")]
[InlineData("Release.JPG")]
[InlineData("front.jpg")]
[InlineData("FrOnt.jpg")]
public void Test_Should_NotBe_Label_Images(string input)
{
Assert.False(ImageHelper.IsLabelImage(new FileInfo(input)));
}
[Theory]
[InlineData("Booklet-1.jpg")]
[InlineData("Booklet-10.jpg")]
[InlineData("Booklet_1.jpg")]
[InlineData("Booklet.jpg")]
[InlineData("Book.jpg")]
[InlineData("Book_3.jpg")]
[InlineData("Book_03.jpg")]
[InlineData("Book-1.jpg")]
[InlineData("Book-01.jpg")]
[InlineData("Back.jpg")]
[InlineData("Inside.jpg")]
[InlineData("Cd.jpg")]
[InlineData("CD.JPG")]
[InlineData("Cd1.jpg")]
[InlineData("CD3.jpg")]
[InlineData("cover_01.jpg")]
[InlineData("cover 03.jpg")]
[InlineData("cover 1.jpg")]
[InlineData("Encartes (11).jpg")]
[InlineData("Encartes (1).png")]
[InlineData("Encartes.jpg")]
[InlineData("Art - Back.jpg")]
[InlineData("disc.jpg")]
[InlineData("inlay.jpg")]
[InlineData("inside.jpg")]
[InlineData("release 1.jpg")]
[InlineData("release-1.jpg")]
[InlineData("release_1.jpg")]
[InlineData("release 3.jpg")]
[InlineData("release 10.jpg")]
public void Test_Should_Be_Release_Secondary_Images(string input)
{
Assert.True(ImageHelper.IsReleaseSecondaryImage(new FileInfo(input)));
}
[Theory]
[InlineData("artist.jpeg")]
[InlineData("artist.jpg")]
[InlineData("artist.png")]
[InlineData("Artist.Jpg")]
[InlineData("Artist.JPG")]
[InlineData("band.jpg")]
[InlineData("group.jpg")]
[InlineData("ARTIST.JPG")]
[InlineData("GrOup.jpg")]
[InlineData("aRtist.jpg")]
[InlineData("cover.jpeg")]
[InlineData("cover.jpg")]
[InlineData("cover.png")]
[InlineData("Cover.Jpg")]
[InlineData("Release.JPG")]
[InlineData("front.jpg")]
[InlineData("FrOnt.jpg")]
[InlineData("label.jpeg")]
[InlineData("label.jpg")]
[InlineData("label.png")]
[InlineData("Label.Jpg")]
[InlineData("label.JPG")]
[InlineData("record_label.jpg")]
[InlineData("RecordLabel.jpg")]
[InlineData("RECORDLABEL.JPG")]
public void Test_Should_Not_Be_Release_Secondary_Images(string input)
{
Assert.False(ImageHelper.IsReleaseSecondaryImage(new FileInfo(input)));
}
[Fact]
public void Get_Release_Image_In_Folder()
{
var folder = new DirectoryInfo(@"C:\roadie_dev_root\image_tests");
if(!folder.Exists)
{
Assert.True(true);
return;
}
var cover = ImageHelper.FindImageTypeInDirectory(folder, Enums.ImageType.Release);
Assert.NotNull(cover);
Assert.Single(cover);
Assert.Equal("cover.jpg", cover.First().Name);
}
[Fact]
public void Get_Artist_Image_In_Folder()
{
var folder = new DirectoryInfo(@"C:\roadie_dev_root\image_tests)");
if (!folder.Exists)
{
Assert.True(true);
return;
}
var artist = ImageHelper.FindImageTypeInDirectory(folder, Enums.ImageType.Artist);
Assert.NotNull(artist);
Assert.Single(artist);
Assert.Equal("artist.jpg", artist.First().Name);
}
}
}

View file

@ -16,6 +16,7 @@ namespace Roadie.Library.Configuration
string InboundFolder { get; set; }
Integrations Integrations { get; set; }
ImageSize LargeImageSize { get; set; }
ImageSize MaximumImageSize { get; set; }
string LibraryFolder { get; set; }
string ListenAddress { get; set; }
ImageSize MediumImageSize { get; set; }

View file

@ -31,6 +31,7 @@ namespace Roadie.Library.Configuration
public string InboundFolder { get; set; }
public Integrations Integrations { get; set; }
public ImageSize LargeImageSize { get; set; }
public ImageSize MaximumImageSize { get; set; }
public string LibraryFolder { get; set; }
public string ListenAddress { get; set; }
public ImageSize MediumImageSize { get; set; }
@ -59,7 +60,7 @@ namespace Roadie.Library.Configuration
this.SmallImageSize = new ImageSize { Width = 160, Height = 160 };
this.MediumImageSize = new ImageSize { Width = 320, Height = 320 };
this.LargeImageSize = new ImageSize { Width = 500, Height = 500 };
this.MaximumImageSize = new ImageSize { Width = 1024, Height = 1024 };
}
}
}

View file

@ -311,15 +311,15 @@ namespace Roadie.Library.Engines
Name = metaData.Artist.ToTitleCase(false)
};
var resultsExceptions = new List<Exception>();
var ArtistGenres = new List<string>();
var ArtistImageUrls = new List<string>();
var ArtistName = metaData.Artist;
var artistGenres = new List<string>();
var artistImageUrls = new List<string>();
var artistName = metaData.Artist;
try
{
if (this.ITunesArtistSearchEngine.IsEnabled)
{
var iTunesResult = await this.ITunesArtistSearchEngine.PerformArtistSearch(ArtistName, 1);
var iTunesResult = await this.ITunesArtistSearchEngine.PerformArtistSearch(artistName, 1);
if (iTunesResult.IsSuccess)
{
var i = iTunesResult.Data.First();
@ -341,11 +341,11 @@ namespace Roadie.Library.Engines
}
if (i.ImageUrls != null)
{
ArtistImageUrls.AddRange(i.ImageUrls);
artistImageUrls.AddRange(i.ImageUrls);
}
if (i.ArtistGenres != null)
{
ArtistGenres.AddRange(i.ArtistGenres);
artistGenres.AddRange(i.ArtistGenres);
}
result.CopyTo(new Artist
{
@ -396,11 +396,11 @@ namespace Roadie.Library.Engines
}
if (mb.ImageUrls != null)
{
ArtistImageUrls.AddRange(mb.ImageUrls);
artistImageUrls.AddRange(mb.ImageUrls);
}
if (mb.ArtistGenres != null)
{
ArtistGenres.AddRange(mb.ArtistGenres);
artistGenres.AddRange(mb.ArtistGenres);
}
if (!string.IsNullOrEmpty(mb.ArtistName) && !mb.ArtistName.Equals(result.Name, StringComparison.OrdinalIgnoreCase))
{
@ -455,11 +455,11 @@ namespace Roadie.Library.Engines
}
if (l.ImageUrls != null)
{
ArtistImageUrls.AddRange(l.ImageUrls);
artistImageUrls.AddRange(l.ImageUrls);
}
if (l.ArtistGenres != null)
{
ArtistGenres.AddRange(l.ArtistGenres);
artistGenres.AddRange(l.ArtistGenres);
}
if (!string.IsNullOrEmpty(l.ArtistName) && !l.ArtistName.Equals(result.Name, StringComparison.OrdinalIgnoreCase))
{
@ -506,11 +506,11 @@ namespace Roadie.Library.Engines
}
if (s.ImageUrls != null)
{
ArtistImageUrls.AddRange(s.ImageUrls);
artistImageUrls.AddRange(s.ImageUrls);
}
if (s.ArtistGenres != null)
{
ArtistGenres.AddRange(s.ArtistGenres);
artistGenres.AddRange(s.ArtistGenres);
}
if (!string.IsNullOrEmpty(s.ArtistName) && !s.ArtistName.Equals(result.Name, StringComparison.OrdinalIgnoreCase))
{
@ -553,7 +553,7 @@ namespace Roadie.Library.Engines
}
if (d.ImageUrls != null)
{
ArtistImageUrls.AddRange(d.ImageUrls);
artistImageUrls.AddRange(d.ImageUrls);
}
if (d.AlternateNames != null)
{
@ -629,9 +629,9 @@ namespace Roadie.Library.Engines
{
result.Tags = string.Join("|", result.Tags.ToListFromDelimited().Distinct().OrderBy(x => x));
}
if (ArtistGenres.Any())
if (artistGenres.Any())
{
var genreInfos = (from ag in ArtistGenres
var genreInfos = (from ag in artistGenres
join g in this.DbContext.Genres on ag equals g.Name into gg
from g in gg.DefaultIfEmpty()
select new
@ -651,10 +651,10 @@ namespace Roadie.Library.Engines
});
}
}
if (ArtistImageUrls.Any())
if (artistImageUrls.Any())
{
var imageBag = new ConcurrentBag<Image>();
var i = ArtistImageUrls.Select(async url =>
var i = artistImageUrls.Select(async url =>
{
imageBag.Add(await WebHelper.GetImageFromUrlAsync(url));
});

View file

@ -488,6 +488,7 @@ namespace Roadie.Library.Engines
ReleaseDate = result.ReleaseDate ?? mb.ReleaseDate,
AmgId = mb.AmgId,
Profile = mb.Profile,
TrackCount = mb.ReleaseMedia != null ? (short)mb.ReleaseMedia.Sum(x => x.TrackCount) : (short)0,
MusicBrainzId = mb.MusicBrainzId,
ITunesId = mb.iTunesId,
Title = result.Title ?? mb.ReleaseTitle,
@ -852,23 +853,14 @@ namespace Roadie.Library.Engines
if (!string.IsNullOrEmpty(artistFolder))
{
// If any file exist for cover that over-rides whatever if found in metadata providers.
var releaseFolder = result.ReleaseFileFolder(artistFolder);
if (Directory.Exists(releaseFolder))
var releaseFolder = new DirectoryInfo(result.ReleaseFileFolder(artistFolder));
if (releaseFolder.Exists)
{
// See if there is a cover file ("cover.jpg") if so set thumbnail image to that
var coverFileName = Path.Combine(releaseFolder, ReleaseFactory.CoverFilename);
if(!File.Exists(coverFileName))
{
// See if any file exists in the release folder with "cover" in the name
var coverFiles = Directory.GetFiles(releaseFolder, "*cover*.jpg", new EnumerationOptions { MatchCasing = MatchCasing.CaseInsensitive });
if(coverFiles != null && coverFiles.Any())
{
coverFileName = coverFiles.First();
}
}
if (File.Exists(coverFileName))
var cover = ImageHelper.FindImageTypeInDirectory(releaseFolder, ImageType.Release);
if(cover.Any())
{
// Read image and convert to jpeg
var coverFileName = cover.First().FullName;
result.Thumbnail = File.ReadAllBytes(coverFileName);
this.Logger.LogDebug("Using Release Cover File [{0}]", coverFileName);
}

View file

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Roadie.Library.Enums
{
public enum ImageType : short
{
Unknown = 0,
Artist,
ArtistSecondary,
Release,
ReleaseSecondary,
Label
}
}

View file

@ -27,8 +27,6 @@ namespace Roadie.Library.Factories
public sealed class ReleaseFactory : FactoryBase, IReleaseFactory
{
public const string CoverFilename = "cover.jpg";
private List<int> _addedTrackIds = new List<int>();
public IEnumerable<int> AddedTrackIds

View file

@ -1,4 +1,5 @@
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Roadie.Library.Caching;
using Roadie.Library.Configuration;
using Roadie.Library.Encoding;
@ -23,13 +24,7 @@ namespace Roadie.Library.FilePlugins
public IAudioMetaDataHelper AudioMetaDataHelper { get; }
public override string[] HandlesTypes
{
get
{
return new string[1] { "audio/mpeg" };
}
}
public override string[] HandlesTypes => new string[1] { "audio/mpeg" };
public Audio(IRoadieSettings configuration,
IHttpEncoder httpEncoder,
@ -108,39 +103,125 @@ namespace Roadie.Library.FilePlugins
return result;
}
PluginBase.CheckMakeFolder(artistFolder);
PluginBase.CheckMakeFolder(releaseFolder);
// See if folder has "cover" image if so then move to release folder for metadata
var imageFiles = ImageHelper.ImageFilesInFolder(fileInfo.DirectoryName);
if (imageFiles != null && imageFiles.Any())
if(PluginBase.CheckMakeFolder(artistFolder))
{
foreach (var imageFile in imageFiles)
this.Logger.LogTrace("Created ArtistFolder [{0}]", artistFolder);
}
if(PluginBase.CheckMakeFolder(releaseFolder))
{
this.Logger.LogTrace("Created ReleaseFolder [{0}]", releaseFolder);
}
try
{
// See if file folder parent folder (likely file is in release folder) has primary artist image if so then move to artist folder
var artistImages = ImageHelper.FindImageTypeInDirectory(fileInfo.Directory, Enums.ImageType.Artist);
if (!artistImages.Any())
{
var i = new FileInfo(imageFile);
var iName = i.Name.ToLower().Trim();
this.Logger.LogDebug("Found Image File [{0}] [{1}]", imageFile, iName);
var isCoverArtType = iName.Contains("cover") || iName.Contains("folder") || iName.Contains("front") || iName.Contains("release") || iName.Contains("album");
if (isCoverArtType)
artistImages = ImageHelper.FindImageTypeInDirectory(fileInfo.Directory.Parent, Enums.ImageType.Artist);
}
if (artistImages.Any())
{
var artistImage = artistImages.First();
var aristImageFilename = Path.Combine(artistFolder, ImageHelper.ArtistImageFilename);
if (aristImageFilename != artistImage.FullName)
{
var coverFileName = Path.Combine(releaseFolder, Factories.ReleaseFactory.CoverFilename);
if (coverFileName != i.FullName)
// Read image and convert to jpeg
var imageBytes = File.ReadAllBytes(artistImage.FullName);
imageBytes = ImageHelper.ConvertToJpegFormat(imageBytes);
// Move artist image to artist folder
if (!doJustInfo)
{
File.WriteAllBytes(aristImageFilename, imageBytes);
artistImage.Delete();
}
this.Logger.LogDebug("Found Artist Image File [{0}], Moved to artist folder.", artistImage.Name);
}
}
// See if any secondary artist images if so then move to artist folder
artistImages = ImageHelper.FindImageTypeInDirectory(fileInfo.Directory, Enums.ImageType.ArtistSecondary);
if (!artistImages.Any())
{
artistImages = ImageHelper.FindImageTypeInDirectory(fileInfo.Directory.Parent, Enums.ImageType.Artist);
}
if (artistImages.Any())
{
var looper = 0;
foreach (var artistImage in artistImages)
{
looper++;
var aristImageFilename = Path.Combine(artistFolder, string.Format(ImageHelper.ArtistSecondaryImageFilename, looper.ToString("00")));
if (aristImageFilename != artistImage.FullName)
{
// Read image and convert to jpeg
var imageBytes = File.ReadAllBytes(i.FullName);
var imageBytes = File.ReadAllBytes(artistImage.FullName);
imageBytes = ImageHelper.ConvertToJpegFormat(imageBytes);
// Move artist image to artist folder
if (!doJustInfo)
{
File.WriteAllBytes(aristImageFilename, imageBytes);
artistImage.Delete();
}
this.Logger.LogDebug("Found Artist Secondary Image File [{0}], Moved to artist folder [{1}].", artistImage.Name, aristImageFilename);
}
}
}
// See if file folder has release image if so then move to release folder
var releaseImages = ImageHelper.FindImageTypeInDirectory(fileInfo.Directory, Enums.ImageType.Release);
if (releaseImages.Any())
{
var releaseImage = releaseImages.First();
var coverFileName = Path.Combine(releaseFolder, ImageHelper.ReleaseCoverFilename);
if (coverFileName != releaseImage.FullName)
{
// Read image and convert to jpeg
var imageBytes = File.ReadAllBytes(releaseImage.FullName);
imageBytes = ImageHelper.ConvertToJpegFormat(imageBytes);
// Move cover to release folder
if (!doJustInfo)
{
File.WriteAllBytes(coverFileName, imageBytes);
releaseImage.Delete();
}
this.Logger.LogDebug("Found Release Image File [{0}], Moved to release folder", releaseImage.Name);
}
}
// See if folder has secondary release image if so then move to release folder
releaseImages = ImageHelper.FindImageTypeInDirectory(fileInfo.Directory, Enums.ImageType.ReleaseSecondary);
if (releaseImages.Any())
{
var looper = 0;
foreach (var releaseImage in releaseImages)
{
looper++;
var releaseImageFilename = Path.Combine(releaseFolder, string.Format(ImageHelper.ReleaseSecondaryImageFilename, looper.ToString("00")));
if (releaseImageFilename != releaseImage.FullName)
{
// Read image and convert to jpeg
var imageBytes = File.ReadAllBytes(releaseImage.FullName);
imageBytes = ImageHelper.ConvertToJpegFormat(imageBytes);
// Move cover to release folder
if (!doJustInfo)
{
File.WriteAllBytes(coverFileName, imageBytes);
i.Delete();
File.WriteAllBytes(releaseImageFilename, imageBytes);
releaseImage.Delete();
}
this.Logger.LogDebug("Found Image File [{0}], Moved to release folder", i.Name);
break;
this.Logger.LogDebug("Found Release Image File [{0}], Moved to release folder [{1}]", releaseImage.Name, releaseImageFilename);
}
}
}
}
catch (Exception ex)
{
this.Logger.LogError(ex, "Error with Managing Images For [{0}]", fileInfo.FullName);
}
var doesFileExistsForTrack = File.Exists(destinationName);
@ -210,7 +291,7 @@ namespace Roadie.Library.FilePlugins
}
sw.Stop();
this.Logger.LogTrace("<< Audio: Process Complete. ElapsedTime [{0}]", sw.ElapsedMilliseconds);
this.Logger.LogTrace("<< Audio: Process Complete. Result `{0}`, ElapsedTime [{1}]", JsonConvert.SerializeObject(result), sw.ElapsedMilliseconds);
return result;
}

View file

@ -1,4 +1,5 @@
using Roadie.Library.SearchEngines.Imaging;
using Roadie.Library.Enums;
using Roadie.Library.SearchEngines.Imaging;
using Roadie.Library.Utility;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats;
@ -9,11 +10,17 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
namespace Roadie.Library.Imaging
{
public static class ImageHelper
{
public static string ArtistImageFilename = "artist.jpg";
public static string ArtistSecondaryImageFilename = "artist {0}.jpg"; // Replace with counter of image
public static string ReleaseCoverFilename = "cover.jpg";
public static string ReleaseSecondaryImageFilename = "release {0}.jpg"; // Replace with counter of image
public static string LabelImageFilename = "label.jpg";
public static byte[] ConvertToJpegFormat(byte[] imageBytes)
{
@ -52,7 +59,7 @@ namespace Roadie.Library.Imaging
public static string[] ImageFilesInFolder(string folder)
{
return ImageHelper.GetFiles(folder, ImageHelper.ImageExtensions());
return ImageHelper.GetFiles(folder, ImageHelper.ImageExtensions(), SearchOption.AllDirectories);
}
public static string[] ImageMimeTypes()
@ -120,5 +127,106 @@ namespace Roadie.Library.Imaging
return null;
}
public static bool IsArtistImage(FileInfo fileinfo)
{
if (fileinfo == null)
{
return false;
}
return Regex.IsMatch(fileinfo.Name, @"(band|artist|group)\.(jpg|jpeg|png|bmp|gif)", RegexOptions.IgnoreCase);
}
public static bool IsArtistSecondaryImage(FileInfo fileinfo)
{
if (fileinfo == null)
{
return false;
}
return Regex.IsMatch(fileinfo.Name, @"(artist_logo|logo|(artist[\s_-]+[0-9]+))\.(jpg|jpeg|png|bmp|gif)", RegexOptions.IgnoreCase);
}
public static bool IsReleaseImage(FileInfo fileinfo, string releaseName = null)
{
if (fileinfo == null)
{
return false;
}
return Regex.IsMatch(fileinfo.Name, @"((f[-_\s]*[0-9]*)|cover|release|front)\.(jpg|jpeg|png|bmp|gif)", RegexOptions.IgnoreCase);
}
public static bool IsReleaseSecondaryImage(FileInfo fileinfo)
{
if (fileinfo == null)
{
return false;
}
return Regex.IsMatch(fileinfo.Name, @"((book[let]*[-_]*[0-9]*)|(encartes[-_\s]*[(]*[0-9]*[)]*)|(cover[\s_-]+[0-9]+)|back|disc|inside|inlet|inlay|cd[0-9]*|inside|(release[\s_-]+[0-9]+))\.(jpg|jpeg|png|bmp|gif)", RegexOptions.IgnoreCase);
}
public static bool IsLabelImage(FileInfo fileinfo)
{
if (fileinfo == null)
{
return false;
}
return Regex.IsMatch(fileinfo.Name, @"(label|recordlabel|record_label)\.(jpg|jpeg|png|bmp|gif)", RegexOptions.IgnoreCase);
}
public static IEnumerable<FileInfo> FindImageTypeInDirectory(DirectoryInfo directory, ImageType type)
{
var result = new List<FileInfo>();
if (directory == null || !directory.Exists)
{
return null;
}
var imageFilesInFolder = ImageFilesInFolder(directory.FullName);
if (imageFilesInFolder == null || !imageFilesInFolder.Any())
{
return null;
}
foreach(var imageFile in imageFilesInFolder)
{
var image = new FileInfo(imageFile);
switch (type)
{
case ImageType.Artist:
if(IsArtistImage(image))
{
result.Add(image);
}
break;
case ImageType.ArtistSecondary:
if (IsArtistSecondaryImage(image))
{
result.Add(image);
}
break;
case ImageType.Release:
if (IsReleaseImage(image))
{
result.Add(image);
}
break;
case ImageType.ReleaseSecondary:
if (IsReleaseSecondaryImage(image))
{
result.Add(image);
}
break;
case ImageType.Label:
if (IsLabelImage(image))
{
result.Add(image);
}
break;
}
}
return result.OrderBy(x => x.Name);
}
}
}

View file

@ -9,13 +9,13 @@
<ItemGroup>
<PackageReference Include="CsvHelper" Version="12.1.2" />
<PackageReference Include="EFCore.BulkExtensions" Version="2.3.9" />
<PackageReference Include="FluentFTP" Version="19.2.4" />
<PackageReference Include="EFCore.BulkExtensions" Version="2.4.0" />
<PackageReference Include="FluentFTP" Version="21.0.0" />
<PackageReference Include="HtmlAgilityPack" Version="1.8.14" />
<PackageReference Include="IdSharp.Common" Version="1.0.1" />
<PackageReference Include="IdSharp.Tagging" Version="1.0.0-rc3" />
<PackageReference Include="Inflatable.Lastfm" Version="1.1.0.339" />
<PackageReference Include="Mapster" Version="3.3.0" />
<PackageReference Include="Mapster" Version="3.3.1" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="2.2.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.2.1" />
<PackageReference Include="Microsoft.Extensions.Caching.Redis" Version="2.2.0" />

View file

@ -288,13 +288,17 @@ namespace Roadie.Library.MetaData.Audio
}
if (!this._trackArtist.Contains(AudioMetaData.ArtistSplitCharacter.ToString()))
{
if(string.IsNullOrEmpty(this.TrackArtist))
{
return new string[0];
}
return new string[1] { this.TrackArtist };
}
if (!string.IsNullOrEmpty(this._artist) || !string.IsNullOrEmpty(this._trackArtist))
{
if (!this._artist.Equals(this._trackArtist, StringComparison.OrdinalIgnoreCase))
{
return this._trackArtist.Split(AudioMetaData.ArtistSplitCharacter).Select(x => x.ToTitleCase()).ToArray();
return this._trackArtist.Split(AudioMetaData.ArtistSplitCharacter).Where(x => !string.IsNullOrEmpty(x)).Select(x => x.ToTitleCase()).ToArray();
}
}
return new string[0];
@ -314,6 +318,9 @@ namespace Roadie.Library.MetaData.Audio
}
}
/// <summary>
/// TYER | TDRC | TORY | TDOR
/// </summary>
public int? Year
{
get

View file

@ -128,7 +128,8 @@ namespace Roadie.Library.MetaData.ID3Tags
result.Title = id3v2.Title.ToTitleCase(false);
result.TrackNumber = ID3TagsHelper.ParseTrackNumber(id3v2.TrackNumber);
result.TotalTrackNumbers = ID3TagsHelper.ParseTotalTrackNumber(id3v2.TrackNumber);
result.Year = ID3TagsHelper.ParseYear(id3v2.Year);
var year = id3v2.Year ?? id3v2.RecordingTimestamp ?? id3v2.ReleaseTimestamp ?? id3v2.OriginalReleaseTimestamp;
result.Year = ID3TagsHelper.ParseYear(year);
isSuccess = true;
}

View file

@ -24,7 +24,8 @@ namespace Roadie.Library.Utility
var artistFolder = artistSortName.ToTitleCase(false);
destinationFolder = destinationFolder ?? configuration.LibraryFolder;
return Path.Combine(destinationFolder, artistFolder.ToFolderNameFriendly());
var directoryInfo = new DirectoryInfo(Path.Combine(destinationFolder, artistFolder.ToFolderNameFriendly()));
return directoryInfo.FullName;
}
/// <summary>
@ -77,7 +78,8 @@ namespace Roadie.Library.Utility
{
return null;
}
return Path.Combine(destinationFolder, track.FilePath, track.FileName);
var directoryInfo = new DirectoryInfo(Path.Combine(destinationFolder, track.FilePath, track.FileName));
return directoryInfo.FullName;
}
/// <summary>
@ -92,7 +94,8 @@ namespace Roadie.Library.Utility
SimpleContract.Requires<ArgumentException>(!string.IsNullOrEmpty(releaseTitle), "Invalid Release Title");
SimpleContract.Requires<ArgumentException>(releaseDate != DateTime.MinValue, "Invalid Release Date");
return Path.Combine(artistFolder, string.Format("{1}{0}", releaseTitle.ToTitleCase(false).ToFolderNameFriendly(), string.Format("[{0}] ", releaseDate.ToString("yyyy"))));
var directoryInfo = new DirectoryInfo(Path.Combine(artistFolder, string.Format("{1}{0}", releaseTitle.ToTitleCase(false).ToFolderNameFriendly(), string.Format("[{0}] ", releaseDate.ToString("yyyy")))));
return directoryInfo.FullName;
}
/// <summary>
@ -195,8 +198,9 @@ namespace Roadie.Library.Utility
var trackFileName = FolderPathHelper.TrackFileName(configuration, trackTitle, trackNumber, diskNumber, totalTrackNumber, fileExtension);
var result = Path.Combine(artistFolder, releaseFolder, trackFileName);
Trace.WriteLine(string.Format("TrackPath [{0}] For ArtistName [{1}], ReleaseTitle [{2}], ReleaseDate [{3}], ReleaseYear [{4}], TrackNumber [{5}]", result, artistSortName, releaseTitle, releaseDate.ToString("s"), releaseDate.ToString("yyyy"), trackNumber));
return result;
var resultInfo = new DirectoryInfo(result);
Trace.WriteLine(string.Format("TrackPath [{0}] For ArtistName [{1}], ReleaseTitle [{2}], ReleaseDate [{3}], ReleaseYear [{4}], TrackNumber [{5}]", resultInfo.FullName, artistSortName, releaseTitle, releaseDate.ToString("s"), releaseDate.ToString("yyyy"), trackNumber));
return resultInfo.FullName;
}
/// <summary>

View file

@ -340,7 +340,7 @@ namespace Roadie.Api.Services
var imageFile = imageFiles.First();
var i = new FileInfo(imageFile);
var iName = i.Name.ToLower().Trim();
var isArtistImage = iName.Contains("artist") || iName.Contains(artist.Name.ToLower());
var isArtistImage = iName.Contains("artist");
if (isArtistImage)
{
// Read image and convert to jpeg
@ -434,7 +434,7 @@ namespace Roadie.Api.Services
artist.Thumbnail = ImageHelper.ConvertToJpegFormat(artistImage);
// Save unaltered image to cover file
var artistImageName = Path.Combine(artist.ArtistFileFolder(this.Configuration, this.Configuration.LibraryFolder), "artist.jpg");
var artistImageName = Path.Combine(artist.ArtistFileFolder(this.Configuration, this.Configuration.LibraryFolder), ImageHelper.ArtistImageFilename);
File.WriteAllBytes(artistImageName, artist.Thumbnail);
// Resize to store in database as thumbnail
@ -730,6 +730,14 @@ namespace Roadie.Api.Services
{
tsw.Restart();
result.Images = this.DbContext.Images.Where(x => x.ArtistId == artist.Id).Select(x => MakeFullsizeImage(x.RoadieId, x.Caption)).ToArray();
var artistFolder = artist.ArtistFileFolder(this.Configuration, this.Configuration.LibraryFolder);
var artistImagesInFolder = ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(artistFolder), ImageType.ArtistSecondary);
if (artistImagesInFolder.Any())
{
result.Images = result.Images.Concat(artistImagesInFolder.Select((x, i) => MakeFullsizeSecondaryImage(id, ImageType.ArtistSecondary, i)));
}
tsw.Stop();
timings.Add("images", tsw.ElapsedMilliseconds);
}
@ -892,8 +900,8 @@ namespace Roadie.Api.Services
}
// Save unaltered image to artist file
var coverFileName = Path.Combine(artistFolder, "artist.jpg");
File.WriteAllBytes(coverFileName, artist.Thumbnail);
var artistImage = Path.Combine(artistFolder, ImageHelper.ArtistImageFilename);
File.WriteAllBytes(artistImage, artist.Thumbnail);
// Resize to store in database as thumbnail
artist.Thumbnail = ImageHelper.ResizeImage(artist.Thumbnail, this.Configuration.MediumImageSize.Width, this.Configuration.MediumImageSize.Height);

View file

@ -11,6 +11,7 @@ namespace Roadie.Api.Services
public interface IImageService
{
Task<FileOperationResult<Library.Models.Image>> ArtistImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null);
Task<FileOperationResult<Library.Models.Image>> ArtistSecondaryImage(Guid id, int imageId, int? width, int? height, EntityTagHeaderValue etag = null);
Task<FileOperationResult<Library.Models.Image>> ById(Guid id, int? width, int? height, EntityTagHeaderValue etag = null);
@ -25,6 +26,7 @@ namespace Roadie.Api.Services
Task<FileOperationResult<Library.Models.Image>> PlaylistImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null);
Task<FileOperationResult<Library.Models.Image>> ReleaseImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null);
Task<FileOperationResult<Library.Models.Image>> ReleaseSecondaryImage(Guid id, int imageId, int? width, int? height, EntityTagHeaderValue etag = null);
Task<OperationResult<IEnumerable<ImageSearchResult>>> Search(string query, int resultsCount = 10);

View file

@ -59,6 +59,20 @@ namespace Roadie.Api.Services
etag: etag);
}
public async Task<FileOperationResult<Image>> ArtistSecondaryImage(Guid id, int imageId, int? width, int? height, EntityTagHeaderValue etag = null)
{
return await this.GetImageFileOperation(type: $"ArtistSecondaryThumbnail-{imageId}",
regionUrn: data.Release.CacheRegionUrn(id),
id: id,
width: width,
height: height,
action: async () =>
{
return await this.ArtistSecondaryImageAction(id, imageId, etag);
},
etag: etag);
}
public async Task<FileOperationResult<Image>> ById(Guid id, int? width, int? height, EntityTagHeaderValue etag = null)
{
return await this.GetImageFileOperation(type: "ImageById",
@ -187,6 +201,20 @@ namespace Roadie.Api.Services
etag: etag);
}
public async Task<FileOperationResult<Image>> ReleaseSecondaryImage(Guid id,int imageId, int? width, int? height, EntityTagHeaderValue etag = null)
{
return await this.GetImageFileOperation(type: $"ReleaseSecondaryThumbnail-{imageId}",
regionUrn: data.Release.CacheRegionUrn(id),
id: id,
width: width,
height: height,
action: async () =>
{
return await this.ReleaseSecondaryImageAction(id, imageId, etag);
},
etag: etag);
}
public async Task<OperationResult<IEnumerable<ImageSearchResult>>> Search(string query, int resultsCount = 10)
{
var sw = Stopwatch.StartNew();
@ -269,10 +297,10 @@ namespace Roadie.Api.Services
}
else
{
var artistImages = Directory.GetFiles(artistFolder, "artist*.*");
if (artistImages.Any())
var artistImages = ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(artistFolder), Library.Enums.ImageType.Artist);
if(artistImages.Any())
{
imageBytes = File.ReadAllBytes(artistImages.First());
imageBytes = File.ReadAllBytes(artistImages.First().FullName);
}
}
}
@ -490,10 +518,10 @@ namespace Roadie.Api.Services
}
else
{
var coverArtFiles = Directory.GetFiles(releaseFolder, "cover*.*");
if (coverArtFiles.Any())
var releaseCoverFiles = ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(releaseFolder), Library.Enums.ImageType.Release);
if(releaseCoverFiles.Any())
{
imageBytes = File.ReadAllBytes(coverArtFiles.First());
imageBytes = File.ReadAllBytes(releaseCoverFiles.First().FullName);
}
}
}
@ -522,6 +550,110 @@ namespace Roadie.Api.Services
return Task.FromResult(new FileOperationResult<Image>(OperationMessages.ErrorOccured));
}
private Task<FileOperationResult<Image>> ReleaseSecondaryImageAction(Guid id, int imageId, EntityTagHeaderValue etag = null)
{
try
{
var release = this.GetRelease(id);
if (release == null)
{
return Task.FromResult(new FileOperationResult<Image>(true, string.Format("Release Not Found [{0}]", id)));
}
byte[] imageBytes = null;
string artistFolder = null;
string releaseFolder = null;
try
{
// See if cover art file exists in release folder
artistFolder = release.Artist.ArtistFileFolder(this.Configuration, this.Configuration.LibraryFolder);
if (!Directory.Exists(artistFolder))
{
this.Logger.LogWarning($"Artist Folder [{ artistFolder }], Not Found For Artist `{ release.Artist.ToString() }`");
}
else
{
releaseFolder = release.ReleaseFileFolder(artistFolder);
if (!Directory.Exists(releaseFolder))
{
this.Logger.LogWarning($"Release Folder [{ releaseFolder }], Not Found For Release `{ release.ToString() }`");
}
else
{
var releaseSecondaryImages = ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(releaseFolder), Library.Enums.ImageType.ReleaseSecondary).ToArray();
if(releaseSecondaryImages.Length >= imageId && releaseSecondaryImages[imageId] != null)
{
imageBytes = File.ReadAllBytes(releaseSecondaryImages[imageId].FullName);
}
}
}
}
catch (Exception ex)
{
this.Logger.LogError(ex, $"Error Reading Release Folder [{ releaseFolder }] Artist Folder [{ artistFolder }] For Artist `{ release.Artist.Id }`");
}
var image = new data.Image
{
Bytes = imageBytes,
CreatedDate = release.CreatedDate,
LastUpdated = release.LastUpdated
};
return Task.FromResult(GenerateFileOperationResult(id, image, etag));
}
catch (Exception ex)
{
this.Logger.LogError($"Error fetching Release Thumbnail [{ id }]", ex);
}
return Task.FromResult(new FileOperationResult<Image>(OperationMessages.ErrorOccured));
}
private Task<FileOperationResult<Image>> ArtistSecondaryImageAction(Guid id, int imageId, EntityTagHeaderValue etag = null)
{
try
{
var artist = this.GetArtist(id);
if (artist == null)
{
return Task.FromResult(new FileOperationResult<Image>(true, string.Format("Release Not Found [{0}]", id)));
}
byte[] imageBytes = null;
string artistFolder = null;
try
{
// See if cover art file exists in release folder
artistFolder = artist.ArtistFileFolder(this.Configuration, this.Configuration.LibraryFolder);
if (!Directory.Exists(artistFolder))
{
this.Logger.LogWarning($"Artist Folder [{ artistFolder }], Not Found For Artist `{ artist }`");
}
else
{
var artistSecondaryImages = ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(artistFolder), Library.Enums.ImageType.ArtistSecondary).ToArray();
if (artistSecondaryImages.Length >= imageId && artistSecondaryImages[imageId] != null)
{
imageBytes = File.ReadAllBytes(artistSecondaryImages[imageId].FullName);
}
}
}
catch (Exception ex)
{
this.Logger.LogError(ex, $"Error Reading Artist Folder [{ artistFolder }] For Artist `{ artist }`");
}
var image = new data.Image
{
Bytes = imageBytes,
CreatedDate = artist.CreatedDate,
LastUpdated = artist.LastUpdated
};
return Task.FromResult(GenerateFileOperationResult(id, image, etag));
}
catch (Exception ex)
{
this.Logger.LogError($"Error fetching Release Thumbnail [{ id }]", ex);
}
return Task.FromResult(new FileOperationResult<Image>(OperationMessages.ErrorOccured));
}
private async Task<FileOperationResult<Image>> TrackImageAction(Guid id, int? width, int? height, EntityTagHeaderValue etag = null)
{
try

View file

@ -834,6 +834,13 @@ namespace Roadie.Api.Services
{
result.Images = releaseImages;
}
var artistFolder = release.Artist.ArtistFileFolder(this.Configuration, this.Configuration.LibraryFolder);
var releaseFolder = release.ReleaseFileFolder(artistFolder);
var releaseImagesInFolder = ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(releaseFolder), ImageType.ReleaseSecondary);
if(releaseImagesInFolder.Any())
{
result.Images = result.Images.Concat(releaseImagesInFolder.Select((x, i) => MakeFullsizeSecondaryImage(id, ImageType.ReleaseSecondary, i)));
}
}
if (includes.Contains("playlists"))
{

View file

@ -9,7 +9,7 @@
<PackageReference Include="Hashids.net" Version="1.2.2" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="5.4.0" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.0.9.2" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.0.10" />
</ItemGroup>
<ItemGroup>

View file

@ -5,6 +5,7 @@ using Roadie.Library;
using Roadie.Library.Caching;
using Roadie.Library.Configuration;
using Roadie.Library.Encoding;
using Roadie.Library.Enums;
using Roadie.Library.Identity;
using Roadie.Library.Models;
using Roadie.Library.Utility;
@ -278,6 +279,15 @@ namespace Roadie.Api.Services
return new Image($"{this.HttpContext.ImageBaseUrl }/{id}", caption, $"{this.HttpContext.ImageBaseUrl }/{id}/{ this.Configuration.SmallImageSize.Width }/{ this.Configuration.SmallImageSize.Height }");
}
protected Image MakeFullsizeSecondaryImage(Guid id, ImageType type, int imageId, string caption = null)
{
if(type == ImageType.ArtistSecondary)
{
return new Image($"{this.HttpContext.ImageBaseUrl }/artist-secondary/{id}/{imageId}", caption, $"{this.HttpContext.ImageBaseUrl }/artist-secondary/{id}/{ imageId }/{ this.Configuration.SmallImageSize.Width }/{ this.Configuration.SmallImageSize.Height }");
}
return new Image($"{this.HttpContext.ImageBaseUrl }/release-secondary/{id}/{imageId}", caption, $"{this.HttpContext.ImageBaseUrl }/release-secondary/{id}/{ imageId }/{ this.Configuration.SmallImageSize.Width }/{ this.Configuration.SmallImageSize.Height }");
}
protected Image MakeImage(Guid id, int width = 200, int height = 200, string caption = null, bool includeCachebuster = false)
{
return new Image($"{this.HttpContext.ImageBaseUrl }/{id}/{ width }/{ height }/{ (includeCachebuster ? DateTime.UtcNow.Ticks.ToString() : string.Empty) }", caption, $"{this.HttpContext.ImageBaseUrl }/{id}/{ this.Configuration.SmallImageSize.Width }/{ this.Configuration.SmallImageSize.Height }");

View file

@ -54,6 +54,27 @@ namespace Roadie.Api.Controllers
entityTag: result.ETag);
}
[HttpGet("artist-secondary/{id}/{imageId}/{width:int?}/{height:int?}/{cacheBuster?}")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
public async Task<IActionResult> ArtistSecondaryImage(Guid id, int imageId, int? width, int? height)
{
var result = await this.ImageService.ArtistSecondaryImage(id, imageId, width ?? this.RoadieSettings.MaximumImageSize.Width, height ?? this.RoadieSettings.MaximumImageSize.Height);
if (result == null || result.IsNotFoundResult)
{
return NotFound();
}
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return File(fileContents: result.Data.Bytes,
contentType: result.ContentType,
fileDownloadName: $"{ result.Data.Caption ?? id.ToString()}.jpg",
lastModified: result.LastModified,
entityTag: result.ETag);
}
[HttpGet("collection/{id}/{width:int?}/{height:int?}/{cacheBuster?}")]
[ProducesResponseType(200)]
@ -177,6 +198,28 @@ namespace Roadie.Api.Controllers
entityTag: result.ETag);
}
[HttpGet("release-secondary/{id}/{imageId}/{width:int?}/{height:int?}/{cacheBuster?}")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
public async Task<IActionResult> ReleaseSecondaryImage(Guid id, int imageId, int? width, int? height)
{
var result = await this.ImageService.ReleaseSecondaryImage(id, imageId, width ?? this.RoadieSettings.MaximumImageSize.Width, height ?? this.RoadieSettings.MaximumImageSize.Height);
if (result == null || result.IsNotFoundResult)
{
return NotFound();
}
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return File(fileContents: result.Data.Bytes,
contentType: result.ContentType,
fileDownloadName: $"{ result.Data.Caption ?? id.ToString()}.jpg",
lastModified: result.LastModified,
entityTag: result.ETag);
}
[HttpGet("track/{id}/{width:int?}/{height:int?}/{cacheBuster?}")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]

View file

@ -23,7 +23,7 @@
<ItemGroup>
<PackageReference Include="BCrypt-Core" Version="2.0.0" />
<PackageReference Include="Mapster" Version="3.3.0" />
<PackageReference Include="Mapster" Version="3.3.1" />
<PackageReference Include="Microsoft.AspNet.SignalR" Version="2.4.0" />
<PackageReference Include="Microsoft.AspNetCore.All" />
<PackageReference Include="Microsoft.AspNetCore.OData" Version="7.1.0" />
@ -34,7 +34,7 @@
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
<PackageReference Include="Serilog.Sinks.RollingFileAlternate" Version="2.0.9" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="5.4.0" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.0.9.2" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.0.10" />
</ItemGroup>
<ItemGroup>