mirror of
https://github.com/sphildreth/roadie
synced 2024-11-10 06:44:12 +00:00
resolves #16
This commit is contained in:
parent
e76432557a
commit
ca5db4cb17
87 changed files with 2246 additions and 2825 deletions
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
|
@ -22,7 +22,6 @@
|
|||
<ItemGroup>
|
||||
<PackageReference Include="McMaster.Extensions.CommandLineUtils" Version="2.3.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.PowerShell.SDK" Version="6.2.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
gci -Path "N:\_complete" -r -include *.zip,*.rar,*.7z | foreach { & 'C:\Program Files\7-Zip\7z.exe' x $_.FullName -y -o"$('"'+$_.DirectoryName+'"')"}
|
||||
gci -Path "N:\_complete" -r -include *.zip,*.rar,*.7z | foreach { & 'C:\Program Files\7-Zip\7z.exe' x $_.FullName -y -o"$('"'+$_.DirectoryName+'"')"}
|
||||
Remove-Item –path N:\_complete -include *.7z,*.zip,*.rar -recurse
|
|
@ -7,7 +7,6 @@ using Roadie.Library.Configuration;
|
|||
using Roadie.Library.Data;
|
||||
using Roadie.Library.Engines;
|
||||
using Roadie.Library.Extensions;
|
||||
using Roadie.Library.Factories;
|
||||
using Roadie.Library.MetaData.ID3Tags;
|
||||
using Roadie.Library.Processors;
|
||||
using System;
|
||||
|
@ -39,7 +38,7 @@ namespace Roadie.Library.Tests
|
|||
|
||||
public ArtistLookupEngineTests()
|
||||
{
|
||||
this.MessageLogger = new EventMessageLogger();
|
||||
this.MessageLogger = new EventMessageLogger<ArtistLookupEngineTests>();
|
||||
this.MessageLogger.Messages += MessageLogger_Messages;
|
||||
|
||||
var settings = new RoadieSettings();
|
||||
|
@ -57,19 +56,6 @@ namespace Roadie.Library.Tests
|
|||
Console.WriteLine($"Log Level [{ e.Level }] Log Message [{ e.Message }] ");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Get_Artist_By_Name()
|
||||
{
|
||||
var optionsBuilder = new DbContextOptionsBuilder<RoadieDbContext>();
|
||||
optionsBuilder.UseMySql("server=voyager;userid=roadie;password=MenAtW0rk668;persistsecurityinfo=True;database=roadie;ConvertZeroDateTime=true");
|
||||
|
||||
using (var context = new RoadieDbContext(optionsBuilder.Options))
|
||||
{
|
||||
IArtistLookupEngine artistLookupEngine = new ArtistLookupEngine(this.Configuration, this.HttpEncoder, context, this.CacheManager, this.Logger);
|
||||
var a = artistLookupEngine.DatabaseQueryForArtistName("Nas");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//[Fact]
|
||||
//public void Update_Genre_Normalized_Name()
|
||||
|
|
|
@ -31,7 +31,7 @@ namespace Roadie.Library.Tests
|
|||
|
||||
public ID3TagsHelperTests()
|
||||
{
|
||||
this.MessageLogger = new EventMessageLogger();
|
||||
this.MessageLogger = new EventMessageLogger<ID3TagsHelperTests>();
|
||||
this.MessageLogger.Messages += MessageLogger_Messages;
|
||||
|
||||
var settings = new RoadieSettings();
|
||||
|
@ -42,7 +42,9 @@ namespace Roadie.Library.Tests
|
|||
settings.ConnectionString = configuration.GetConnectionString("RoadieDatabaseConnection");
|
||||
this.Configuration = settings;
|
||||
this.CacheManager = new DictionaryCacheManager(this.Logger, new CachePolicy(TimeSpan.FromHours(4)));
|
||||
this.TagsHelper = new ID3TagsHelper(this.Configuration, this.CacheManager, this.Logger);
|
||||
var tagHelperLooper = new EventMessageLogger<ID3TagsHelper>();
|
||||
tagHelperLooper.Messages += MessageLogger_Messages;
|
||||
this.TagsHelper = new ID3TagsHelper(this.Configuration, this.CacheManager, tagHelperLooper);
|
||||
}
|
||||
|
||||
private void MessageLogger_Messages(object sender, EventMessage e)
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
|
|
74
Roadie.Api.Library.Tests/SearchEngineTests.cs
Normal file
74
Roadie.Api.Library.Tests/SearchEngineTests.cs
Normal file
|
@ -0,0 +1,74 @@
|
|||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Roadie.Library.Caching;
|
||||
using Roadie.Library.Configuration;
|
||||
using Roadie.Library.Processors;
|
||||
using Roadie.Library.SearchEngines.MetaData.Discogs;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace Roadie.Library.Tests
|
||||
{
|
||||
public class SearchEngineTests
|
||||
{
|
||||
private IEventMessageLogger MessageLogger { get; }
|
||||
private ILogger Logger
|
||||
{
|
||||
get
|
||||
{
|
||||
return MessageLogger as ILogger;
|
||||
}
|
||||
}
|
||||
|
||||
public SearchEngineTests()
|
||||
{
|
||||
MessageLogger = new EventMessageLogger<SearchEngineTests>();
|
||||
MessageLogger.Messages += MessageLogger_Messages;
|
||||
|
||||
var settings = new RoadieSettings();
|
||||
IConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
|
||||
configurationBuilder.AddJsonFile("appsettings.test.json");
|
||||
IConfiguration configuration = configurationBuilder.Build();
|
||||
configuration.GetSection("RoadieSettings").Bind(settings);
|
||||
Configuration = settings;
|
||||
CacheManager = new DictionaryCacheManager(Logger, new CachePolicy(TimeSpan.FromHours(4)));
|
||||
HttpEncoder = new Encoding.DummyHttpEncoder();
|
||||
}
|
||||
|
||||
|
||||
|
||||
private IRoadieSettings Configuration { get; }
|
||||
public DictionaryCacheManager CacheManager { get; }
|
||||
private Encoding.IHttpEncoder HttpEncoder { get; }
|
||||
|
||||
[Fact]
|
||||
public async Task DiscogsHelperReleaseSearch()
|
||||
{
|
||||
if(!Configuration.Integrations.DiscogsProviderEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var discogsLogger = new EventMessageLogger<DiscogsHelper>();
|
||||
discogsLogger.Messages += MessageLogger_Messages;
|
||||
|
||||
var engine = new DiscogsHelper(Configuration, CacheManager, discogsLogger);
|
||||
|
||||
var artistName = "With The Dead";
|
||||
var title = "Love From With The Dead";
|
||||
|
||||
var result = await engine.PerformReleaseSearch(artistName, title, 1);
|
||||
Assert.NotNull(result);
|
||||
Assert.NotEmpty(result.Data);
|
||||
|
||||
}
|
||||
|
||||
private void MessageLogger_Messages(object sender, EventMessage e)
|
||||
{
|
||||
Console.WriteLine($"Log Level [{ e.Level }] Log Message [{ e.Message }] ");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
27
Roadie.Api.Library.Tests/WebHelperTests.cs
Normal file
27
Roadie.Api.Library.Tests/WebHelperTests.cs
Normal file
|
@ -0,0 +1,27 @@
|
|||
using Roadie.Library.Imaging;
|
||||
using Roadie.Library.Utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Xunit;
|
||||
|
||||
|
||||
namespace Roadie.Library.Tests
|
||||
{
|
||||
public class WebHelperTests
|
||||
{
|
||||
[Fact]
|
||||
public void DownloadTestImage()
|
||||
{
|
||||
var testImageUrl = @"https://i.ytimg.com/vi/OiH5YMXQwYg/maxresdefault.jpg";
|
||||
var imageBytes = WebHelper.BytesForImageUrl(testImageUrl);
|
||||
Assert.NotNull(imageBytes);
|
||||
Assert.NotEmpty(imageBytes);
|
||||
|
||||
var coverFileName = Path.Combine(@"C:\roadie_dev_root", "testImage.jpg");
|
||||
File.WriteAllBytes(coverFileName, imageBytes);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -25,23 +25,23 @@
|
|||
"Love/Hate": [ "Love; Hate", "Love;Hate", "Love/ Hate", "Love Hate" ]
|
||||
},
|
||||
"Integrations": {
|
||||
"ITunesProviderEnabled": true,
|
||||
"MusicBrainzProviderEnabled": true,
|
||||
"SpotifyProviderEnabled": true,
|
||||
"ITunesProviderEnabled": false,
|
||||
"MusicBrainzProviderEnabled": false,
|
||||
"SpotifyProviderEnabled": false,
|
||||
"ApiKeys": [
|
||||
{
|
||||
"ApiName": "BingImageSearch",
|
||||
"Key": "<KEY HERE>"
|
||||
"Key": ""
|
||||
},
|
||||
{
|
||||
"ApiName": "LastFMApiKey",
|
||||
"Key": "<KEY HERE>",
|
||||
"KeySecret": "<SECRET HERE>"
|
||||
"Key": "<",
|
||||
"KeySecret": ""
|
||||
},
|
||||
{
|
||||
"ApiName": "DiscogsConsumerKey",
|
||||
"Key": "<KEY HERE>",
|
||||
"KeySecret": "<SECRET HERE>"
|
||||
"Key": "",
|
||||
"KeySecret": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -6,12 +6,9 @@ namespace Roadie.Library.Configuration
|
|||
{
|
||||
public class Integrations : IIntegrations
|
||||
{
|
||||
private string _discogsConsumerKey = null;
|
||||
private string _discogsConsumerSecret = null;
|
||||
private bool _discogsEnabled = true;
|
||||
private string _lastFMApiKey = null;
|
||||
private bool _bingSearchEnabled = true;
|
||||
private bool _lastFmEnabled = true;
|
||||
private string _lastFMSecret = null;
|
||||
|
||||
public List<ApiKey> ApiKeys { get; set; } = new List<ApiKey>();
|
||||
|
||||
|
@ -58,6 +55,21 @@ namespace Roadie.Library.Configuration
|
|||
|
||||
public bool ITunesProviderEnabled { get; set; }
|
||||
|
||||
public bool BingImageSearchEngineEnabled
|
||||
{
|
||||
get
|
||||
{
|
||||
var apiKey = ApiKeys.FirstOrDefault(x => x.ApiName == "BingImageSearch");
|
||||
if (string.IsNullOrEmpty(apiKey?.Key))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return _bingSearchEnabled;
|
||||
}
|
||||
set => _bingSearchEnabled = value;
|
||||
}
|
||||
|
||||
|
||||
public string LastFMApiKey
|
||||
{
|
||||
get
|
||||
|
@ -94,5 +106,11 @@ namespace Roadie.Library.Configuration
|
|||
|
||||
public bool MusicBrainzProviderEnabled { get; set; }
|
||||
public bool SpotifyProviderEnabled { get; set; }
|
||||
|
||||
public Integrations()
|
||||
{
|
||||
DiscogsReadWriteTimeout = short.MaxValue;
|
||||
DiscogsTimeout = short.MaxValue;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -47,9 +47,9 @@ namespace Roadie.Library.Data
|
|||
return $"urn:artist_by_name:{name}";
|
||||
}
|
||||
|
||||
public string ArtistFileFolder(IRoadieSettings configuration, string destinationRoot)
|
||||
public string ArtistFileFolder(IRoadieSettings configuration)
|
||||
{
|
||||
return FolderPathHelper.ArtistPath(configuration, SortNameValue, destinationRoot);
|
||||
return FolderPathHelper.ArtistPath(configuration, SortNameValue);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
|
|
|
@ -48,17 +48,17 @@ namespace Roadie.Library.Data
|
|||
/// <summary>
|
||||
/// Returns a full file path to the current track
|
||||
/// </summary>
|
||||
public string PathToTrack(IRoadieSettings configuration, string libraryFolder)
|
||||
public string PathToTrack(IRoadieSettings configuration)
|
||||
{
|
||||
return FolderPathHelper.PathForTrack(configuration, this, libraryFolder);
|
||||
return FolderPathHelper.PathForTrack(configuration, this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a full file path to the current track thumbnail (if any)
|
||||
/// </summary>
|
||||
public string PathToTrackThumbnail(IRoadieSettings configuration, string libraryFolder)
|
||||
public string PathToTrackThumbnail(IRoadieSettings configuration)
|
||||
{
|
||||
return FolderPathHelper.PathForTrackThumbnail(configuration, this, libraryFolder);
|
||||
return FolderPathHelper.PathForTrackThumbnail(configuration, this);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
|
|
|
@ -46,17 +46,17 @@ namespace Roadie.Library.Engines
|
|||
public IArtistSearchEngine WikipediaArtistSearchEngine { get; }
|
||||
|
||||
public ArtistLookupEngine(IRoadieSettings configuration, IHttpEncoder httpEncoder, IRoadieDbContext context,
|
||||
ICacheManager cacheManager, ILogger logger)
|
||||
ICacheManager cacheManager, ILogger<ArtistLookupEngine> logger, musicbrainz.IMusicBrainzProvider musicBrainzProvider,
|
||||
lastfm.ILastFmHelper lastFmHelper, spotify.ISpotifyHelper spotifyHelper, wikipedia.IWikipediaHelper wikipediaHelper,
|
||||
discogs.IDiscogsHelper discogsHelper, IITunesSearchEngine iTunesSearchEngine)
|
||||
: base(configuration, httpEncoder, context, cacheManager, logger)
|
||||
{
|
||||
ITunesArtistSearchEngine = new ITunesSearchEngine(Configuration, CacheManager, Logger);
|
||||
MusicBrainzArtistSearchEngine = new musicbrainz.MusicBrainzProvider(Configuration, CacheManager, Logger);
|
||||
LastFmArtistSearchEngine =
|
||||
new lastfm.LastFmHelper(Configuration, CacheManager, Logger, context, httpEncoder);
|
||||
SpotifyArtistSearchEngine = new spotify.SpotifyHelper(Configuration, CacheManager, Logger);
|
||||
WikipediaArtistSearchEngine =
|
||||
new wikipedia.WikipediaHelper(Configuration, CacheManager, Logger, HttpEncoder);
|
||||
DiscogsArtistSearchEngine = new discogs.DiscogsHelper(Configuration, CacheManager, Logger);
|
||||
ITunesArtistSearchEngine = iTunesSearchEngine;
|
||||
MusicBrainzArtistSearchEngine = musicBrainzProvider;
|
||||
LastFmArtistSearchEngine = lastFmHelper;
|
||||
SpotifyArtistSearchEngine = spotifyHelper;
|
||||
WikipediaArtistSearchEngine = wikipediaHelper;
|
||||
DiscogsArtistSearchEngine = discogsHelper;
|
||||
}
|
||||
|
||||
public async Task<OperationResult<Artist>> Add(Artist artist)
|
||||
|
@ -240,24 +240,28 @@ namespace Roadie.Library.Engines
|
|||
OperationResult<Artist> artistSearch = null;
|
||||
|
||||
// See if roadie.json file exists in the metadata files folder, if so then use artist data from that
|
||||
var releaseRoadieDataFilename = Path.Combine(Path.GetDirectoryName(metaData.Filename),
|
||||
"roadie.artist.json");
|
||||
if (File.Exists(releaseRoadieDataFilename))
|
||||
string releaseRoadieDataFilename = null;
|
||||
try
|
||||
{
|
||||
releaseRoadieDataFilename = Path.Combine(Path.GetDirectoryName(metaData.Filename), "roadie.artist.json");
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
if (!string.IsNullOrEmpty(releaseRoadieDataFilename) && File.Exists(releaseRoadieDataFilename))
|
||||
{
|
||||
artist = JsonConvert.DeserializeObject<Artist>(File.ReadAllText(releaseRoadieDataFilename));
|
||||
var addResult = await Add(artist);
|
||||
if (!addResult.IsSuccess)
|
||||
{
|
||||
sw.Stop();
|
||||
Logger.LogWarning("Unable To Add Artist For Roadie Data File [{0}]",
|
||||
releaseRoadieDataFilename);
|
||||
Logger.LogWarning("Unable To Add Artist For Roadie Data File [{0}]", releaseRoadieDataFilename);
|
||||
return new OperationResult<Artist>
|
||||
{
|
||||
OperationTime = sw.ElapsedMilliseconds,
|
||||
Errors = addResult.Errors
|
||||
};
|
||||
}
|
||||
|
||||
artist = addResult.Data;
|
||||
}
|
||||
else
|
||||
|
@ -508,10 +512,14 @@ namespace Roadie.Library.Engines
|
|||
if (d.Urls != null) result.URLs = result.URLs.AddToDelimitedList(d.Urls);
|
||||
if (d.ImageUrls != null) artistImageUrls.AddRange(d.ImageUrls);
|
||||
if (d.AlternateNames != null)
|
||||
{
|
||||
result.AlternateNames = result.AlternateNames.AddToDelimitedList(d.AlternateNames);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(d.ArtistName) &&
|
||||
!d.ArtistName.Equals(result.Name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
result.AlternateNames.AddToDelimitedList(new[] { d.ArtistName });
|
||||
}
|
||||
result.CopyTo(new Artist
|
||||
{
|
||||
Profile = HttpEncoder.HtmlEncode(d.Profile),
|
||||
|
|
|
@ -20,10 +20,10 @@ namespace Roadie.Library.Engines
|
|||
private ILabelSearchEngine DiscogsLabelSearchEngine { get; }
|
||||
|
||||
public LabelLookupEngine(IRoadieSettings configuration, IHttpEncoder httpEncoder, IRoadieDbContext context,
|
||||
ICacheManager cacheManager, ILogger logger)
|
||||
ICacheManager cacheManager, ILogger<LabelLookupEngine> logger, discogs.IDiscogsHelper discogsHelper)
|
||||
: base(configuration, httpEncoder, context, cacheManager, logger)
|
||||
{
|
||||
DiscogsLabelSearchEngine = new discogs.DiscogsHelper(Configuration, CacheManager, Logger);
|
||||
DiscogsLabelSearchEngine = discogsHelper;
|
||||
}
|
||||
|
||||
public async Task<OperationResult<Label>> Add(Label label)
|
||||
|
|
|
@ -53,21 +53,21 @@ namespace Roadie.Library.Engines
|
|||
private ILabelLookupEngine LabelLookupEngine { get; }
|
||||
|
||||
public ReleaseLookupEngine(IRoadieSettings configuration, IHttpEncoder httpEncoder, IRoadieDbContext context,
|
||||
ICacheManager cacheManager, ILogger logger, IArtistLookupEngine artistLookupEngine,
|
||||
ILabelLookupEngine labelLookupEngine)
|
||||
ICacheManager cacheManager, ILogger<ReleaseLookupEngine> logger, IArtistLookupEngine artistLookupEngine,
|
||||
ILabelLookupEngine labelLookupEngine, musicbrainz.IMusicBrainzProvider musicBrainzProvider, lastfm.ILastFmHelper lastFmHelper,
|
||||
spotify.ISpotifyHelper spotifyHelper, wikipedia.IWikipediaHelper wikipediaHelper, discogs.IDiscogsHelper discogsHelper,
|
||||
IITunesSearchEngine iTunesSearchEngine)
|
||||
: base(configuration, httpEncoder, context, cacheManager, logger)
|
||||
{
|
||||
ArtistLookupEngine = ArtistLookupEngine;
|
||||
ArtistLookupEngine = artistLookupEngine;
|
||||
LabelLookupEngine = labelLookupEngine;
|
||||
|
||||
ITunesReleaseSearchEngine = new ITunesSearchEngine(Configuration, CacheManager, Logger);
|
||||
MusicBrainzReleaseSearchEngine = new musicbrainz.MusicBrainzProvider(Configuration, CacheManager, Logger);
|
||||
LastFmReleaseSearchEngine =
|
||||
new lastfm.LastFmHelper(Configuration, CacheManager, Logger, context, httpEncoder);
|
||||
DiscogsReleaseSearchEngine = new discogs.DiscogsHelper(Configuration, CacheManager, Logger);
|
||||
SpotifyReleaseSearchEngine = new spotify.SpotifyHelper(Configuration, CacheManager, Logger);
|
||||
WikipediaReleaseSearchEngine =
|
||||
new wikipedia.WikipediaHelper(Configuration, CacheManager, Logger, HttpEncoder);
|
||||
ITunesReleaseSearchEngine = iTunesSearchEngine;
|
||||
MusicBrainzReleaseSearchEngine = musicBrainzProvider;
|
||||
LastFmReleaseSearchEngine = lastFmHelper;
|
||||
DiscogsReleaseSearchEngine = discogsHelper;
|
||||
SpotifyReleaseSearchEngine = spotifyHelper;
|
||||
WikipediaReleaseSearchEngine = wikipediaHelper;
|
||||
}
|
||||
|
||||
public async Task<OperationResult<Release>> Add(Release release, bool doAddTracksInDatabase = false)
|
||||
|
@ -109,6 +109,8 @@ namespace Roadie.Library.Engines
|
|||
{
|
||||
_addedReleaseIds.Add(release.Id);
|
||||
if (releaseGenreTables != null && releaseGenreTables.Any(x => x.GenreId == null))
|
||||
{
|
||||
var addedGenreIds = new List<int>();
|
||||
foreach (var releaseGenreTable in releaseGenreTables)
|
||||
{
|
||||
var genreName = releaseGenreTable.Genre?.Name?.ToAlphanumericName();
|
||||
|
@ -120,7 +122,6 @@ namespace Roadie.Library.Engines
|
|||
genreName = genreName.Substring(0, 99);
|
||||
Logger.LogWarning($"Genre Name Too long was [{originalName}] truncated to [{releaseGenreTable.Genre.Name}]");
|
||||
}
|
||||
|
||||
var genre = DbContext.Genres.FirstOrDefault(x => x.NormalizedName == genreName);
|
||||
if (genre == null)
|
||||
{
|
||||
|
@ -132,17 +133,19 @@ namespace Roadie.Library.Engines
|
|||
DbContext.Genres.Add(genre);
|
||||
await DbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
if (genre != null && genre.Id > 0)
|
||||
if (genre != null &&
|
||||
genre.Id > 0 &&
|
||||
!addedGenreIds.Any(x => x == genre.Id))
|
||||
{
|
||||
DbContext.ReleaseGenres.Add(new ReleaseGenre
|
||||
DbContext.ReleaseGenres.Add(new ReleaseGenre
|
||||
{
|
||||
ReleaseId = release.Id,
|
||||
GenreId = genre.Id
|
||||
});
|
||||
|
||||
addedGenreIds.Add(genre.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (releaseImages != null && releaseImages.Any(x => x.Status == Statuses.New))
|
||||
{
|
||||
|
@ -193,6 +196,7 @@ namespace Roadie.Library.Engines
|
|||
}
|
||||
|
||||
if (doAddTracksInDatabase)
|
||||
{
|
||||
if (releaseMedias != null && releaseMedias.Any(x => x.Status == Statuses.New))
|
||||
{
|
||||
foreach (var newReleaseMedia in releaseMedias.Where(x => x.Status == Statuses.New))
|
||||
|
@ -214,10 +218,11 @@ namespace Roadie.Library.Engines
|
|||
{
|
||||
if (!release.IsCastRecording)
|
||||
{
|
||||
var trackArtistData =
|
||||
await ArtistLookupEngine.GetByName(
|
||||
new AudioMetaData { Artist = newTrack.TrackArtist.Name }, true);
|
||||
if (trackArtistData.IsSuccess) trackArtistId = trackArtistData.Data.Id;
|
||||
var trackArtistData = await ArtistLookupEngine.GetByName(new AudioMetaData { Artist = newTrack.TrackArtist.Name }, true);
|
||||
if (trackArtistData.IsSuccess)
|
||||
{
|
||||
trackArtistId = trackArtistData.Data.Id;
|
||||
}
|
||||
}
|
||||
else if (newTrack.TrackArtists != null && newTrack.TrackArtists.Any())
|
||||
{
|
||||
|
@ -261,7 +266,7 @@ namespace Roadie.Library.Engines
|
|||
Logger.LogError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Logger.LogInformation("Added New Release: `{0}`", release.ToString());
|
||||
}
|
||||
}
|
||||
|
@ -337,7 +342,7 @@ namespace Roadie.Library.Engines
|
|||
try
|
||||
{
|
||||
releaseSearch = await PerformMetaDataProvidersReleaseSearch(metaData,
|
||||
artist.ArtistFileFolder(Configuration, Configuration.LibraryFolder), submissionId);
|
||||
artist.ArtistFileFolder(Configuration), submissionId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
|
@ -1,479 +0,0 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Roadie.Library.Caching;
|
||||
using Roadie.Library.Configuration;
|
||||
using Roadie.Library.Data;
|
||||
using Roadie.Library.Encoding;
|
||||
using Roadie.Library.Engines;
|
||||
using Roadie.Library.Enums;
|
||||
using Roadie.Library.Extensions;
|
||||
using Roadie.Library.Imaging;
|
||||
using Roadie.Library.MetaData.Audio;
|
||||
using Roadie.Library.Processors;
|
||||
using Roadie.Library.Utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Roadie.Library.Factories
|
||||
{
|
||||
#pragma warning disable EF1000
|
||||
|
||||
public sealed class ArtistFactory : FactoryBase, IArtistFactory
|
||||
{
|
||||
private readonly List<int> _addedArtistIds = new List<int>();
|
||||
|
||||
public ArtistFactory(IRoadieSettings configuration, IHttpEncoder httpEncoder, IRoadieDbContext context,
|
||||
ICacheManager cacheManager, ILogger logger, IArtistLookupEngine artistLookupEngine,
|
||||
IReleaseFactory releaseFactory, IImageFactory imageFactory, IReleaseLookupEngine releaseLookupEngine,
|
||||
IAudioMetaDataHelper audioMetaDataHelper)
|
||||
: base(configuration, context, cacheManager, logger, httpEncoder, artistLookupEngine, releaseLookupEngine)
|
||||
{
|
||||
ReleaseFactory = releaseFactory;
|
||||
ImageFactory = imageFactory;
|
||||
AudioMetaDataHelper = audioMetaDataHelper;
|
||||
}
|
||||
|
||||
public IEnumerable<int> AddedArtistIds => _addedArtistIds;
|
||||
|
||||
private IReleaseFactory ReleaseFactory { get; }
|
||||
private IImageFactory ImageFactory { get; }
|
||||
private IAudioMetaDataHelper AudioMetaDataHelper { get; }
|
||||
|
||||
public async Task<OperationResult<bool>> Delete(Guid RoadieId)
|
||||
{
|
||||
var isSuccess = false;
|
||||
var Artist = DbContext.Artists.FirstOrDefault(x => x.RoadieId == RoadieId);
|
||||
if (Artist != null) return await Delete(Artist);
|
||||
return new OperationResult<bool>
|
||||
{
|
||||
Data = isSuccess
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<OperationResult<bool>> Delete(Artist Artist)
|
||||
{
|
||||
var isSuccess = false;
|
||||
try
|
||||
{
|
||||
if (Artist != null)
|
||||
{
|
||||
DbContext.Artists.Remove(Artist);
|
||||
await DbContext.SaveChangesAsync();
|
||||
CacheManager.ClearRegion(Artist.CacheRegion);
|
||||
Logger.LogInformation(string.Format("x DeleteArtist [{0}]", Artist.Id));
|
||||
isSuccess = true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex, ex.Serialize());
|
||||
return new OperationResult<bool>
|
||||
{
|
||||
Errors = new Exception[1] { ex }
|
||||
};
|
||||
}
|
||||
|
||||
return new OperationResult<bool>
|
||||
{
|
||||
IsSuccess = isSuccess,
|
||||
Data = isSuccess
|
||||
};
|
||||
}
|
||||
|
||||
public OperationResult<Artist> GetByExternalIds(string musicBrainzId = null, string iTunesId = null,
|
||||
string amgId = null, string spotifyId = null)
|
||||
{
|
||||
var sw = new Stopwatch();
|
||||
sw.Start();
|
||||
var Artist = (from a in DbContext.Artists
|
||||
where a.MusicBrainzId != null && musicBrainzId != null && a.MusicBrainzId == musicBrainzId ||
|
||||
a.ITunesId != null || iTunesId != null && a.ITunesId == iTunesId || a.AmgId != null ||
|
||||
amgId != null && a.AmgId == amgId || a.SpotifyId != null ||
|
||||
spotifyId != null && a.SpotifyId == spotifyId
|
||||
select a).FirstOrDefault();
|
||||
sw.Stop();
|
||||
if (Artist == null || !Artist.IsValid)
|
||||
Logger.LogTrace(
|
||||
"ArtistFactory: Artist Not Found By External Ids: MusicbrainzId [{0}], iTunesIs [{1}], AmgId [{2}], SpotifyId [{3}]",
|
||||
musicBrainzId, iTunesId, amgId, spotifyId);
|
||||
return new OperationResult<Artist>
|
||||
{
|
||||
IsSuccess = Artist != null,
|
||||
OperationTime = sw.ElapsedMilliseconds,
|
||||
Data = Artist
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Merge one Artist into another one
|
||||
/// </summary>
|
||||
/// <param name="artistToMerge">The Artist to be merged</param>
|
||||
/// <param name="artistToMergeInto">The Artist to merge into</param>
|
||||
/// <returns></returns>
|
||||
public async Task<OperationResult<Artist>> MergeArtists(Artist artistToMerge, Artist artistToMergeInto,
|
||||
bool doDbUpdates = false)
|
||||
{
|
||||
SimpleContract.Requires<ArgumentNullException>(artistToMerge != null, "Invalid Artist");
|
||||
SimpleContract.Requires<ArgumentNullException>(artistToMergeInto != null, "Invalid Artist");
|
||||
|
||||
var result = false;
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
var sw = new Stopwatch();
|
||||
sw.Start();
|
||||
|
||||
artistToMergeInto.RealName = artistToMerge.RealName ?? artistToMergeInto.RealName;
|
||||
artistToMergeInto.MusicBrainzId = artistToMerge.MusicBrainzId ?? artistToMergeInto.MusicBrainzId;
|
||||
artistToMergeInto.ITunesId = artistToMerge.ITunesId ?? artistToMergeInto.ITunesId;
|
||||
artistToMergeInto.AmgId = artistToMerge.AmgId ?? artistToMergeInto.AmgId;
|
||||
artistToMergeInto.SpotifyId = artistToMerge.SpotifyId ?? artistToMergeInto.SpotifyId;
|
||||
artistToMergeInto.Thumbnail = artistToMerge.Thumbnail ?? artistToMergeInto.Thumbnail;
|
||||
artistToMergeInto.Profile = artistToMerge.Profile ?? artistToMergeInto.Profile;
|
||||
artistToMergeInto.BirthDate = artistToMerge.BirthDate ?? artistToMergeInto.BirthDate;
|
||||
artistToMergeInto.BeginDate = artistToMerge.BeginDate ?? artistToMergeInto.BeginDate;
|
||||
artistToMergeInto.EndDate = artistToMerge.EndDate ?? artistToMergeInto.EndDate;
|
||||
if (!string.IsNullOrEmpty(artistToMerge.ArtistType) && !artistToMerge.ArtistType.Equals("Other", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
artistToMergeInto.ArtistType = artistToMerge.ArtistType;
|
||||
}
|
||||
artistToMergeInto.BioContext = artistToMerge.BioContext ?? artistToMergeInto.BioContext;
|
||||
artistToMergeInto.DiscogsId = artistToMerge.DiscogsId ?? artistToMergeInto.DiscogsId;
|
||||
artistToMergeInto.Tags = artistToMergeInto.Tags.AddToDelimitedList(artistToMerge.Tags.ToListFromDelimited());
|
||||
var altNames = artistToMerge.AlternateNames.ToListFromDelimited().ToList();
|
||||
altNames.Add(artistToMerge.Name);
|
||||
altNames.Add(artistToMerge.SortName);
|
||||
artistToMergeInto.AlternateNames = artistToMergeInto.AlternateNames.AddToDelimitedList(altNames);
|
||||
artistToMergeInto.URLs = artistToMergeInto.URLs.AddToDelimitedList(artistToMerge.URLs.ToListFromDelimited());
|
||||
artistToMergeInto.ISNI = artistToMergeInto.ISNI.AddToDelimitedList(artistToMerge.ISNI.ToListFromDelimited());
|
||||
artistToMergeInto.LastUpdated = now;
|
||||
|
||||
if (doDbUpdates)
|
||||
{
|
||||
try
|
||||
{
|
||||
var artistGenres = DbContext.ArtistGenres.Where(x => x.ArtistId == artistToMerge.Id).ToArray();
|
||||
if (artistGenres != null)
|
||||
{
|
||||
foreach (var artistGenre in artistGenres)
|
||||
{
|
||||
artistGenre.ArtistId = artistToMergeInto.Id;
|
||||
}
|
||||
}
|
||||
var artistImages = DbContext.Images.Where(x => x.ArtistId == artistToMerge.Id).ToArray();
|
||||
if (artistImages != null)
|
||||
{
|
||||
foreach (var artistImage in artistImages)
|
||||
{
|
||||
artistImage.ArtistId = artistToMergeInto.Id;
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
foreach (var artistTrack in artistTracks)
|
||||
{
|
||||
artistTrack.ArtistId = artistToMergeInto.Id;
|
||||
}
|
||||
}
|
||||
var artistReleases = DbContext.Releases.Where(x => x.ArtistId == artistToMerge.Id).ToArray();
|
||||
if (artistReleases != null)
|
||||
{
|
||||
foreach (var artistRelease in artistReleases)
|
||||
{
|
||||
// See if there is already a release by the same name for the artist to merge into, if so then merge releases
|
||||
var artistToMergeHasRelease = DbContext.Releases.FirstOrDefault(x => x.ArtistId == artistToMerge.Id && x.Title == artistRelease.Title);
|
||||
if (artistToMergeHasRelease != null)
|
||||
{
|
||||
await ReleaseFactory.MergeReleases(artistRelease, artistToMergeHasRelease, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
artistRelease.ArtistId = artistToMerge.Id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogWarning(ex.ToString());
|
||||
}
|
||||
|
||||
var artistFolder = artistToMerge.ArtistFileFolder(Configuration, Configuration.LibraryFolder);
|
||||
foreach (var release in DbContext.Releases.Include("Artist").Where(x => x.ArtistId == artistToMerge.Id).ToArray())
|
||||
{
|
||||
var originalReleaseFolder = release.ReleaseFileFolder(artistFolder);
|
||||
await ReleaseFactory.Update(release, null, originalReleaseFolder);
|
||||
}
|
||||
await Delete(artistToMerge);
|
||||
}
|
||||
|
||||
result = true;
|
||||
|
||||
sw.Stop();
|
||||
return new OperationResult<Artist>
|
||||
{
|
||||
Data = artistToMergeInto,
|
||||
IsSuccess = result,
|
||||
OperationTime = sw.ElapsedMilliseconds
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform a Metadata Provider search and then merge the results into the given Artist
|
||||
/// </summary>
|
||||
/// <param name="ArtistId">Given Artist RoadieId</param>
|
||||
/// <returns>Operation Result</returns>
|
||||
public async Task<OperationResult<bool>> RefreshArtistMetadata(Guid ArtistId)
|
||||
{
|
||||
SimpleContract.Requires<ArgumentOutOfRangeException>(ArtistId != Guid.Empty, "Invalid ArtistId");
|
||||
|
||||
var result = true;
|
||||
var resultErrors = new List<Exception>();
|
||||
var sw = new Stopwatch();
|
||||
sw.Start();
|
||||
try
|
||||
{
|
||||
var Artist = DbContext.Artists.FirstOrDefault(x => x.RoadieId == ArtistId);
|
||||
if (Artist == null)
|
||||
{
|
||||
Logger.LogWarning("Unable To Find Artist [{0}]", ArtistId);
|
||||
return new OperationResult<bool>();
|
||||
}
|
||||
|
||||
OperationResult<Artist> ArtistSearch = null;
|
||||
try
|
||||
{
|
||||
ArtistSearch = await ArtistLookupEngine.PerformMetaDataProvidersArtistSearch(new AudioMetaData
|
||||
{
|
||||
Artist = Artist.Name
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex, ex.Serialize());
|
||||
}
|
||||
|
||||
if (ArtistSearch.IsSuccess)
|
||||
{
|
||||
// Do metadata search for Artist like if new Artist then set some overides and merge
|
||||
var mergeResult = await MergeArtists(ArtistSearch.Data, Artist);
|
||||
if (mergeResult.IsSuccess)
|
||||
{
|
||||
Artist = mergeResult.Data;
|
||||
await DbContext.SaveChangesAsync();
|
||||
sw.Stop();
|
||||
CacheManager.ClearRegion(Artist.CacheRegion);
|
||||
Logger.LogInformation("Scanned RefreshArtistMetadata [{0}], OperationTime [{1}]",
|
||||
Artist.ToString(), sw.ElapsedMilliseconds);
|
||||
}
|
||||
else
|
||||
{
|
||||
sw.Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex, ex.Serialize());
|
||||
resultErrors.Add(ex);
|
||||
}
|
||||
|
||||
return new OperationResult<bool>
|
||||
{
|
||||
Data = result,
|
||||
IsSuccess = result,
|
||||
Errors = resultErrors,
|
||||
OperationTime = sw.ElapsedMilliseconds
|
||||
};
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public async Task<OperationResult<bool>> ScanArtistReleasesFolders(Guid artistId, string destinationFolder,
|
||||
bool doJustInfo)
|
||||
{
|
||||
SimpleContract.Requires<ArgumentOutOfRangeException>(artistId != Guid.Empty, "Invalid ArtistId");
|
||||
|
||||
var result = true;
|
||||
var resultErrors = new List<Exception>();
|
||||
var sw = new Stopwatch();
|
||||
sw.Start();
|
||||
try
|
||||
{
|
||||
var artist = DbContext.Artists
|
||||
.Include("Releases")
|
||||
.Include("Releases.Labels")
|
||||
.FirstOrDefault(x => x.RoadieId == artistId);
|
||||
if (artist == null)
|
||||
{
|
||||
Logger.LogWarning("Unable To Find Artist [{0}]", artistId);
|
||||
return new OperationResult<bool>();
|
||||
}
|
||||
|
||||
var releaseScannedCount = 0;
|
||||
var artistFolder = artist.ArtistFileFolder(Configuration, destinationFolder);
|
||||
var scannedArtistFolders = new List<string>();
|
||||
// Scan known releases for changes
|
||||
if (artist.Releases != null)
|
||||
foreach (var release in artist.Releases)
|
||||
try
|
||||
{
|
||||
result = result && (await ReleaseFactory.ScanReleaseFolder(Guid.Empty, destinationFolder,
|
||||
doJustInfo, release)).Data;
|
||||
releaseScannedCount++;
|
||||
scannedArtistFolders.Add(release.ReleaseFileFolder(artistFolder));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex, ex.Serialize());
|
||||
}
|
||||
|
||||
// Any folder found in Artist folder not already scanned scan
|
||||
var folderProcessor = new FolderProcessor(Configuration, HttpEncoder, destinationFolder, DbContext,
|
||||
CacheManager, Logger, ArtistLookupEngine, this, ReleaseFactory, ImageFactory, ReleaseLookupEngine,
|
||||
AudioMetaDataHelper);
|
||||
var nonReleaseFolders = from d in Directory.EnumerateDirectories(artistFolder)
|
||||
where !(from r in scannedArtistFolders select r).Contains(d)
|
||||
orderby d
|
||||
select d;
|
||||
foreach (var folder in nonReleaseFolders)
|
||||
await folderProcessor.Process(new DirectoryInfo(folder), doJustInfo);
|
||||
if (!doJustInfo) FolderProcessor.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 imageFile = imageFiles.First();
|
||||
var i = new FileInfo(imageFile);
|
||||
var iName = i.Name.ToLower().Trim();
|
||||
var isArtistImage = iName.Contains("artist") || iName.Contains(artist.Name.ToLower());
|
||||
if (isArtistImage)
|
||||
{
|
||||
// Read image and convert to jpeg
|
||||
artist.Thumbnail = File.ReadAllBytes(i.FullName);
|
||||
artist.Thumbnail = ImageHelper.ResizeImage(artist.Thumbnail,
|
||||
Configuration.MediumImageSize.Width, Configuration.MediumImageSize.Height);
|
||||
artist.Thumbnail = ImageHelper.ConvertToJpegFormat(artist.Thumbnail);
|
||||
if (artist.Thumbnail.Length >= ImageHelper.MaximumThumbnailByteSize)
|
||||
{
|
||||
Logger.LogWarning(
|
||||
$"Artist Thumbnail larger than maximum size after resizing to [{Configuration.ThumbnailImageSize.Width}x{Configuration.ThumbnailImageSize.Height}] Thumbnail Size [{artist.Thumbnail.Length}]");
|
||||
artist.Thumbnail = null;
|
||||
}
|
||||
|
||||
artist.LastUpdated = DateTime.UtcNow;
|
||||
await DbContext.SaveChangesAsync();
|
||||
CacheManager.ClearRegion(artist.CacheRegion);
|
||||
Logger.LogInformation("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);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex, ex.Serialize());
|
||||
resultErrors.Add(ex);
|
||||
}
|
||||
|
||||
return new OperationResult<bool>
|
||||
{
|
||||
Data = result,
|
||||
IsSuccess = result,
|
||||
Errors = resultErrors,
|
||||
OperationTime = sw.ElapsedMilliseconds
|
||||
};
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public async Task<OperationResult<Artist>> Update(Artist Artist, IEnumerable<Image> ArtistImages,
|
||||
string destinationFolder = null)
|
||||
{
|
||||
SimpleContract.Requires<ArgumentNullException>(Artist != null, "Invalid Artist");
|
||||
|
||||
var sw = new Stopwatch();
|
||||
sw.Start();
|
||||
|
||||
var artistGenreTables = Artist.Genres
|
||||
.Select(x => new ArtistGenre { ArtistId = Artist.Id, GenreId = x.GenreId }).ToList();
|
||||
var artistAssociatedWith = Artist.AssociatedArtists.Select(x => new ArtistAssociation
|
||||
{ ArtistId = Artist.Id, AssociatedArtistId = x.AssociatedArtistId }).ToList();
|
||||
var similarArtists = Artist.SimilarArtists.Select(x => new ArtistSimilar
|
||||
{ ArtistId = Artist.Id, SimilarArtistId = x.SimilarArtistId }).ToList();
|
||||
var result = true;
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
var originalArtistFolder =
|
||||
Artist.ArtistFileFolder(Configuration, destinationFolder ?? Configuration.LibraryFolder);
|
||||
var originalName = Artist.Name;
|
||||
var originalSortName = Artist.SortName;
|
||||
|
||||
Artist.LastUpdated = now;
|
||||
await DbContext.SaveChangesAsync();
|
||||
|
||||
DbContext.ArtistGenres.RemoveRange(from at in DbContext.ArtistGenres
|
||||
where at.ArtistId == Artist.Id
|
||||
select at);
|
||||
Artist.Genres = artistGenreTables;
|
||||
DbContext.ArtistAssociations.RemoveRange(from at in DbContext.ArtistAssociations
|
||||
where at.ArtistId == Artist.Id
|
||||
select at);
|
||||
Artist.AssociatedArtists = artistAssociatedWith;
|
||||
Artist.SimilarArtists = similarArtists;
|
||||
await DbContext.SaveChangesAsync();
|
||||
|
||||
var existingImageIds = (from ai in ArtistImages
|
||||
where ai.Status != Statuses.New
|
||||
select ai.RoadieId).ToArray();
|
||||
DbContext.Images.RemoveRange(from i in DbContext.Images
|
||||
where i.ArtistId == Artist.Id
|
||||
where !(from x in existingImageIds select x).Contains(i.RoadieId)
|
||||
select i);
|
||||
await DbContext.SaveChangesAsync();
|
||||
if (ArtistImages != null && ArtistImages.Any(x => x.Status == Statuses.New))
|
||||
{
|
||||
foreach (var ArtistImage in ArtistImages.Where(x => x.Status == Statuses.New))
|
||||
DbContext.Images.Add(ArtistImage);
|
||||
try
|
||||
{
|
||||
await DbContext.SaveChangesAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex, ex.Serialize());
|
||||
}
|
||||
}
|
||||
|
||||
var newArtistFolder =
|
||||
Artist.ArtistFileFolder(Configuration, destinationFolder ?? Configuration.LibraryFolder);
|
||||
if (!originalArtistFolder.Equals(newArtistFolder, StringComparison.OrdinalIgnoreCase))
|
||||
Logger.LogTrace("Moving Artist From Folder [{0}] To [{1}]", originalArtistFolder, newArtistFolder);
|
||||
// Directory.Move(originalArtistFolder, Artist.ArtistFileFolder(destinationFolder ?? SettingsHelper.Instance.LibraryFolder));
|
||||
// TODO if name changed then update Artist track files to have new Artist name
|
||||
CacheManager.ClearRegion(Artist.CacheRegion);
|
||||
sw.Stop();
|
||||
|
||||
return new OperationResult<Artist>
|
||||
{
|
||||
Data = Artist,
|
||||
IsSuccess = result,
|
||||
OperationTime = sw.ElapsedMilliseconds
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,128 +0,0 @@
|
|||
using Microsoft.Extensions.Logging;
|
||||
using Roadie.Library.Caching;
|
||||
using Roadie.Library.Configuration;
|
||||
using Roadie.Library.Data;
|
||||
using Roadie.Library.Encoding;
|
||||
using Roadie.Library.Engines;
|
||||
using Roadie.Library.SearchEngines.MetaData;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Roadie.Library.Factories
|
||||
{
|
||||
public abstract class FactoryBase
|
||||
{
|
||||
protected IArtistLookupEngine ArtistLookupEngine { get; }
|
||||
|
||||
protected ICacheManager CacheManager { get; }
|
||||
|
||||
protected IRoadieSettings Configuration { get; }
|
||||
|
||||
protected IRoadieDbContext DbContext { get; }
|
||||
|
||||
protected ILabelSearchEngine DiscogsLabelSearchEngine { get; }
|
||||
|
||||
protected IHttpEncoder HttpEncoder { get; }
|
||||
|
||||
protected ILogger Logger { get; }
|
||||
|
||||
protected IReleaseLookupEngine ReleaseLookupEngine { get; }
|
||||
|
||||
public FactoryBase(IRoadieSettings configuration, IRoadieDbContext context, ICacheManager cacheManager,
|
||||
ILogger logger, IHttpEncoder httpEncoder, IArtistLookupEngine artistLookupEngine,
|
||||
IReleaseLookupEngine releaseLookupEngine)
|
||||
{
|
||||
Configuration = configuration;
|
||||
DbContext = context;
|
||||
CacheManager = cacheManager;
|
||||
Logger = logger;
|
||||
HttpEncoder = httpEncoder;
|
||||
|
||||
ArtistLookupEngine = artistLookupEngine;
|
||||
ReleaseLookupEngine = releaseLookupEngine;
|
||||
}
|
||||
|
||||
[Obsolete("Use Service Methods")]
|
||||
protected IEnumerable<int> ArtistIdsForRelease(int releaseId)
|
||||
{
|
||||
var trackArtistIds = (from r in DbContext.Releases
|
||||
join rm in DbContext.ReleaseMedias on r.Id equals rm.ReleaseId
|
||||
join tr in DbContext.Tracks on rm.Id equals tr.ReleaseMediaId
|
||||
where r.Id == releaseId
|
||||
where tr.ArtistId != null
|
||||
select tr.ArtistId.Value).ToList();
|
||||
trackArtistIds.Add(DbContext.Releases.FirstOrDefault(x => x.Id == releaseId).ArtistId);
|
||||
return trackArtistIds.Distinct().ToArray();
|
||||
}
|
||||
|
||||
[Obsolete("Use Service Methods")]
|
||||
protected async Task UpdateArtistCounts(int artistId, DateTime now)
|
||||
{
|
||||
var artist = DbContext.Artists.FirstOrDefault(x => x.Id == artistId);
|
||||
if (artist != null)
|
||||
{
|
||||
artist.ReleaseCount = DbContext.Releases.Where(x => x.ArtistId == artistId).Count();
|
||||
artist.TrackCount = (from r in DbContext.Releases
|
||||
join rm in DbContext.ReleaseMedias on r.Id equals rm.ReleaseId
|
||||
join tr in DbContext.Tracks on rm.Id equals tr.ReleaseMediaId
|
||||
where tr.ArtistId == artistId || r.ArtistId == artistId
|
||||
select tr).Count();
|
||||
|
||||
artist.LastUpdated = now;
|
||||
await DbContext.SaveChangesAsync();
|
||||
CacheManager.ClearRegion(artist.CacheRegion);
|
||||
}
|
||||
}
|
||||
|
||||
[Obsolete("Use Service Methods")]
|
||||
/// <summary>
|
||||
/// Update the counts for all artists on a release (both track and release artists)
|
||||
/// </summary>
|
||||
protected async Task UpdateArtistCountsForRelease(int releaseId, DateTime now)
|
||||
{
|
||||
foreach (var artistId in ArtistIdsForRelease(releaseId)) await UpdateArtistCounts(artistId, now);
|
||||
}
|
||||
|
||||
[Obsolete("Use Service Methods")]
|
||||
protected async Task UpdateLabelCounts(int labelId, DateTime now)
|
||||
{
|
||||
var label = DbContext.Labels.FirstOrDefault(x => x.Id == labelId);
|
||||
if (label != null)
|
||||
{
|
||||
label.ReleaseCount = DbContext.ReleaseLabels.Where(x => x.LabelId == label.Id).Count();
|
||||
label.ArtistCount = (from r in DbContext.Releases
|
||||
join rl in DbContext.ReleaseLabels on r.Id equals rl.ReleaseId
|
||||
join a in DbContext.Artists on r.ArtistId equals a.Id
|
||||
where rl.LabelId == label.Id
|
||||
group a by a.Id
|
||||
into artists
|
||||
select artists).Select(x => x.Key).Count();
|
||||
label.TrackCount = (from r in DbContext.Releases
|
||||
join rl in DbContext.ReleaseLabels on r.Id equals rl.ReleaseId
|
||||
join rm in DbContext.ReleaseMedias on r.Id equals rm.ReleaseId
|
||||
join t in DbContext.Tracks on rm.Id equals t.ReleaseMediaId
|
||||
where rl.LabelId == label.Id
|
||||
select t).Count();
|
||||
await DbContext.SaveChangesAsync();
|
||||
CacheManager.ClearRegion(label.CacheRegion);
|
||||
}
|
||||
}
|
||||
|
||||
[Obsolete("Use Service Methods")]
|
||||
protected async Task UpdateReleaseCounts(int releaseId, DateTime now)
|
||||
{
|
||||
var release = DbContext.Releases.FirstOrDefault(x => x.Id == releaseId);
|
||||
if (release != null)
|
||||
{
|
||||
release.Duration = (from t in DbContext.Tracks
|
||||
join rm in DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id
|
||||
where rm.ReleaseId == releaseId
|
||||
select t).Sum(x => x.Duration);
|
||||
await DbContext.SaveChangesAsync();
|
||||
CacheManager.ClearRegion(release.CacheRegion);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
using Roadie.Library.Data;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Roadie.Library.Factories
|
||||
{
|
||||
public interface IArtistFactory
|
||||
{
|
||||
Task<OperationResult<bool>> Delete(Artist Artist);
|
||||
|
||||
Task<OperationResult<bool>> Delete(Guid RoadieId);
|
||||
|
||||
OperationResult<Artist> GetByExternalIds(string musicBrainzId = null, string iTunesId = null,
|
||||
string amgId = null, string spotifyId = null);
|
||||
|
||||
Task<OperationResult<Artist>> MergeArtists(Artist ArtistToMerge, Artist artistToMergeInto,
|
||||
bool doDbUpdates = false);
|
||||
|
||||
Task<OperationResult<bool>> RefreshArtistMetadata(Guid ArtistId);
|
||||
|
||||
Task<OperationResult<bool>> ScanArtistReleasesFolders(Guid artistId, string destinationFolder, bool doJustInfo);
|
||||
|
||||
Task<OperationResult<Artist>> Update(Artist Artist, IEnumerable<Image> ArtistImages,
|
||||
string destinationFolder = null);
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
using Roadie.Library.MetaData.Audio;
|
||||
|
||||
namespace Roadie.Library.Factories
|
||||
{
|
||||
public interface IImageFactory
|
||||
{
|
||||
AudioMetaDataImage GetPictureForMetaData(string filename, AudioMetaData metaData);
|
||||
|
||||
AudioMetaDataImage ImageForFilename(string filename);
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
namespace Roadie.Library.Factories
|
||||
{
|
||||
public interface ILabelFactory
|
||||
{
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
using Roadie.Library.Data;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Roadie.Library.Factories
|
||||
{
|
||||
public interface IPlaylistFactory
|
||||
{
|
||||
Task<OperationResult<bool>> AddTracksToPlaylist(Playlist playlist, IEnumerable<Guid> trackIds);
|
||||
|
||||
Task<OperationResult<bool>> ReorderPlaylist(Playlist playlist);
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
using Roadie.Library.Data;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Roadie.Library.Factories
|
||||
{
|
||||
public interface IReleaseFactory
|
||||
{
|
||||
IEnumerable<int> AddedTrackIds { get; }
|
||||
|
||||
Task<OperationResult<bool>> CheckAndChangeReleaseTitle(Release release, string oldReleaseFolder,
|
||||
string destinationFolder = null);
|
||||
|
||||
Task<OperationResult<bool>> Delete(Release release, bool doDeleteFiles = false,
|
||||
bool doUpdateArtistCounts = true);
|
||||
|
||||
Task<OperationResult<bool>> DeleteReleases(IEnumerable<Guid> releaseIds, bool doDeleteFiles = false);
|
||||
|
||||
OperationResult<Release> GetAllForArtist(Artist artist, bool forceRefresh = false);
|
||||
|
||||
Task<OperationResult<bool>> MergeReleases(Release releaseToMerge, Release releaseToMergeInto, bool addAsMedia);
|
||||
|
||||
Task<OperationResult<bool>> ScanReleaseFolder(Guid releaseId, string destinationFolder, bool doJustInfo,
|
||||
Release releaseToScan = null);
|
||||
|
||||
Task<OperationResult<Release>> Update(Release release, IEnumerable<Image> releaseImages,
|
||||
string originalReleaseFolder, string destinationFolder = null);
|
||||
}
|
||||
}
|
|
@ -1,107 +0,0 @@
|
|||
using Microsoft.Extensions.Logging;
|
||||
using Roadie.Library.Caching;
|
||||
using Roadie.Library.Configuration;
|
||||
using Roadie.Library.Data;
|
||||
using Roadie.Library.Encoding;
|
||||
using Roadie.Library.Engines;
|
||||
using Roadie.Library.Extensions;
|
||||
using Roadie.Library.Imaging;
|
||||
using Roadie.Library.MetaData.Audio;
|
||||
using Roadie.Library.Processors;
|
||||
using Roadie.Library.Utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace Roadie.Library.Factories
|
||||
{
|
||||
public sealed class ImageFactory : FactoryBase, IImageFactory
|
||||
{
|
||||
public ImageFactory(IRoadieSettings configuration, IHttpEncoder httpEncoder, IRoadieDbContext context,
|
||||
ICacheManager cacheManager, ILogger logger, IArtistLookupEngine artistLookupEngine,
|
||||
IReleaseLookupEngine releaseLookupEngine)
|
||||
: base(configuration, context, cacheManager, logger, httpEncoder, artistLookupEngine, releaseLookupEngine)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get image data from all sources for either fileanme or MetaData
|
||||
/// </summary>
|
||||
/// <param name="filename">Name of the File (ie a CUE file)</param>
|
||||
/// <param name="metaData">Populated MetaData</param>
|
||||
/// <returns></returns>
|
||||
public AudioMetaDataImage GetPictureForMetaData(string filename, AudioMetaData metaData)
|
||||
{
|
||||
SimpleContract.Requires<ArgumentException>(!string.IsNullOrEmpty(filename), "Invalid Filename");
|
||||
SimpleContract.Requires<ArgumentException>(metaData != null, "Invalid MetaData");
|
||||
|
||||
return ImageForFilename(filename);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does image exist with the same filename
|
||||
/// </summary>
|
||||
/// <param name="filename">Name of the File (ie a CUE file)</param>
|
||||
/// <returns>Null if not found else populated image</returns>
|
||||
public AudioMetaDataImage ImageForFilename(string filename)
|
||||
{
|
||||
AudioMetaDataImage imageMetaData = null;
|
||||
|
||||
if (string.IsNullOrEmpty(filename)) return imageMetaData;
|
||||
try
|
||||
{
|
||||
var fileInfo = new FileInfo(filename);
|
||||
var ReleaseCover = Path.ChangeExtension(filename, "jpg");
|
||||
if (File.Exists(ReleaseCover))
|
||||
{
|
||||
using (var processor = new ImageProcessor(Configuration))
|
||||
{
|
||||
imageMetaData = new AudioMetaDataImage
|
||||
{
|
||||
Data = processor.Process(File.ReadAllBytes(ReleaseCover)),
|
||||
Type = AudioMetaDataImageType.FrontCover,
|
||||
MimeType = FileProcessor.DetermineFileType(fileInfo)
|
||||
};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Is there a picture in filename folder (for the Release)
|
||||
var pictures = fileInfo.Directory.GetFiles("*.jpg");
|
||||
var tagImages = new List<AudioMetaDataImage>();
|
||||
if (pictures != null && pictures.Any())
|
||||
{
|
||||
FileInfo picture = null;
|
||||
// See if there is a "cover" or "front" jpg file if so use it
|
||||
picture = pictures.FirstOrDefault(x =>
|
||||
x.Name.Equals("cover", StringComparison.OrdinalIgnoreCase));
|
||||
if (picture == null)
|
||||
picture = pictures.FirstOrDefault(x =>
|
||||
x.Name.Equals("front", StringComparison.OrdinalIgnoreCase));
|
||||
if (picture == null) picture = pictures.First();
|
||||
if (picture != null)
|
||||
using (var processor = new ImageProcessor(Configuration))
|
||||
{
|
||||
imageMetaData = new AudioMetaDataImage
|
||||
{
|
||||
Data = processor.Process(File.ReadAllBytes(picture.FullName)),
|
||||
Type = AudioMetaDataImageType.FrontCover,
|
||||
MimeType = FileProcessor.DetermineFileType(picture)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex, ex.Serialize());
|
||||
}
|
||||
|
||||
return imageMetaData;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
using Microsoft.Extensions.Logging;
|
||||
using Roadie.Library.Caching;
|
||||
using Roadie.Library.Configuration;
|
||||
using Roadie.Library.Data;
|
||||
using Roadie.Library.Encoding;
|
||||
using Roadie.Library.Engines;
|
||||
|
||||
namespace Roadie.Library.Factories
|
||||
{
|
||||
public sealed class LabelFactory : FactoryBase, ILabelFactory
|
||||
{
|
||||
public LabelFactory(IRoadieSettings configuration, IHttpEncoder httpEncoder, IRoadieDbContext context,
|
||||
ICacheManager cacheManager, ILogger logger, IArtistLookupEngine artistLookupEngine,
|
||||
IReleaseLookupEngine releaseLookupEngine)
|
||||
: base(configuration, context, cacheManager, logger, httpEncoder, artistLookupEngine, releaseLookupEngine)
|
||||
{
|
||||
}
|
||||
|
||||
// TODO Merge
|
||||
}
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
using Microsoft.Extensions.Logging;
|
||||
using Roadie.Library.Caching;
|
||||
using Roadie.Library.Configuration;
|
||||
using Roadie.Library.Data;
|
||||
using Roadie.Library.Encoding;
|
||||
using Roadie.Library.Engines;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Roadie.Library.Factories
|
||||
{
|
||||
public class PlaylistFactory : FactoryBase, IPlaylistFactory
|
||||
{
|
||||
public PlaylistFactory(IRoadieSettings configuration, IHttpEncoder httpEncoder, IRoadieDbContext context,
|
||||
ICacheManager cacheManager, ILogger logger, IArtistLookupEngine artistLookupEngine,
|
||||
IReleaseLookupEngine releaseLookupEngine)
|
||||
: base(configuration, context, cacheManager, logger, httpEncoder, artistLookupEngine, releaseLookupEngine)
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("Use PlaylistService")]
|
||||
public async Task<OperationResult<bool>> AddTracksToPlaylist(Playlist playlist, IEnumerable<Guid> trackIds)
|
||||
{
|
||||
var sw = new Stopwatch();
|
||||
sw.Start();
|
||||
|
||||
var result = false;
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
var existingTracksForPlaylist = from plt in DbContext.PlaylistTracks
|
||||
join t in DbContext.Tracks on plt.TrackId equals t.Id
|
||||
where plt.PlayListId == playlist.Id
|
||||
select t;
|
||||
var newTracksForPlaylist = (from t in DbContext.Tracks
|
||||
where (from x in trackIds select x).Contains(t.RoadieId)
|
||||
where !(from x in existingTracksForPlaylist select x.RoadieId).Contains(t.RoadieId)
|
||||
select t).ToArray();
|
||||
foreach (var newTrackForPlaylist in newTracksForPlaylist)
|
||||
DbContext.PlaylistTracks.Add(new PlaylistTrack
|
||||
{
|
||||
TrackId = newTrackForPlaylist.Id,
|
||||
PlayListId = playlist.Id,
|
||||
CreatedDate = now
|
||||
});
|
||||
playlist.LastUpdated = now;
|
||||
await DbContext.SaveChangesAsync();
|
||||
result = true;
|
||||
|
||||
var r = await ReorderPlaylist(playlist);
|
||||
result = result && r.IsSuccess;
|
||||
|
||||
return new OperationResult<bool>
|
||||
{
|
||||
Data = result
|
||||
};
|
||||
}
|
||||
|
||||
[Obsolete("Use PlaylistService")]
|
||||
public async Task<OperationResult<bool>> ReorderPlaylist(Playlist playlist)
|
||||
{
|
||||
var sw = new Stopwatch();
|
||||
sw.Start();
|
||||
|
||||
var result = false;
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
if (playlist != null)
|
||||
{
|
||||
var looper = 0;
|
||||
foreach (var playlistTrack in DbContext.PlaylistTracks.Where(x => x.PlayListId == playlist.Id)
|
||||
.OrderBy(x => x.CreatedDate))
|
||||
{
|
||||
looper++;
|
||||
playlistTrack.ListNumber = looper;
|
||||
playlistTrack.LastUpdated = now;
|
||||
}
|
||||
|
||||
await DbContext.SaveChangesAsync();
|
||||
result = true;
|
||||
}
|
||||
|
||||
return new OperationResult<bool>
|
||||
{
|
||||
IsSuccess = result,
|
||||
Data = result
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,950 +0,0 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Roadie.Library.Caching;
|
||||
using Roadie.Library.Configuration;
|
||||
using Roadie.Library.Data;
|
||||
using Roadie.Library.Encoding;
|
||||
using Roadie.Library.Engines;
|
||||
using Roadie.Library.Enums;
|
||||
using Roadie.Library.Extensions;
|
||||
using Roadie.Library.Imaging;
|
||||
using Roadie.Library.MetaData.Audio;
|
||||
using Roadie.Library.Processors;
|
||||
using Roadie.Library.Utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Roadie.Library.Factories
|
||||
{
|
||||
#pragma warning disable EF1000
|
||||
|
||||
public sealed class ReleaseFactory : FactoryBase, IReleaseFactory
|
||||
{
|
||||
private readonly List<int> _addedTrackIds = new List<int>();
|
||||
|
||||
public ReleaseFactory(IRoadieSettings configuration, IHttpEncoder httpEncoder, IRoadieDbContext context,
|
||||
ICacheManager cacheManager, ILogger logger, IArtistLookupEngine artistLookupEngine,
|
||||
ILabelFactory labelFactory, IAudioMetaDataHelper audioMetaDataHelper,
|
||||
IReleaseLookupEngine releaseLookupEngine)
|
||||
: base(configuration, context, cacheManager, logger, httpEncoder, artistLookupEngine, releaseLookupEngine)
|
||||
{
|
||||
LabelFactory = labelFactory;
|
||||
AudioMetaDataHelper = audioMetaDataHelper;
|
||||
}
|
||||
|
||||
private IAudioMetaDataHelper AudioMetaDataHelper { get; }
|
||||
|
||||
private ILabelFactory LabelFactory { get; }
|
||||
|
||||
public IEnumerable<int> AddedTrackIds => _addedTrackIds;
|
||||
|
||||
/// <summary>
|
||||
/// See if the given release has properties that have been modified that affect the folder structure, if so then handle
|
||||
/// necessary operations for changes
|
||||
/// </summary>
|
||||
/// <param name="release">Release that has been modified</param>
|
||||
/// <param name="oldReleaseFolder">Folder for release before any changes</param>
|
||||
/// <returns></returns>
|
||||
public async Task<OperationResult<bool>> CheckAndChangeReleaseTitle(Release release, string oldReleaseFolder,
|
||||
string destinationFolder = null)
|
||||
{
|
||||
SimpleContract.Requires<ArgumentNullException>(release != null, "Invalid Release");
|
||||
SimpleContract.Requires<ArgumentNullException>(!string.IsNullOrEmpty(oldReleaseFolder),
|
||||
"Invalid Release Old Folder");
|
||||
|
||||
destinationFolder = destinationFolder ?? Configuration.LibraryFolder;
|
||||
|
||||
var sw = new Stopwatch();
|
||||
sw.Start();
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
var result = false;
|
||||
var artistFolder = release.Artist.ArtistFileFolder(Configuration, destinationFolder);
|
||||
var newReleaseFolder = release.ReleaseFileFolder(artistFolder);
|
||||
if (!oldReleaseFolder.Equals(newReleaseFolder, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Logger.LogTrace("Moving Release From Folder [{0}] To [{1}]", oldReleaseFolder, newReleaseFolder);
|
||||
|
||||
// Create the new release folder
|
||||
if (!Directory.Exists(newReleaseFolder)) Directory.CreateDirectory(newReleaseFolder);
|
||||
var releaseDirectoryInfo = new DirectoryInfo(newReleaseFolder);
|
||||
// Update and move tracks under new release folder
|
||||
foreach (var releaseMedia in DbContext.ReleaseMedias.Where(x => x.ReleaseId == release.Id).ToArray())
|
||||
// Update the track path to have the new album title. This is needed because future scans might not work properly without updating track title.
|
||||
foreach (var track in DbContext.Tracks.Where(x => x.ReleaseMediaId == releaseMedia.Id).ToArray())
|
||||
{
|
||||
var existingTrackPath = track.PathToTrack(Configuration, destinationFolder);
|
||||
|
||||
var existingTrackFileInfo = new FileInfo(existingTrackPath);
|
||||
var newTrackFileInfo = new FileInfo(track.PathToTrack(Configuration, destinationFolder));
|
||||
if (existingTrackFileInfo.Exists)
|
||||
{
|
||||
// Update the tracks release tags
|
||||
var audioMetaData = await AudioMetaDataHelper.GetInfo(existingTrackFileInfo);
|
||||
audioMetaData.Release = release.Title;
|
||||
AudioMetaDataHelper.WriteTags(audioMetaData, existingTrackFileInfo);
|
||||
|
||||
// Update track path
|
||||
track.FilePath = Path.Combine(releaseDirectoryInfo.Parent.Name, releaseDirectoryInfo.Name);
|
||||
track.LastUpdated = now;
|
||||
|
||||
// Move the physical track
|
||||
var newTrackPath = track.PathToTrack(Configuration, destinationFolder);
|
||||
if (!existingTrackPath.Equals(newTrackPath, StringComparison.OrdinalIgnoreCase))
|
||||
File.Move(existingTrackPath, newTrackPath);
|
||||
}
|
||||
|
||||
CacheManager.ClearRegion(track.CacheRegion);
|
||||
}
|
||||
|
||||
await DbContext.SaveChangesAsync();
|
||||
|
||||
// Clean up any empty folders for the artist
|
||||
FolderPathHelper.DeleteEmptyFoldersForArtist(Configuration, release.Artist, destinationFolder);
|
||||
}
|
||||
|
||||
sw.Stop();
|
||||
CacheManager.ClearRegion(release.CacheRegion);
|
||||
if (release.Artist != null) CacheManager.ClearRegion(release.Artist.CacheRegion);
|
||||
|
||||
return new OperationResult<bool>
|
||||
{
|
||||
IsSuccess = result,
|
||||
OperationTime = sw.ElapsedMilliseconds
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<OperationResult<bool>> Delete(Release release, bool doDeleteFiles = false,
|
||||
bool doUpdateArtistCounts = true)
|
||||
{
|
||||
SimpleContract.Requires<ArgumentNullException>(release != null, "Invalid Release");
|
||||
SimpleContract.Requires<ArgumentNullException>(release.Artist != null, "Invalid Artist");
|
||||
|
||||
var releaseCacheRegion = release.CacheRegion;
|
||||
var artistCacheRegion = release.Artist.CacheRegion;
|
||||
|
||||
var result = false;
|
||||
var sw = new Stopwatch();
|
||||
sw.Start();
|
||||
if (doDeleteFiles)
|
||||
{
|
||||
var releaseTracks = (from r in DbContext.Releases
|
||||
join rm in DbContext.ReleaseMedias on r.Id equals rm.ReleaseId
|
||||
join t in DbContext.Tracks on rm.Id equals t.ReleaseMediaId
|
||||
where r.Id == release.Id
|
||||
select t).ToArray();
|
||||
foreach (var track in releaseTracks)
|
||||
{
|
||||
string trackPath = null;
|
||||
try
|
||||
{
|
||||
trackPath = track.PathToTrack(Configuration, Configuration.LibraryFolder);
|
||||
if (File.Exists(trackPath))
|
||||
{
|
||||
File.Delete(trackPath);
|
||||
Logger.LogWarning("x For Release [{0}], Deleted File [{1}]", release.Id, trackPath);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex,
|
||||
string.Format("Error Deleting File [{0}] For Track [{1}] Exception [{2}]", trackPath,
|
||||
track.Id, ex.Serialize()));
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
FolderPathHelper.DeleteEmptyFoldersForArtist(Configuration, release.Artist);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
var releaseLabelIds = DbContext.ReleaseLabels.Where(x => x.ReleaseId == release.Id).Select(x => x.LabelId)
|
||||
.ToArray();
|
||||
DbContext.Releases.Remove(release);
|
||||
var i = await DbContext.SaveChangesAsync();
|
||||
result = true;
|
||||
try
|
||||
{
|
||||
CacheManager.ClearRegion(releaseCacheRegion);
|
||||
CacheManager.ClearRegion(artistCacheRegion);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex,
|
||||
string.Format("Error Clearing Cache For Release [{0}] Exception [{1}]", release.Id,
|
||||
ex.Serialize()));
|
||||
}
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
if (doUpdateArtistCounts) await UpdateArtistCounts(release.Artist.Id, now);
|
||||
if (releaseLabelIds != null && releaseLabelIds.Any())
|
||||
foreach (var releaseLabelId in releaseLabelIds)
|
||||
await UpdateLabelCounts(releaseLabelId, now);
|
||||
sw.Stop();
|
||||
return new OperationResult<bool>
|
||||
{
|
||||
Data = result,
|
||||
IsSuccess = result,
|
||||
OperationTime = sw.ElapsedMilliseconds
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<OperationResult<bool>> DeleteReleases(IEnumerable<Guid> releaseIds,
|
||||
bool doDeleteFiles = false)
|
||||
{
|
||||
SimpleContract.Requires<ArgumentNullException>(releaseIds != null && releaseIds.Any(),
|
||||
"No Release Ids Found");
|
||||
var result = false;
|
||||
var sw = new Stopwatch();
|
||||
sw.Start();
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
var releases = (from r in DbContext.Releases.Include(r => r.Artist)
|
||||
where releaseIds.Contains(r.RoadieId)
|
||||
select r
|
||||
).ToArray();
|
||||
|
||||
var artistIds = releases.Select(x => x.ArtistId).Distinct().ToArray();
|
||||
|
||||
foreach (var release in releases)
|
||||
{
|
||||
var defaultResult = await Delete(release, doDeleteFiles, false);
|
||||
result = result & defaultResult.IsSuccess;
|
||||
}
|
||||
|
||||
foreach (var artistId in artistIds) await UpdateArtistCounts(artistId, now);
|
||||
sw.Stop();
|
||||
|
||||
return new OperationResult<bool>
|
||||
{
|
||||
Data = result,
|
||||
IsSuccess = result,
|
||||
OperationTime = sw.ElapsedMilliseconds
|
||||
};
|
||||
}
|
||||
|
||||
public OperationResult<Release> GetAllForArtist(Artist artist, bool forceRefresh = false)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Merge one release into another one
|
||||
/// </summary>
|
||||
/// <param name="releaseToMerge">The release to be merged</param>
|
||||
/// <param name="releaseToMergeInto">The release to merge into</param>
|
||||
/// <param name="addAsMedia">If true then add a ReleaseMedia to the release to be merged into</param>
|
||||
/// <returns></returns>
|
||||
public async Task<OperationResult<bool>> MergeReleases(Release releaseToMerge, Release releaseToMergeInto,
|
||||
bool addAsMedia)
|
||||
{
|
||||
SimpleContract.Requires<ArgumentNullException>(releaseToMerge != null, "Invalid Release");
|
||||
SimpleContract.Requires<ArgumentNullException>(releaseToMergeInto != null, "Invalid Release");
|
||||
SimpleContract.Requires<ArgumentNullException>(releaseToMerge.Artist != null, "Invalid Artist");
|
||||
SimpleContract.Requires<ArgumentNullException>(releaseToMergeInto.Artist != null, "Invalid Artist");
|
||||
var result = false;
|
||||
var resultErrors = new List<Exception>();
|
||||
var sw = new Stopwatch();
|
||||
sw.Start();
|
||||
|
||||
try
|
||||
{
|
||||
var mergedFilesToDelete = new List<string>();
|
||||
var mergedTracksToMove = new List<Track>();
|
||||
|
||||
releaseToMergeInto.MediaCount = releaseToMergeInto.MediaCount ?? 0;
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
var releaseToMergeReleaseMedia =
|
||||
DbContext.ReleaseMedias.Where(x => x.ReleaseId == releaseToMerge.Id).ToList();
|
||||
var releaseToMergeIntoReleaseMedia =
|
||||
DbContext.ReleaseMedias.Where(x => x.ReleaseId == releaseToMergeInto.Id).ToList();
|
||||
var releaseToMergeIntoLastMediaNumber = releaseToMergeIntoReleaseMedia.Max(x => x.MediaNumber);
|
||||
|
||||
// Add new ReleaseMedia
|
||||
if (addAsMedia || !releaseToMergeIntoReleaseMedia.Any())
|
||||
foreach (var rm in releaseToMergeReleaseMedia)
|
||||
{
|
||||
releaseToMergeIntoLastMediaNumber++;
|
||||
rm.ReleaseId = releaseToMergeInto.Id;
|
||||
rm.MediaNumber = releaseToMergeIntoLastMediaNumber;
|
||||
rm.LastUpdated = now;
|
||||
releaseToMergeInto.MediaCount++;
|
||||
releaseToMergeInto.TrackCount += rm.TrackCount;
|
||||
}
|
||||
// Merge into existing ReleaseMedia
|
||||
else
|
||||
// See if each media exists and merge details of each including tracks
|
||||
foreach (var rm in releaseToMergeReleaseMedia)
|
||||
{
|
||||
var existingReleaseMedia =
|
||||
releaseToMergeIntoReleaseMedia.FirstOrDefault(x => x.MediaNumber == rm.MediaNumber);
|
||||
var mergeTracks = DbContext.Tracks.Where(x => x.ReleaseMediaId == rm.Id).ToArray();
|
||||
if (existingReleaseMedia == null)
|
||||
{
|
||||
releaseToMergeIntoLastMediaNumber++;
|
||||
// Doesnt exist in release being merged to add
|
||||
rm.ReleaseId = releaseToMergeInto.Id;
|
||||
rm.MediaNumber = releaseToMergeIntoLastMediaNumber;
|
||||
rm.LastUpdated = now;
|
||||
releaseToMergeInto.MediaCount++;
|
||||
releaseToMergeInto.TrackCount += rm.TrackCount;
|
||||
mergedTracksToMove.AddRange(mergeTracks);
|
||||
}
|
||||
else
|
||||
{
|
||||
// ReleaseMedia Does exist merge tracks and details
|
||||
|
||||
var mergeIntoTracks = DbContext.Tracks
|
||||
.Where(x => x.ReleaseMediaId == existingReleaseMedia.Id).ToArray();
|
||||
foreach (var mergeTrack in mergeTracks)
|
||||
{
|
||||
var existingTrack =
|
||||
mergeIntoTracks.FirstOrDefault(x => x.TrackNumber == mergeTrack.TrackNumber);
|
||||
if (existingTrack == null)
|
||||
{
|
||||
// Track does not exist, update to existing ReleaseMedia and update ReleaseToMergeInfo counts
|
||||
mergeTrack.LastUpdated = now;
|
||||
mergeTrack.ReleaseMediaId = existingReleaseMedia.Id;
|
||||
existingReleaseMedia.TrackCount++;
|
||||
existingReleaseMedia.LastUpdated = now;
|
||||
releaseToMergeInto.TrackCount++;
|
||||
mergedTracksToMove.Add(mergeTrack);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Track does exist merge two tracks together
|
||||
existingTrack.MusicBrainzId =
|
||||
existingTrack.MusicBrainzId ?? mergeTrack.MusicBrainzId;
|
||||
existingTrack.SpotifyId = existingTrack.SpotifyId ?? mergeTrack.SpotifyId;
|
||||
existingTrack.AmgId = existingTrack.AmgId ?? mergeTrack.AmgId;
|
||||
existingTrack.ISRC = existingTrack.ISRC ?? mergeTrack.ISRC;
|
||||
existingTrack.AmgId = existingTrack.AmgId ?? mergeTrack.AmgId;
|
||||
existingTrack.LastFMId = existingTrack.LastFMId ?? mergeTrack.LastFMId;
|
||||
existingTrack.PartTitles = existingTrack.PartTitles ?? mergeTrack.PartTitles;
|
||||
existingTrack.PlayedCount =
|
||||
(existingTrack.PlayedCount ?? 0) + (mergeTrack.PlayedCount ?? 0);
|
||||
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,
|
||||
StringComparison.OrdinalIgnoreCase))
|
||||
existingTrack.AlternateNames =
|
||||
existingTrack.AlternateNames.AddToDelimitedList(new[]
|
||||
{mergeTrack.Title, mergeTrack.Title.ToAlphanumericName()});
|
||||
existingTrack.AlternateNames =
|
||||
existingTrack.AlternateNames.AddToDelimitedList(mergeTrack.AlternateNames
|
||||
.ToListFromDelimited());
|
||||
existingTrack.LastUpdated = now;
|
||||
var mergedTrackFileName =
|
||||
mergeTrack.PathToTrack(Configuration, Configuration.LibraryFolder);
|
||||
var trackFileName =
|
||||
existingTrack.PathToTrack(Configuration, Configuration.LibraryFolder);
|
||||
if (!trackFileName.Equals(mergedTrackFileName, StringComparison.Ordinal) &&
|
||||
File.Exists(trackFileName)) mergedFilesToDelete.Add(mergedTrackFileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var destinationRoot = Configuration.LibraryFolder;
|
||||
var releaseToMergeFolder =
|
||||
releaseToMerge.ReleaseFileFolder(
|
||||
releaseToMerge.Artist.ArtistFileFolder(Configuration, destinationRoot));
|
||||
var releaseToMergeIntoArtistFolder =
|
||||
releaseToMergeInto.Artist.ArtistFileFolder(Configuration, destinationRoot);
|
||||
var releaseToMergeIntoDirectory =
|
||||
new DirectoryInfo(releaseToMergeInto.ReleaseFileFolder(releaseToMergeIntoArtistFolder));
|
||||
|
||||
// Move tracks for releaseToMergeInto into correct folders
|
||||
if (mergedTracksToMove.Any())
|
||||
foreach (var track in mergedTracksToMove)
|
||||
{
|
||||
var oldTrackPath = track.PathToTrack(Configuration, Configuration.LibraryFolder);
|
||||
var newTrackPath = FolderPathHelper.TrackFullPath(Configuration, releaseToMerge.Artist,
|
||||
releaseToMerge, track);
|
||||
var trackFile = new FileInfo(oldTrackPath);
|
||||
if (!newTrackPath.ToLower().Equals(oldTrackPath.ToLower()))
|
||||
{
|
||||
var audioMetaData = await AudioMetaDataHelper.GetInfo(trackFile);
|
||||
track.FilePath = FolderPathHelper.TrackPath(Configuration, releaseToMergeInto.Artist,
|
||||
releaseToMergeInto, track);
|
||||
track.Hash = HashHelper.CreateMD5(
|
||||
releaseToMergeInto.ArtistId + trackFile.LastWriteTimeUtc.GetHashCode().ToString() +
|
||||
audioMetaData.GetHashCode());
|
||||
track.LastUpdated = now;
|
||||
File.Move(oldTrackPath, newTrackPath);
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup folders
|
||||
FolderProcessor.DeleteEmptyFolders(new DirectoryInfo(releaseToMergeIntoArtistFolder), Logger);
|
||||
|
||||
// Now Merge release details
|
||||
releaseToMergeInto.AlternateNames = releaseToMergeInto.AlternateNames.AddToDelimitedList(new[]
|
||||
{releaseToMerge.Title, releaseToMerge.Title.ToAlphanumericName()});
|
||||
releaseToMergeInto.AlternateNames =
|
||||
releaseToMergeInto.AlternateNames.AddToDelimitedList(releaseToMerge.AlternateNames
|
||||
.ToListFromDelimited());
|
||||
releaseToMergeInto.Tags =
|
||||
releaseToMergeInto.Tags.AddToDelimitedList(releaseToMerge.Tags.ToListFromDelimited());
|
||||
releaseToMergeInto.URLs.AddToDelimitedList(releaseToMerge.URLs.ToListFromDelimited());
|
||||
releaseToMergeInto.MusicBrainzId = releaseToMergeInto.MusicBrainzId ?? releaseToMerge.MusicBrainzId;
|
||||
releaseToMergeInto.Profile = releaseToMergeInto.Profile ?? releaseToMerge.Profile;
|
||||
releaseToMergeInto.ReleaseDate = releaseToMergeInto.ReleaseDate ?? releaseToMerge.ReleaseDate;
|
||||
releaseToMergeInto.MusicBrainzId = releaseToMergeInto.MusicBrainzId ?? releaseToMerge.MusicBrainzId;
|
||||
releaseToMergeInto.DiscogsId = releaseToMergeInto.DiscogsId ?? releaseToMerge.DiscogsId;
|
||||
releaseToMergeInto.ITunesId = releaseToMergeInto.ITunesId ?? releaseToMerge.ITunesId;
|
||||
releaseToMergeInto.AmgId = releaseToMergeInto.AmgId ?? releaseToMerge.AmgId;
|
||||
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;
|
||||
releaseToMergeInto.LastUpdated = now;
|
||||
await DbContext.SaveChangesAsync();
|
||||
|
||||
// Update any collection pointers for release to be merged
|
||||
var collectionRecords = DbContext.CollectionReleases.Where(x => x.ReleaseId == releaseToMerge.Id);
|
||||
if (collectionRecords != null && collectionRecords.Any())
|
||||
{
|
||||
foreach (var cr in collectionRecords)
|
||||
{
|
||||
cr.ReleaseId = releaseToMergeInto.Id;
|
||||
cr.LastUpdated = now;
|
||||
}
|
||||
|
||||
await DbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
// Update any existing playlist for release to be merged
|
||||
var playListTrackInfos = (from pl in DbContext.PlaylistTracks
|
||||
join t in DbContext.Tracks on pl.TrackId equals t.Id
|
||||
join rm in DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id
|
||||
where rm.ReleaseId == releaseToMerge.Id
|
||||
select new
|
||||
{
|
||||
track = t,
|
||||
rm,
|
||||
pl
|
||||
}).ToArray();
|
||||
if (playListTrackInfos != null && playListTrackInfos.Any())
|
||||
{
|
||||
foreach (var playListTrackInfo in playListTrackInfos)
|
||||
{
|
||||
var matchingTrack = (from t in DbContext.Tracks
|
||||
join rm in DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id
|
||||
where rm.ReleaseId == releaseToMergeInto.Id
|
||||
where rm.MediaNumber == playListTrackInfo.rm.MediaNumber
|
||||
where t.TrackNumber == playListTrackInfo.track.TrackNumber
|
||||
select t).FirstOrDefault();
|
||||
if (matchingTrack != null)
|
||||
{
|
||||
playListTrackInfo.pl.TrackId = matchingTrack.Id;
|
||||
playListTrackInfo.pl.LastUpdated = now;
|
||||
}
|
||||
}
|
||||
|
||||
await DbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
await Delete(releaseToMerge);
|
||||
|
||||
// Delete any files flagged to be deleted (duplicate as track already exists on merged to release)
|
||||
if (mergedFilesToDelete.Any())
|
||||
foreach (var mergedFileToDelete in mergedFilesToDelete)
|
||||
try
|
||||
{
|
||||
if (File.Exists(mergedFileToDelete))
|
||||
{
|
||||
File.Delete(mergedFileToDelete);
|
||||
Logger.LogWarning("x Deleted Merged File [{0}]", mergedFileToDelete);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
// Clear cache regions for manipulated records
|
||||
CacheManager.ClearRegion(releaseToMergeInto.CacheRegion);
|
||||
if (releaseToMergeInto.Artist != null) CacheManager.ClearRegion(releaseToMergeInto.Artist.CacheRegion);
|
||||
if (releaseToMerge.Artist != null) CacheManager.ClearRegion(releaseToMerge.Artist.CacheRegion);
|
||||
|
||||
sw.Stop();
|
||||
result = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex,
|
||||
$"MergeReleases ReleaseToMerge `{releaseToMerge}`, ReleaseToMergeInto `{releaseToMergeInto}`, addAsMedia [{addAsMedia}]");
|
||||
resultErrors.Add(ex);
|
||||
}
|
||||
|
||||
return new OperationResult<bool>
|
||||
{
|
||||
Data = result,
|
||||
IsSuccess = result,
|
||||
Errors = resultErrors,
|
||||
OperationTime = sw.ElapsedMilliseconds
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For the given ReleaseId, Scan folder adding new, removing not found and updating DB tracks for tracks found
|
||||
/// </summary>
|
||||
public async Task<OperationResult<bool>> ScanReleaseFolder(Guid releaseId, string destinationFolder,
|
||||
bool doJustInfo, Release releaseToScan = null)
|
||||
{
|
||||
SimpleContract.Requires<ArgumentOutOfRangeException>(
|
||||
releaseId != Guid.Empty && releaseToScan == null || releaseToScan != null, "Invalid ReleaseId");
|
||||
|
||||
_addedTrackIds.Clear();
|
||||
|
||||
var result = false;
|
||||
var resultErrors = new List<Exception>();
|
||||
var sw = new Stopwatch();
|
||||
sw.Start();
|
||||
var modifiedRelease = false;
|
||||
string releasePath = null;
|
||||
try
|
||||
{
|
||||
var release = releaseToScan ?? DbContext.Releases
|
||||
.Include(x => x.Artist)
|
||||
.Include(x => x.Labels)
|
||||
.FirstOrDefault(x => x.RoadieId == releaseId);
|
||||
if (release == null)
|
||||
{
|
||||
Logger.LogCritical("Unable To Find Release [{0}]", releaseId);
|
||||
return new OperationResult<bool>();
|
||||
}
|
||||
|
||||
// This is recorded from metadata and if set then used to gauage if the release is complete
|
||||
short? totalTrackCount = null;
|
||||
short totalMissingCount = 0;
|
||||
releasePath =
|
||||
release.ReleaseFileFolder(release.Artist.ArtistFileFolder(Configuration, destinationFolder));
|
||||
var releaseDirectory = new DirectoryInfo(releasePath);
|
||||
if (!Directory.Exists(releasePath))
|
||||
Logger.LogWarning("Unable To Find Release Folder [{0}] For Release `{1}`", releasePath,
|
||||
release.ToString());
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
#region Get Tracks for Release from DB and set as missing any not found in Folder
|
||||
|
||||
foreach (var releaseMedia in DbContext.ReleaseMedias.Where(x => x.ReleaseId == release.Id).ToArray())
|
||||
{
|
||||
var foundMissingTracks = false;
|
||||
foreach (var existingTrack in DbContext.Tracks.Where(x => x.ReleaseMediaId == releaseMedia.Id)
|
||||
.ToArray())
|
||||
{
|
||||
var trackPath = existingTrack.PathToTrack(Configuration, destinationFolder);
|
||||
|
||||
if (!File.Exists(trackPath))
|
||||
{
|
||||
Logger.LogWarning("Track `{0}`, File [{1}] Not Found.", existingTrack.ToString(),
|
||||
trackPath);
|
||||
if (!doJustInfo)
|
||||
{
|
||||
existingTrack.UpdateTrackMissingFile(now);
|
||||
foundMissingTracks = true;
|
||||
modifiedRelease = true;
|
||||
totalMissingCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (foundMissingTracks) await DbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
#endregion Get Tracks for Release from DB and set as missing any not found in Folder
|
||||
|
||||
#region Scan Folder and Add or Update Existing Tracks from Files
|
||||
|
||||
var existingReleaseMedia = DbContext.ReleaseMedias.Include(x => x.Tracks)
|
||||
.Where(x => x.ReleaseId == release.Id).ToList();
|
||||
var foundInFolderTracks = new List<Track>();
|
||||
short totalNumberOfTracksFound = 0;
|
||||
// This is the number of tracks metadata says the release should have (releaseMediaNumber, TotalNumberOfTracks)
|
||||
var releaseMediaTotalNumberOfTracks = new Dictionary<short, short?>();
|
||||
var releaseMediaTracksFound = new Dictionary<int, short>();
|
||||
if (Directory.Exists(releasePath))
|
||||
foreach (var file in releaseDirectory.GetFiles("*.mp3", SearchOption.AllDirectories))
|
||||
{
|
||||
int? trackArtistId = null;
|
||||
string partTitles = null;
|
||||
var audioMetaData = await AudioMetaDataHelper.GetInfo(file, doJustInfo);
|
||||
// This is the path for the new track not in the database but the found MP3 file to be added to library
|
||||
var trackPath = Path.Combine(releaseDirectory.Parent.Name, releaseDirectory.Name);
|
||||
|
||||
if (audioMetaData.IsValid)
|
||||
{
|
||||
var trackHash = HashHelper.CreateMD5(
|
||||
release.ArtistId + file.LastWriteTimeUtc.GetHashCode().ToString() +
|
||||
audioMetaData.GetHashCode());
|
||||
totalNumberOfTracksFound++;
|
||||
totalTrackCount = totalTrackCount ?? (short)(audioMetaData.TotalTrackNumbers ?? 0);
|
||||
var releaseMediaNumber = (short)(audioMetaData.Disc ?? 1);
|
||||
if (!releaseMediaTotalNumberOfTracks.ContainsKey(releaseMediaNumber))
|
||||
releaseMediaTotalNumberOfTracks.Add(releaseMediaNumber,
|
||||
(short)(audioMetaData.TotalTrackNumbers ?? 0));
|
||||
else
|
||||
releaseMediaTotalNumberOfTracks[releaseMediaNumber] =
|
||||
releaseMediaTotalNumberOfTracks[releaseMediaNumber]
|
||||
.TakeLarger((short)(audioMetaData.TotalTrackNumbers ?? 0));
|
||||
var releaseMedia =
|
||||
existingReleaseMedia.FirstOrDefault(x => x.MediaNumber == releaseMediaNumber);
|
||||
if (releaseMedia == null)
|
||||
{
|
||||
// New ReleaseMedia - Not Found In Database
|
||||
releaseMedia = new ReleaseMedia
|
||||
{
|
||||
ReleaseId = release.Id,
|
||||
Status = Statuses.Incomplete,
|
||||
MediaNumber = releaseMediaNumber
|
||||
};
|
||||
DbContext.ReleaseMedias.Add(releaseMedia);
|
||||
await DbContext.SaveChangesAsync();
|
||||
existingReleaseMedia.Add(releaseMedia);
|
||||
modifiedRelease = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Existing ReleaseMedia Found
|
||||
releaseMedia.LastUpdated = now;
|
||||
}
|
||||
|
||||
var track = releaseMedia.Tracks.FirstOrDefault(x =>
|
||||
x.TrackNumber == audioMetaData.TrackNumber);
|
||||
if (track == null)
|
||||
{
|
||||
// New Track - Not Found In Database
|
||||
track = new Track
|
||||
{
|
||||
Status = Statuses.New,
|
||||
FilePath = trackPath,
|
||||
FileName = file.Name,
|
||||
FileSize = (int)file.Length,
|
||||
Hash = trackHash,
|
||||
MusicBrainzId = audioMetaData.MusicBrainzId,
|
||||
AmgId = audioMetaData.AmgId,
|
||||
SpotifyId = audioMetaData.SpotifyId,
|
||||
Title = audioMetaData.Title,
|
||||
TrackNumber = audioMetaData.TrackNumber ?? totalNumberOfTracksFound,
|
||||
Duration = audioMetaData.Time != null
|
||||
? (int)audioMetaData.Time.Value.TotalMilliseconds
|
||||
: 0,
|
||||
ReleaseMediaId = releaseMedia.Id,
|
||||
ISRC = audioMetaData.ISRC,
|
||||
LastFMId = audioMetaData.LastFmId
|
||||
};
|
||||
|
||||
if (audioMetaData.TrackArtist != null)
|
||||
{
|
||||
if (audioMetaData.TrackArtists.Count() == 1)
|
||||
{
|
||||
var trackArtistData =
|
||||
await ArtistLookupEngine.GetByName(
|
||||
new AudioMetaData { Artist = audioMetaData.TrackArtist }, true);
|
||||
if (trackArtistData.IsSuccess && release.ArtistId != trackArtistData.Data.Id)
|
||||
trackArtistId = trackArtistData.Data.Id;
|
||||
}
|
||||
else if (audioMetaData.TrackArtists.Any())
|
||||
{
|
||||
partTitles = string.Join(AudioMetaData.ArtistSplitCharacter.ToString(),
|
||||
audioMetaData.TrackArtists);
|
||||
}
|
||||
else
|
||||
{
|
||||
partTitles = audioMetaData.TrackArtist;
|
||||
}
|
||||
}
|
||||
|
||||
var alt = track.Title.ToAlphanumericName();
|
||||
track.AlternateNames =
|
||||
!alt.Equals(audioMetaData.Title, StringComparison.OrdinalIgnoreCase)
|
||||
? track.AlternateNames.AddToDelimitedList(new[] { alt })
|
||||
: null;
|
||||
track.ArtistId = trackArtistId;
|
||||
track.PartTitles = partTitles;
|
||||
DbContext.Tracks.Add(track);
|
||||
await DbContext.SaveChangesAsync();
|
||||
_addedTrackIds.Add(track.Id);
|
||||
modifiedRelease = true;
|
||||
}
|
||||
else if (string.IsNullOrEmpty(track.Hash) || trackHash != track.Hash)
|
||||
{
|
||||
if (audioMetaData.TrackArtist != null)
|
||||
{
|
||||
if (audioMetaData.TrackArtists.Count() == 1)
|
||||
{
|
||||
var trackArtistData =
|
||||
await ArtistLookupEngine.GetByName(
|
||||
new AudioMetaData { Artist = audioMetaData.TrackArtist }, true);
|
||||
if (trackArtistData.IsSuccess && release.ArtistId != trackArtistData.Data.Id)
|
||||
trackArtistId = trackArtistData.Data.Id;
|
||||
}
|
||||
else if (audioMetaData.TrackArtists.Any())
|
||||
{
|
||||
partTitles = string.Join(AudioMetaData.ArtistSplitCharacter.ToString(),
|
||||
audioMetaData.TrackArtists);
|
||||
}
|
||||
else
|
||||
{
|
||||
partTitles = audioMetaData.TrackArtist;
|
||||
}
|
||||
}
|
||||
|
||||
track.Title = audioMetaData.Title;
|
||||
track.Duration = audioMetaData.Time != null
|
||||
? (int)audioMetaData.Time.Value.TotalMilliseconds
|
||||
: 0;
|
||||
track.TrackNumber = audioMetaData.TrackNumber ?? totalNumberOfTracksFound;
|
||||
track.ArtistId = trackArtistId;
|
||||
track.PartTitles = partTitles;
|
||||
track.Hash = trackHash;
|
||||
track.FileName = file.Name;
|
||||
track.FileSize = (int)file.Length;
|
||||
track.FilePath = trackPath;
|
||||
track.Status = Statuses.Ok;
|
||||
track.LastUpdated = now;
|
||||
var alt = track.Title.ToAlphanumericName();
|
||||
if (!alt.Equals(track.Title, StringComparison.OrdinalIgnoreCase))
|
||||
track.AlternateNames = track.AlternateNames.AddToDelimitedList(new[] { alt });
|
||||
track.TrackNumber = audioMetaData.TrackNumber ?? -1;
|
||||
track.LastUpdated = now;
|
||||
modifiedRelease = true;
|
||||
}
|
||||
else if (track.Status != Statuses.Ok)
|
||||
{
|
||||
track.Status = Statuses.Ok;
|
||||
track.LastUpdated = now;
|
||||
modifiedRelease = true;
|
||||
}
|
||||
|
||||
foundInFolderTracks.Add(track);
|
||||
if (releaseMediaTracksFound.ContainsKey(releaseMedia.Id))
|
||||
releaseMediaTracksFound[releaseMedia.Id]++;
|
||||
else
|
||||
releaseMediaTracksFound[releaseMedia.Id] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogWarning("Release Track File Has Invalid MetaData `{0}`",
|
||||
audioMetaData.ToString());
|
||||
}
|
||||
}
|
||||
else
|
||||
Logger.LogWarning("Unable To Find Releaes Path [{0}] For Release `{1}`", releasePath,
|
||||
release.ToString());
|
||||
|
||||
var releaseMediaNumbersFound = new List<short?>();
|
||||
foreach (var kp in releaseMediaTracksFound)
|
||||
{
|
||||
var releaseMedia = DbContext.ReleaseMedias.FirstOrDefault(x => x.Id == kp.Key);
|
||||
if (releaseMedia != null)
|
||||
{
|
||||
if (!releaseMediaNumbersFound.Any(x => x == releaseMedia.MediaNumber))
|
||||
releaseMediaNumbersFound.Add(releaseMedia.MediaNumber);
|
||||
var releaseMediaFoundInFolderTrackNumbers = foundInFolderTracks
|
||||
.Where(x => x.ReleaseMediaId == releaseMedia.Id).Select(x => x.TrackNumber).OrderBy(x => x)
|
||||
.ToArray();
|
||||
var areTracksForRelaseMediaSequential = releaseMediaFoundInFolderTrackNumbers
|
||||
.Zip(releaseMediaFoundInFolderTrackNumbers.Skip(1), (a, b) => a + 1 == b).All(x => x);
|
||||
if (!areTracksForRelaseMediaSequential)
|
||||
Logger.LogDebug("ReleaseMedia [{0}] Track Numbers Are Not Sequential", releaseMedia.Id);
|
||||
releaseMedia.TrackCount = kp.Value;
|
||||
releaseMedia.LastUpdated = now;
|
||||
releaseMedia.Status = areTracksForRelaseMediaSequential ? Statuses.Ok : Statuses.Incomplete;
|
||||
await DbContext.SaveChangesAsync();
|
||||
modifiedRelease = true;
|
||||
}
|
||||
|
||||
;
|
||||
}
|
||||
|
||||
var foundInFolderTrackNumbers =
|
||||
foundInFolderTracks.Select(x => x.TrackNumber).OrderBy(x => x).ToArray();
|
||||
if (modifiedRelease || !foundInFolderTrackNumbers.Count().Equals(release.TrackCount) ||
|
||||
releaseMediaNumbersFound.Count() != (release.MediaCount ?? 0))
|
||||
{
|
||||
var areTracksForRelaseSequential = foundInFolderTrackNumbers
|
||||
.Zip(foundInFolderTrackNumbers.Skip(1), (a, b) => a + 1 == b).All(x => x);
|
||||
var maxFoundInFolderTrackNumbers =
|
||||
foundInFolderTrackNumbers.Any() ? foundInFolderTrackNumbers.Max() : (short)0;
|
||||
release.Status = areTracksForRelaseSequential ? Statuses.Ok : Statuses.Incomplete;
|
||||
release.TrackCount = (short)foundInFolderTrackNumbers.Count();
|
||||
release.MediaCount = (short)releaseMediaNumbersFound.Count();
|
||||
if (release.TrackCount < maxFoundInFolderTrackNumbers)
|
||||
release.TrackCount = maxFoundInFolderTrackNumbers;
|
||||
release.LibraryStatus = release.TrackCount > 0 && release.TrackCount == totalNumberOfTracksFound
|
||||
? LibraryStatus.Complete
|
||||
: LibraryStatus.Incomplete;
|
||||
release.LastUpdated = now;
|
||||
release.Status = release.LibraryStatus == LibraryStatus.Complete
|
||||
? Statuses.Complete
|
||||
: Statuses.Incomplete;
|
||||
|
||||
await DbContext.SaveChangesAsync();
|
||||
CacheManager.ClearRegion(release.Artist.CacheRegion);
|
||||
CacheManager.ClearRegion(release.CacheRegion);
|
||||
}
|
||||
|
||||
#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 = File.ReadAllBytes(i.FullName);
|
||||
release.Thumbnail = ImageHelper.ResizeImage(release.Thumbnail,
|
||||
Configuration.MediumImageSize.Width, Configuration.MediumImageSize.Height);
|
||||
release.Thumbnail = ImageHelper.ConvertToJpegFormat(release.Thumbnail);
|
||||
if (release.Thumbnail.Length >= ImageHelper.MaximumThumbnailByteSize)
|
||||
{
|
||||
Logger.LogWarning(
|
||||
$"Release Thumbnail larger than maximum size after resizing to [{Configuration.ThumbnailImageSize.Width}x{Configuration.ThumbnailImageSize.Height}] Thumbnail Size [{release.Thumbnail.Length}]");
|
||||
release.Thumbnail = null;
|
||||
}
|
||||
|
||||
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);
|
||||
if (release.Labels != null && release.Labels.Any())
|
||||
foreach (var label in release.Labels)
|
||||
await UpdateLabelCounts(label.Id, now);
|
||||
|
||||
Logger.LogInformation("Scanned Release `{0}` Folder [{1}], Modified Release [{2}], OperationTime [{3}]",
|
||||
release.ToString(), releasePath, modifiedRelease, sw.ElapsedMilliseconds);
|
||||
result = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex, "ReleasePath [" + releasePath + "] " + ex.Serialize());
|
||||
resultErrors.Add(ex);
|
||||
}
|
||||
|
||||
return new OperationResult<bool>
|
||||
{
|
||||
Data = result,
|
||||
IsSuccess = result,
|
||||
Errors = resultErrors,
|
||||
OperationTime = sw.ElapsedMilliseconds
|
||||
};
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public async Task<OperationResult<Release>> Update(Release release, IEnumerable<Image> releaseImages,
|
||||
string originalReleaseFolder, string destinationFolder = null)
|
||||
{
|
||||
SimpleContract.Requires<ArgumentNullException>(release != null, "Invalid Release");
|
||||
|
||||
var sw = new Stopwatch();
|
||||
sw.Start();
|
||||
|
||||
var artistFolder =
|
||||
release.Artist.ArtistFileFolder(Configuration, destinationFolder ?? Configuration.LibraryFolder);
|
||||
var releaseGenreTables = release.Genres
|
||||
.Select(x => new ReleaseGenre { ReleaseId = release.Id, GenreId = x.GenreId }).ToList();
|
||||
var releaseLabels = release.Labels.Select(x => new ReleaseLabel
|
||||
{
|
||||
CatalogNumber = x.CatalogNumber,
|
||||
BeginDate = x.BeginDate,
|
||||
EndDate = x.EndDate,
|
||||
ReleaseId = release.Id,
|
||||
LabelId = x.Label != null && x.Label.Id > 0 ? x.Label.Id : x.LabelId,
|
||||
Status = x.Status,
|
||||
RoadieId = x.RoadieId
|
||||
}).ToList();
|
||||
var result = true;
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
release.LastUpdated = now;
|
||||
release.Labels = null;
|
||||
|
||||
await CheckAndChangeReleaseTitle(release, originalReleaseFolder);
|
||||
|
||||
await DbContext.SaveChangesAsync();
|
||||
|
||||
DbContext.ReleaseGenres.RemoveRange(from at in DbContext.ReleaseGenres
|
||||
where at.ReleaseId == release.Id
|
||||
select at);
|
||||
release.Genres = releaseGenreTables;
|
||||
|
||||
var existingReleaseLabelIds = (from rl in releaseLabels
|
||||
where rl.Status != Statuses.New
|
||||
select rl.RoadieId).ToArray();
|
||||
DbContext.ReleaseLabels.RemoveRange(from rl in DbContext.ReleaseLabels
|
||||
where rl.ReleaseId == release.Id
|
||||
where !(from x in existingReleaseLabelIds select x).Contains(rl.RoadieId)
|
||||
select rl);
|
||||
release.Labels = releaseLabels;
|
||||
await DbContext.SaveChangesAsync();
|
||||
|
||||
if (releaseImages != null)
|
||||
{
|
||||
var existingImageIds = (from ai in releaseImages
|
||||
where ai.Status != Statuses.New
|
||||
select ai.RoadieId).ToArray();
|
||||
DbContext.Images.RemoveRange(from i in DbContext.Images
|
||||
where i.ReleaseId == release.Id
|
||||
where !(from x in existingImageIds select x).Contains(i.RoadieId)
|
||||
select i);
|
||||
await DbContext.SaveChangesAsync();
|
||||
if (releaseImages.Any(x => x.Status == Statuses.New))
|
||||
{
|
||||
foreach (var releaseImage in releaseImages.Where(x => x.Status == Statuses.New))
|
||||
DbContext.Images.Add(releaseImage);
|
||||
try
|
||||
{
|
||||
await DbContext.SaveChangesAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CacheManager.ClearRegion(release.CacheRegion);
|
||||
if (release.Artist != null) CacheManager.ClearRegion(release.Artist.CacheRegion);
|
||||
sw.Stop();
|
||||
|
||||
return new OperationResult<Release>
|
||||
{
|
||||
Data = release,
|
||||
IsSuccess = result,
|
||||
OperationTime = sw.ElapsedMilliseconds
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,7 +6,6 @@ using Roadie.Library.Encoding;
|
|||
using Roadie.Library.Engines;
|
||||
using Roadie.Library.Enums;
|
||||
using Roadie.Library.Extensions;
|
||||
using Roadie.Library.Factories;
|
||||
using Roadie.Library.Imaging;
|
||||
using Roadie.Library.MetaData.Audio;
|
||||
using Roadie.Library.Utility;
|
||||
|
@ -28,35 +27,22 @@ namespace Roadie.Library.FilePlugins
|
|||
|
||||
public override string[] HandlesTypes => new string[1] { "audio/mpeg" };
|
||||
|
||||
public Audio(IRoadieSettings configuration,
|
||||
IHttpEncoder httpEncoder,
|
||||
IArtistFactory artistFactory,
|
||||
IReleaseFactory releaseFactory,
|
||||
IImageFactory imageFactory,
|
||||
ICacheManager cacheManager,
|
||||
ILogger logger,
|
||||
IArtistLookupEngine artistLookupEngine,
|
||||
IReleaseLookupEngine releaseLookupEngine,
|
||||
IAudioMetaDataHelper audioMetaDataHelper)
|
||||
: base(configuration, httpEncoder, artistFactory, releaseFactory, imageFactory, cacheManager, logger,
|
||||
artistLookupEngine, releaseLookupEngine)
|
||||
public Audio(IRoadieSettings configuration, IHttpEncoder httpEncoder, ICacheManager cacheManager,
|
||||
ILogger logger, IArtistLookupEngine artistLookupEngine, IReleaseLookupEngine releaseLookupEngine,
|
||||
IAudioMetaDataHelper audioMetaDataHelper)
|
||||
: base(configuration, httpEncoder, cacheManager, logger, artistLookupEngine, releaseLookupEngine)
|
||||
{
|
||||
AudioMetaDataHelper = audioMetaDataHelper;
|
||||
}
|
||||
|
||||
public override async Task<OperationResult<bool>> Process(string destinationRoot, FileInfo fileInfo,
|
||||
bool doJustInfo, int? submissionId)
|
||||
public override async Task<OperationResult<bool>> Process(FileInfo fileInfo, bool doJustInfo, int? submissionId)
|
||||
{
|
||||
Logger.LogTrace(
|
||||
">> Audio: Process destinationRoot [{0}], FileInfo [{1}], doJustInfo [{2}], submissionId [{3}]",
|
||||
destinationRoot, fileInfo, doJustInfo, submissionId);
|
||||
Logger.LogTrace($">> Audio: Process FileInfo [{fileInfo}], doJustInfo [{doJustInfo}], submissionId [{submissionId}]", fileInfo, doJustInfo, submissionId);
|
||||
var sw = Stopwatch.StartNew();
|
||||
var result = new OperationResult<bool>();
|
||||
|
||||
try
|
||||
{
|
||||
var dr = destinationRoot ?? fileInfo.DirectoryName;
|
||||
|
||||
string destinationName = null;
|
||||
|
||||
var metaData = await AudioMetaDataHelper.GetInfo(fileInfo, doJustInfo);
|
||||
|
@ -89,7 +75,7 @@ namespace Roadie.Library.FilePlugins
|
|||
SimpleContract.Requires<ArgumentException>(year > 0, string.Format("Invalid Track Year [{0}]", year));
|
||||
SimpleContract.Requires<ArgumentException>(trackNumber > 0, "Missing Track Number");
|
||||
|
||||
var artistFolder = await DetermineArtistFolder(dr, metaData, doJustInfo);
|
||||
var artistFolder = await DetermineArtistFolder(metaData, doJustInfo);
|
||||
if (string.IsNullOrEmpty(artistFolder))
|
||||
{
|
||||
Logger.LogWarning("Unable To Find ArtistFolder [{0}] For MetaData [{1}]", artistFolder,
|
||||
|
@ -104,8 +90,7 @@ namespace Roadie.Library.FilePlugins
|
|||
return new OperationResult<bool>("Unable To Find Release Folder");
|
||||
}
|
||||
|
||||
destinationName =
|
||||
FolderPathHelper.TrackFullPath(Configuration, metaData, dr, artistFolder, releaseFolder);
|
||||
destinationName = FolderPathHelper.TrackFullPath(Configuration, metaData, Configuration.LibraryFolder, artistFolder, releaseFolder);
|
||||
Logger.LogTrace(
|
||||
"Info: FileInfo [{0}], Artist Folder [{1}], Release Folder [{1}], Destination Name [{3}]",
|
||||
fileInfo.FullName, artistFolder, releaseFolder, destinationName);
|
||||
|
@ -318,14 +303,14 @@ namespace Roadie.Library.FilePlugins
|
|||
return result;
|
||||
}
|
||||
|
||||
private async Task<string> DetermineArtistFolder(string destinationRoot, AudioMetaData metaData,
|
||||
private async Task<string> DetermineArtistFolder(AudioMetaData metaData,
|
||||
bool doJustInfo)
|
||||
{
|
||||
var artist = await ArtistLookupEngine.GetByName(metaData, !doJustInfo);
|
||||
if (!artist.IsSuccess) return null;
|
||||
try
|
||||
{
|
||||
return artist.Data.ArtistFileFolder(Configuration, destinationRoot);
|
||||
return artist.Data.ArtistFileFolder(Configuration);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
|
@ -7,6 +7,6 @@ namespace Roadie.Library.FilePlugins
|
|||
{
|
||||
string[] HandlesTypes { get; }
|
||||
|
||||
Task<OperationResult<bool>> Process(string destinationRoot, FileInfo file, bool doJustInfo, int? submissionId);
|
||||
Task<OperationResult<bool>> Process(FileInfo file, bool doJustInfo, int? submissionId);
|
||||
}
|
||||
}
|
|
@ -3,7 +3,6 @@ using Roadie.Library.Caching;
|
|||
using Roadie.Library.Configuration;
|
||||
using Roadie.Library.Encoding;
|
||||
using Roadie.Library.Engines;
|
||||
using Roadie.Library.Factories;
|
||||
using Roadie.Library.Utility;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
@ -17,8 +16,6 @@ namespace Roadie.Library.FilePlugins
|
|||
|
||||
public int MinWeightToDelete => Configuration.FilePlugins.MinWeightToDelete;
|
||||
|
||||
protected IArtistFactory ArtistFactory { get; }
|
||||
|
||||
protected IArtistLookupEngine ArtistLookupEngine { get; }
|
||||
|
||||
protected ICacheManager CacheManager { get; }
|
||||
|
@ -27,23 +24,16 @@ namespace Roadie.Library.FilePlugins
|
|||
|
||||
protected IHttpEncoder HttpEncoder { get; }
|
||||
|
||||
protected IImageFactory ImageFactory { get; }
|
||||
|
||||
protected ILogger Logger { get; }
|
||||
|
||||
protected IReleaseFactory ReleaseFactory { get; }
|
||||
|
||||
protected IReleaseLookupEngine ReleaseLookupEngine { get; }
|
||||
|
||||
public PluginBase(IRoadieSettings configuration, IHttpEncoder httpEncoder, IArtistFactory artistFactory,
|
||||
IReleaseFactory releaseFactory, IImageFactory imageFactory, ICacheManager cacheManager, ILogger logger,
|
||||
public PluginBase(IRoadieSettings configuration, IHttpEncoder httpEncoder, ICacheManager cacheManager, ILogger logger,
|
||||
IArtistLookupEngine artistLookupEngine, IReleaseLookupEngine releaseLookupEngine)
|
||||
{
|
||||
Configuration = configuration;
|
||||
HttpEncoder = httpEncoder;
|
||||
ArtistFactory = artistFactory;
|
||||
ReleaseFactory = releaseFactory;
|
||||
ImageFactory = imageFactory;
|
||||
CacheManager = cacheManager;
|
||||
Logger = logger;
|
||||
ArtistLookupEngine = artistLookupEngine;
|
||||
|
@ -68,8 +58,7 @@ namespace Roadie.Library.FilePlugins
|
|||
return false;
|
||||
}
|
||||
|
||||
public abstract Task<OperationResult<bool>> Process(string destinationRoot, FileInfo fileInfo, bool doJustInfo,
|
||||
int? submissionId);
|
||||
public abstract Task<OperationResult<bool>> Process(FileInfo fileInfo, bool doJustInfo, int? submissionId);
|
||||
|
||||
protected virtual bool IsFileLocked(FileInfo file)
|
||||
{
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
using Roadie.Library.Enums;
|
||||
using Roadie.Library.Configuration;
|
||||
using Roadie.Library.Enums;
|
||||
using Roadie.Library.Extensions;
|
||||
using Roadie.Library.MetaData.Audio;
|
||||
using Roadie.Library.SearchEngines.Imaging;
|
||||
using Roadie.Library.Utility;
|
||||
using SixLabors.ImageSharp;
|
||||
|
@ -219,5 +221,86 @@ namespace Roadie.Library.Imaging
|
|||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get image data from all sources for either fileanme or MetaData
|
||||
/// </summary>
|
||||
/// <param name="filename">Name of the File (ie a CUE file)</param>
|
||||
/// <param name="metaData">Populated MetaData</param>
|
||||
/// <returns></returns>
|
||||
public static AudioMetaDataImage GetPictureForMetaData(IRoadieSettings configuration, string filename, AudioMetaData metaData)
|
||||
{
|
||||
SimpleContract.Requires<ArgumentException>(!string.IsNullOrEmpty(filename), "Invalid Filename");
|
||||
SimpleContract.Requires<ArgumentException>(metaData != null, "Invalid MetaData");
|
||||
|
||||
return ImageForFilename(configuration, filename);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does image exist with the same filename
|
||||
/// </summary>
|
||||
/// <param name="filename">Name of the File (ie a CUE file)</param>
|
||||
/// <returns>Null if not found else populated image</returns>
|
||||
public static AudioMetaDataImage ImageForFilename(IRoadieSettings configuration, string filename)
|
||||
{
|
||||
AudioMetaDataImage imageMetaData = null;
|
||||
|
||||
if (string.IsNullOrEmpty(filename)) return imageMetaData;
|
||||
try
|
||||
{
|
||||
var fileInfo = new FileInfo(filename);
|
||||
var ReleaseCover = Path.ChangeExtension(filename, "jpg");
|
||||
if (File.Exists(ReleaseCover))
|
||||
{
|
||||
using (var processor = new ImageProcessor(configuration))
|
||||
{
|
||||
imageMetaData = new AudioMetaDataImage
|
||||
{
|
||||
Data = processor.Process(File.ReadAllBytes(ReleaseCover)),
|
||||
Type = AudioMetaDataImageType.FrontCover,
|
||||
MimeType = Library.Processors.FileProcessor.DetermineFileType(fileInfo)
|
||||
};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Is there a picture in filename folder (for the Release)
|
||||
var pictures = fileInfo.Directory.GetFiles("*.jpg");
|
||||
var tagImages = new List<AudioMetaDataImage>();
|
||||
if (pictures != null && pictures.Any())
|
||||
{
|
||||
FileInfo picture = null;
|
||||
// See if there is a "cover" or "front" jpg file if so use it
|
||||
picture = pictures.FirstOrDefault(x =>
|
||||
x.Name.Equals("cover", StringComparison.OrdinalIgnoreCase));
|
||||
if (picture == null)
|
||||
picture = pictures.FirstOrDefault(x =>
|
||||
x.Name.Equals("front", StringComparison.OrdinalIgnoreCase));
|
||||
if (picture == null) picture = pictures.First();
|
||||
if (picture != null)
|
||||
using (var processor = new ImageProcessor(configuration))
|
||||
{
|
||||
imageMetaData = new AudioMetaDataImage
|
||||
{
|
||||
Data = processor.Process(File.ReadAllBytes(picture.FullName)),
|
||||
Type = AudioMetaDataImageType.FrontCover,
|
||||
MimeType = Library.Processors.FileProcessor.DetermineFileType(picture)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Trace.WriteLine(ex, ex.Serialize());
|
||||
}
|
||||
|
||||
return imageMetaData;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -120,7 +120,7 @@ namespace Roadie.Library.Inspect
|
|||
{
|
||||
Console.WriteLine("Roadie Media Inspector");
|
||||
|
||||
MessageLogger = new EventMessageLogger();
|
||||
MessageLogger = new EventMessageLogger<Inspector>();
|
||||
MessageLogger.Messages += MessageLogger_Messages;
|
||||
|
||||
var settings = new RoadieSettings();
|
||||
|
@ -131,18 +131,15 @@ namespace Roadie.Library.Inspect
|
|||
settings.ConnectionString = configuration.GetConnectionString("RoadieDatabaseConnection");
|
||||
Configuration = settings;
|
||||
CacheManager = new DictionaryCacheManager(Logger, new CachePolicy(TimeSpan.FromHours(4)));
|
||||
TagsHelper = new ID3TagsHelper(Configuration, CacheManager, Logger);
|
||||
|
||||
var tagHelperLooper = new EventMessageLogger<ID3TagsHelper>();
|
||||
tagHelperLooper.Messages += MessageLogger_Messages;
|
||||
TagsHelper = new ID3TagsHelper(Configuration, CacheManager, tagHelperLooper);
|
||||
}
|
||||
|
||||
public static string ArtistInspectorToken(AudioMetaData metaData)
|
||||
{
|
||||
return ToToken(metaData.Artist);
|
||||
}
|
||||
public static string ArtistInspectorToken(AudioMetaData metaData) => ToToken(metaData.Artist);
|
||||
|
||||
public static string ReleaseInspectorToken(AudioMetaData metaData)
|
||||
{
|
||||
return ToToken(metaData.Artist + metaData.Release);
|
||||
}
|
||||
public static string ReleaseInspectorToken(AudioMetaData metaData) => ToToken(metaData.Artist + metaData.Release);
|
||||
|
||||
public static string ToToken(string input)
|
||||
{
|
||||
|
@ -156,8 +153,7 @@ namespace Roadie.Library.Inspect
|
|||
return token;
|
||||
}
|
||||
|
||||
public void Inspect(bool doCopy, bool isReadOnly, string directoryToInspect, string destination,
|
||||
bool dontAppendSubFolder, bool dontDeleteEmptyFolders)
|
||||
public void Inspect(bool doCopy, bool isReadOnly, string directoryToInspect, string destination, bool dontAppendSubFolder, bool dontDeleteEmptyFolders)
|
||||
{
|
||||
Configuration.Inspector.IsInReadOnlyMode = isReadOnly;
|
||||
Configuration.Inspector.DoCopyFiles = doCopy;
|
||||
|
@ -570,10 +566,13 @@ namespace Roadie.Library.Inspect
|
|||
}
|
||||
}
|
||||
|
||||
private string RunScript(string scriptFilename, bool doCopy, bool isReadOnly, string directoryToInspect,
|
||||
string dest)
|
||||
private string RunScript(string scriptFilename, bool doCopy, bool isReadOnly, string directoryToInspect, string dest)
|
||||
{
|
||||
if (string.IsNullOrEmpty(scriptFilename)) return null;
|
||||
if (string.IsNullOrEmpty(scriptFilename))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var script = File.ReadAllText(scriptFilename);
|
||||
|
@ -589,7 +588,6 @@ namespace Roadie.Library.Inspect
|
|||
{
|
||||
Console.WriteLine($"📛 Error with Script File [{scriptFilename}], Error [{ex}] ");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ namespace Roadie.Library.Inspect.Plugins.Directory
|
|||
{
|
||||
if (!Configuration.Inspector.IsInReadOnlyMode) file.Delete();
|
||||
deletedFiles.Add(file.Name);
|
||||
Console.WriteLine($" X Deleted File [{file}], Was foud in in FileExtensionsToDelete");
|
||||
Console.WriteLine($" X Deleted File [{file}], Was found in in FileExtensionsToDelete");
|
||||
}
|
||||
|
||||
result.Data = $"Deleted [{deletedFiles.Count()}] unwanted files";
|
||||
|
|
|
@ -3,7 +3,7 @@ using System;
|
|||
|
||||
namespace Roadie.Library.Processors
|
||||
{
|
||||
public class EventMessageLogger : ILogger, IEventMessageLogger
|
||||
public class EventMessageLogger<T> : ILogger<T>, IEventMessageLogger
|
||||
{
|
||||
public event EventHandler<EventMessage> Messages;
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@ using Roadie.Library.Data;
|
|||
using Roadie.Library.Encoding;
|
||||
using Roadie.Library.Engines;
|
||||
using Roadie.Library.Extensions;
|
||||
using Roadie.Library.Factories;
|
||||
using Roadie.Library.FilePlugins;
|
||||
using Roadie.Library.MetaData.Audio;
|
||||
using System;
|
||||
|
@ -18,8 +17,32 @@ using System.Threading.Tasks;
|
|||
|
||||
namespace Roadie.Library.Processors
|
||||
{
|
||||
public sealed class FileProcessor : ProcessorBase
|
||||
public sealed class FileProcessor : IFileProcessor
|
||||
{
|
||||
private bool DoDeleteUnknowns => Configuration.Processing.DoDeleteUnknowns;
|
||||
|
||||
private bool DoMoveUnknowns => Configuration.Processing.DoMoveUnknowns;
|
||||
|
||||
public IHttpEncoder HttpEncoder { get; }
|
||||
|
||||
public int? SubmissionId { get; set; }
|
||||
|
||||
private string UnknownFolder => Configuration.Processing.UnknownFolder;
|
||||
|
||||
private IArtistLookupEngine ArtistLookupEngine { get; }
|
||||
|
||||
private IAudioMetaDataHelper AudioMetaDataHelper { get; }
|
||||
|
||||
private ICacheManager CacheManager { get; }
|
||||
|
||||
private IRoadieSettings Configuration { get; }
|
||||
|
||||
private IRoadieDbContext DbContext { get; }
|
||||
|
||||
private ILogger Logger { get; }
|
||||
|
||||
private IReleaseLookupEngine ReleaseLookupEngine { get; }
|
||||
|
||||
private IEnumerable<IFilePlugin> _plugins;
|
||||
|
||||
public IEnumerable<IFilePlugin> Plugins
|
||||
|
@ -38,8 +61,7 @@ namespace Roadie.Library.Processors
|
|||
foreach (var t in types)
|
||||
if (t.GetInterface("IFilePlugin") != null && !t.IsAbstract && !t.IsInterface)
|
||||
{
|
||||
var plugin = Activator.CreateInstance(t, Configuration, HttpEncoder, ArtistFactory,
|
||||
ReleaseFactory, ImageFactory, CacheManager, Logger, ArtistLookupEngine,
|
||||
var plugin = Activator.CreateInstance(t, Configuration, HttpEncoder, CacheManager, Logger, ArtistLookupEngine,
|
||||
ReleaseLookupEngine, AudioMetaDataHelper) as IFilePlugin;
|
||||
plugins.Add(plugin);
|
||||
}
|
||||
|
@ -56,14 +78,20 @@ namespace Roadie.Library.Processors
|
|||
}
|
||||
}
|
||||
|
||||
public FileProcessor(IRoadieSettings configuration, IHttpEncoder httpEncoder, string destinationRoot,
|
||||
IRoadieDbContext context, ICacheManager cacheManager, ILogger logger,
|
||||
IArtistLookupEngine artistLookupEngine, IArtistFactory artistFactory, IReleaseFactory releaseFactory,
|
||||
IImageFactory imageFactory, IReleaseLookupEngine releaseLookupEngine,
|
||||
IAudioMetaDataHelper audioMetaDataHelper)
|
||||
: base(configuration, httpEncoder, destinationRoot, context, cacheManager, logger, artistLookupEngine,
|
||||
artistFactory, releaseFactory, imageFactory, releaseLookupEngine, audioMetaDataHelper)
|
||||
public FileProcessor(IRoadieSettings configuration, IHttpEncoder httpEncoder,
|
||||
IRoadieDbContext context, ICacheManager cacheManager, ILogger<FileProcessor> logger,
|
||||
IArtistLookupEngine artistLookupEngine, IReleaseLookupEngine releaseLookupEngine,
|
||||
IAudioMetaDataHelper audioMetaDataHelper)
|
||||
{
|
||||
Configuration = configuration;
|
||||
HttpEncoder = httpEncoder;
|
||||
DbContext = context;
|
||||
CacheManager = cacheManager;
|
||||
Logger = logger;
|
||||
|
||||
ArtistLookupEngine = artistLookupEngine;
|
||||
ReleaseLookupEngine = releaseLookupEngine;
|
||||
AudioMetaDataHelper = audioMetaDataHelper;
|
||||
}
|
||||
|
||||
public static string DetermineFileType(FileInfo fileinfo)
|
||||
|
@ -98,7 +126,7 @@ namespace Roadie.Library.Processors
|
|||
// See if there is a plugin
|
||||
if (p.HandlesTypes.Contains(fileType))
|
||||
{
|
||||
pluginResult = await p.Process(DestinationRoot, fileInfo, doJustInfo, SubmissionId);
|
||||
pluginResult = await p.Process(fileInfo, doJustInfo, SubmissionId);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,140 +0,0 @@
|
|||
using Microsoft.Extensions.Logging;
|
||||
using Roadie.Library.Caching;
|
||||
using Roadie.Library.Configuration;
|
||||
using Roadie.Library.Data;
|
||||
using Roadie.Library.Encoding;
|
||||
using Roadie.Library.Engines;
|
||||
using Roadie.Library.Extensions;
|
||||
using Roadie.Library.Factories;
|
||||
using Roadie.Library.FilePlugins;
|
||||
using Roadie.Library.MetaData.Audio;
|
||||
using Roadie.Library.Utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Roadie.Library.Processors
|
||||
{
|
||||
public sealed class FolderProcessor : ProcessorBase
|
||||
{
|
||||
public int? ProcessLimit { get; set; }
|
||||
|
||||
private FileProcessor FileProcessor { get; }
|
||||
|
||||
public FolderProcessor(IRoadieSettings configuration, IHttpEncoder httpEncoder, string destinationRoot,
|
||||
IRoadieDbContext context, ICacheManager cacheManager, ILogger logger,
|
||||
IArtistLookupEngine artistLookupEngine, IArtistFactory artistFactory, IReleaseFactory releaseFactory,
|
||||
IImageFactory imageFactory, IReleaseLookupEngine releaseLookupEngine,
|
||||
IAudioMetaDataHelper audioMetaDataHelper)
|
||||
: base(configuration, httpEncoder, destinationRoot, context, cacheManager, logger, artistLookupEngine,
|
||||
artistFactory, releaseFactory, imageFactory, releaseLookupEngine, audioMetaDataHelper)
|
||||
{
|
||||
SimpleContract.Requires<ArgumentNullException>(!string.IsNullOrEmpty(destinationRoot),
|
||||
"Invalid Destination Folder");
|
||||
FileProcessor = new FileProcessor(configuration, httpEncoder, destinationRoot, context, cacheManager,
|
||||
logger, artistLookupEngine, artistFactory, releaseFactory, imageFactory, releaseLookupEngine,
|
||||
audioMetaDataHelper);
|
||||
}
|
||||
|
||||
public static OperationResult<bool> DeleteEmptyFolders(DirectoryInfo processingFolder, ILogger logger)
|
||||
{
|
||||
var result = new OperationResult<bool>();
|
||||
try
|
||||
{
|
||||
result.IsSuccess = FolderPathHelper.DeleteEmptyFolders(processingFolder);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex,
|
||||
string.Format("Error Deleting Empty Folder [{0}] Error [{1}]", processingFolder.FullName,
|
||||
ex.Serialize()));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<OperationResult<bool>> Process(DirectoryInfo folder, bool doJustInfo,
|
||||
int? submissionId = null)
|
||||
{
|
||||
var sw = new Stopwatch();
|
||||
sw.Start();
|
||||
await PreProcessFolder(folder, doJustInfo);
|
||||
var processedFiles = 0;
|
||||
var pluginResultInfos = new List<PluginResultInfo>();
|
||||
var errors = new List<string>();
|
||||
|
||||
FileProcessor.SubmissionId = submissionId;
|
||||
|
||||
foreach (var file in Directory.EnumerateFiles(folder.FullName, "*.*", SearchOption.AllDirectories)
|
||||
.ToArray())
|
||||
{
|
||||
var operation = await FileProcessor.Process(file, doJustInfo);
|
||||
if (operation != null && operation.AdditionalData != null &&
|
||||
operation.AdditionalData.ContainsKey(PluginResultInfo.AdditionalDataKeyPluginResultInfo))
|
||||
pluginResultInfos.Add(
|
||||
operation.AdditionalData[
|
||||
PluginResultInfo.AdditionalDataKeyPluginResultInfo] as PluginResultInfo);
|
||||
processedFiles++;
|
||||
if (ProcessLimit.HasValue && processedFiles > ProcessLimit.Value) break;
|
||||
}
|
||||
|
||||
await PostProcessFolder(folder, pluginResultInfos, doJustInfo);
|
||||
sw.Stop();
|
||||
Logger.LogInformation("** Completed! Processed Folder [{0}]: Processed Files [{1}] : Elapsed Time [{2}]",
|
||||
folder.FullName, processedFiles, sw.Elapsed);
|
||||
return new OperationResult<bool>
|
||||
{
|
||||
IsSuccess = !errors.Any(),
|
||||
AdditionalData = new Dictionary<string, object>
|
||||
{
|
||||
{"processedFiles", processedFiles},
|
||||
{"newArtists", ArtistLookupEngine.AddedArtistIds.Count()},
|
||||
{"newReleases", ReleaseLookupEngine.AddedReleaseIds.Count()},
|
||||
{"newTracks", ReleaseFactory.AddedTrackIds.Count()}
|
||||
},
|
||||
OperationTime = sw.ElapsedMilliseconds
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform any operations to the given folder and the plugin results after processing
|
||||
/// </summary>
|
||||
private async Task<bool> PostProcessFolder(DirectoryInfo inboundFolder,
|
||||
IEnumerable<PluginResultInfo> pluginResults, bool doJustInfo)
|
||||
{
|
||||
SimpleContract.Requires<ArgumentNullException>(inboundFolder != null, "Invalid InboundFolder");
|
||||
if (pluginResults != null)
|
||||
foreach (var releasesInfo in pluginResults.GroupBy(x => x.ReleaseId).Select(x => x.First()))
|
||||
await ReleaseFactory.ScanReleaseFolder(releasesInfo.ReleaseId, DestinationRoot, doJustInfo);
|
||||
if (!doJustInfo)
|
||||
{
|
||||
var fileExtensionsToDelete = Configuration.FileExtensionsToDelete ?? new string[0];
|
||||
if (fileExtensionsToDelete.Any())
|
||||
foreach (var fileInFolder in inboundFolder.GetFiles("*.*", SearchOption.AllDirectories))
|
||||
if (fileExtensionsToDelete.Any(x =>
|
||||
x.Equals(fileInFolder.Extension, StringComparison.OrdinalIgnoreCase)))
|
||||
if (!doJustInfo)
|
||||
{
|
||||
fileInFolder.Delete();
|
||||
Logger.LogInformation("x Deleted File [{0}], Was foud in in FileExtensionsToDelete",
|
||||
fileInFolder.Name);
|
||||
}
|
||||
|
||||
DeleteEmptyFolders(inboundFolder, Logger);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform any operations to the given folder before processing
|
||||
/// </summary>
|
||||
private Task<bool> PreProcessFolder(DirectoryInfo inboundFolder, bool doJustInfo = false)
|
||||
{
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
}
|
||||
}
|
18
Roadie.Api.Library/Processors/IFileProcessor.cs
Normal file
18
Roadie.Api.Library/Processors/IFileProcessor.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Roadie.Library.Encoding;
|
||||
using Roadie.Library.FilePlugins;
|
||||
|
||||
namespace Roadie.Library.Processors
|
||||
{
|
||||
public interface IFileProcessor
|
||||
{
|
||||
IHttpEncoder HttpEncoder { get; }
|
||||
IEnumerable<IFilePlugin> Plugins { get; }
|
||||
int? SubmissionId { get; set; }
|
||||
|
||||
Task<OperationResult<bool>> Process(FileInfo fileInfo, bool doJustInfo = false);
|
||||
Task<OperationResult<bool>> Process(string filename, bool doJustInfo = false);
|
||||
}
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
using Microsoft.Extensions.Logging;
|
||||
using Roadie.Library.Caching;
|
||||
using Roadie.Library.Configuration;
|
||||
using Roadie.Library.Data;
|
||||
using Roadie.Library.Encoding;
|
||||
using Roadie.Library.Engines;
|
||||
using Roadie.Library.Factories;
|
||||
using Roadie.Library.MetaData.Audio;
|
||||
|
||||
namespace Roadie.Library.Processors
|
||||
{
|
||||
public abstract class ProcessorBase
|
||||
{
|
||||
protected bool DoDeleteUnknowns => Configuration.Processing.DoDeleteUnknowns;
|
||||
|
||||
protected bool DoMoveUnknowns => Configuration.Processing.DoMoveUnknowns;
|
||||
|
||||
public IHttpEncoder HttpEncoder { get; }
|
||||
|
||||
public int? SubmissionId { get; set; }
|
||||
|
||||
protected string UnknownFolder => Configuration.Processing.UnknownFolder;
|
||||
|
||||
protected IArtistFactory ArtistFactory { get; }
|
||||
|
||||
protected IArtistLookupEngine ArtistLookupEngine { get; }
|
||||
|
||||
protected IAudioMetaDataHelper AudioMetaDataHelper { get; }
|
||||
|
||||
protected ICacheManager CacheManager { get; }
|
||||
|
||||
protected IRoadieSettings Configuration { get; }
|
||||
|
||||
protected IRoadieDbContext DbContext { get; }
|
||||
|
||||
protected string DestinationRoot { get; }
|
||||
|
||||
protected IImageFactory ImageFactory { get; }
|
||||
|
||||
protected ILogger Logger { get; }
|
||||
|
||||
protected IReleaseFactory ReleaseFactory { get; }
|
||||
|
||||
protected IReleaseLookupEngine ReleaseLookupEngine { get; }
|
||||
|
||||
public ProcessorBase(IRoadieSettings configuration, IHttpEncoder httpEncoder, string destinationRoot,
|
||||
IRoadieDbContext context, ICacheManager cacheManager,
|
||||
ILogger logger, IArtistLookupEngine artistLookupEngine, IArtistFactory artistFactory,
|
||||
IReleaseFactory releaseFactory, IImageFactory imageFactory, IReleaseLookupEngine releaseLookupEngine,
|
||||
IAudioMetaDataHelper audioMetaDataHelper)
|
||||
{
|
||||
Configuration = configuration;
|
||||
HttpEncoder = httpEncoder;
|
||||
DbContext = context;
|
||||
CacheManager = cacheManager;
|
||||
Logger = logger;
|
||||
|
||||
DestinationRoot = destinationRoot;
|
||||
ArtistLookupEngine = artistLookupEngine;
|
||||
ReleaseLookupEngine = releaseLookupEngine;
|
||||
ArtistFactory = artistFactory;
|
||||
ReleaseFactory = releaseFactory;
|
||||
ImageFactory = imageFactory;
|
||||
AudioMetaDataHelper = audioMetaDataHelper;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,7 +10,7 @@
|
|||
<ItemGroup>
|
||||
<PackageReference Include="AutoCompare.Core" Version="1.0.0" />
|
||||
<PackageReference Include="CsvHelper" Version="12.1.2" />
|
||||
<PackageReference Include="EFCore.BulkExtensions" Version="2.4.7" />
|
||||
<PackageReference Include="EFCore.BulkExtensions" Version="2.4.9" />
|
||||
<PackageReference Include="FluentFTP" Version="25.0.5" />
|
||||
<PackageReference Include="Hashids.net" Version="1.2.2" />
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.11.8" />
|
||||
|
@ -22,11 +22,12 @@
|
|||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.2.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Redis" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.PowerShell.5.ReferenceAssemblies" Version="1.1.0" />
|
||||
<PackageReference Include="Microsoft.PowerShell.SDK" Version="6.2.1" />
|
||||
<PackageReference Include="MimeMapping" Version="1.0.1.12" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
|
||||
<PackageReference Include="NodaTime" Version="2.4.6" />
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="2.2.0" />
|
||||
<PackageReference Include="RestSharp" Version="106.6.9" />
|
||||
<PackageReference Include="RestSharp" Version="106.6.10" />
|
||||
<PackageReference Include="SixLabors.Core" Version="1.0.0-beta0006" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.0-beta0005" />
|
||||
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta0005" />
|
||||
|
|
9
Roadie.Api.Library/Scrobble/ILastFMScrobbler.cs
Normal file
9
Roadie.Api.Library/Scrobble/ILastFMScrobbler.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
using System.Threading.Tasks;
|
||||
using Roadie.Library.Models.Users;
|
||||
|
||||
namespace Roadie.Library.Scrobble
|
||||
{
|
||||
public interface ILastFMScrobbler : IScrobblerIntegration
|
||||
{
|
||||
}
|
||||
}
|
9
Roadie.Api.Library/Scrobble/IRoadieScrobbler.cs
Normal file
9
Roadie.Api.Library/Scrobble/IRoadieScrobbler.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
using System.Threading.Tasks;
|
||||
using Roadie.Library.Models.Users;
|
||||
|
||||
namespace Roadie.Library.Scrobble
|
||||
{
|
||||
public interface IRoadieScrobbler : IScrobblerIntegration
|
||||
{
|
||||
}
|
||||
}
|
|
@ -13,11 +13,11 @@ namespace Roadie.Library.Scrobble
|
|||
/// LastFM Scrobbler
|
||||
/// <seealso cref="https://www.last.fm/api/scrobbling" />
|
||||
/// </summary>
|
||||
public class LastFMScrobbler : ScrobblerIntegrationBase
|
||||
public class LastFMScrobbler : ScrobblerIntegrationBase, ILastFMScrobbler
|
||||
{
|
||||
private ILastFmHelper LastFmHelper { get; }
|
||||
|
||||
public LastFMScrobbler(IRoadieSettings configuration, ILogger logger, data.IRoadieDbContext dbContext,
|
||||
public LastFMScrobbler(IRoadieSettings configuration, ILogger<LastFMScrobbler> logger, data.IRoadieDbContext dbContext,
|
||||
ICacheManager cacheManager, ILastFmHelper lastFmHelper, IHttpContext httpContext)
|
||||
: base(configuration, logger, dbContext, cacheManager, httpContext)
|
||||
{
|
||||
|
@ -32,10 +32,7 @@ namespace Roadie.Library.Scrobble
|
|||
/// indication of what music player they're using.
|
||||
/// </remark>
|
||||
/// </summary>
|
||||
public override async Task<OperationResult<bool>> NowPlaying(User roadieUser, ScrobbleInfo scrobble)
|
||||
{
|
||||
return await LastFmHelper.NowPlaying(roadieUser, scrobble);
|
||||
}
|
||||
public override async Task<OperationResult<bool>> NowPlaying(User roadieUser, ScrobbleInfo scrobble) => await LastFmHelper.NowPlaying(roadieUser, scrobble);
|
||||
|
||||
/// <summary>
|
||||
/// Send a Scrobble Request
|
||||
|
@ -44,9 +41,7 @@ namespace Roadie.Library.Scrobble
|
|||
/// listening history and generate personalised charts and recommendations (and more).
|
||||
/// </remark>
|
||||
/// </summary>
|
||||
public override async Task<OperationResult<bool>> Scrobble(User roadieUser, ScrobbleInfo scrobble)
|
||||
{
|
||||
return await LastFmHelper.Scrobble(roadieUser, scrobble);
|
||||
}
|
||||
public override async Task<OperationResult<bool>> Scrobble(User roadieUser, ScrobbleInfo scrobble) => await LastFmHelper.Scrobble(roadieUser, scrobble);
|
||||
|
||||
}
|
||||
}
|
|
@ -12,9 +12,9 @@ using data = Roadie.Library.Data;
|
|||
|
||||
namespace Roadie.Library.Scrobble
|
||||
{
|
||||
public class RoadieScrobbler : ScrobblerIntegrationBase
|
||||
public class RoadieScrobbler : ScrobblerIntegrationBase, IRoadieScrobbler
|
||||
{
|
||||
public RoadieScrobbler(IRoadieSettings configuration, ILogger logger, data.IRoadieDbContext dbContext,
|
||||
public RoadieScrobbler(IRoadieSettings configuration, ILogger<RoadieScrobbler> logger, data.IRoadieDbContext dbContext,
|
||||
ICacheManager cacheManager, IHttpContext httpContext)
|
||||
: base(configuration, logger, dbContext, cacheManager, httpContext)
|
||||
{
|
||||
|
@ -25,11 +25,11 @@ namespace Roadie.Library.Scrobble
|
|||
/// </summary>
|
||||
public override async Task<OperationResult<bool>> NowPlaying(User roadieUser, ScrobbleInfo scrobble)
|
||||
{
|
||||
return new OperationResult<bool>
|
||||
return await Task.FromResult(new OperationResult<bool>
|
||||
{
|
||||
Data = true,
|
||||
IsSuccess = true
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -30,9 +30,9 @@ namespace Roadie.Library.Scrobble
|
|||
|
||||
private IEnumerable<IScrobblerIntegration> Scrobblers { get; }
|
||||
|
||||
public ScrobbleHandler(IRoadieSettings configuration, ILogger<ScrobbleHandler> logger,
|
||||
data.IRoadieDbContext dbContext,
|
||||
ICacheManager cacheManager, IHttpEncoder httpEncoder, IHttpContext httpContext)
|
||||
public ScrobbleHandler(IRoadieSettings configuration, ILogger<ScrobbleHandler> logger, data.IRoadieDbContext dbContext,
|
||||
ICacheManager cacheManager, IHttpEncoder httpEncoder, IHttpContext httpContext,
|
||||
ILastFmHelper lastFmHelper, IRoadieScrobbler roadieScrobbler, ILastFMScrobbler lastFMScrobbler)
|
||||
{
|
||||
Logger = logger;
|
||||
Configuration = configuration;
|
||||
|
@ -41,10 +41,12 @@ namespace Roadie.Library.Scrobble
|
|||
HttpContext = httpContext;
|
||||
var scrobblers = new List<IScrobblerIntegration>
|
||||
{
|
||||
new RoadieScrobbler(configuration, logger, dbContext, cacheManager, httpContext)
|
||||
roadieScrobbler
|
||||
};
|
||||
if (configuration.Integrations.LastFmProviderEnabled)
|
||||
scrobblers.Add(new LastFmHelper(configuration, cacheManager, logger, dbContext, httpEncoder));
|
||||
{
|
||||
scrobblers.Add(lastFMScrobbler);
|
||||
}
|
||||
Scrobblers = scrobblers;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,10 +14,12 @@ namespace Roadie.Library.SearchEngines.Imaging
|
|||
/// <summary>
|
||||
/// https://msdn.microsoft.com/en-us/library/dn760791(v=bsynd.50).aspx
|
||||
/// </summary>
|
||||
public class BingImageSearchEngine : ImageSearchEngineBase
|
||||
public class BingImageSearchEngine : ImageSearchEngineBase, IBingImageSearchEngine
|
||||
{
|
||||
public BingImageSearchEngine(IRoadieSettings configuration, ILogger logger, string requestIp = null,
|
||||
string referrer = null)
|
||||
|
||||
public override bool IsEnabled => Configuration.Integrations.BingImageSearchEngineEnabled;
|
||||
|
||||
public BingImageSearchEngine(IRoadieSettings configuration, ILogger<BingImageSearchEngine> logger, string requestIp = null, string referrer = null)
|
||||
: base(configuration, logger, "https://api.cognitive.microsoft.com", requestIp, referrer)
|
||||
{
|
||||
_apiKey = configuration.Integrations.ApiKeys.FirstOrDefault(x => x.ApiName == "BingImageSearch") ??
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using RestSharp;
|
||||
|
||||
namespace Roadie.Library.SearchEngines.Imaging
|
||||
{
|
||||
public interface IBingImageSearchEngine : IImageSearchEngine
|
||||
{
|
||||
}
|
||||
}
|
|
@ -5,17 +5,7 @@ using System.Threading.Tasks;
|
|||
|
||||
namespace Roadie.Library.SearchEngines.Imaging
|
||||
{
|
||||
public interface IITunesSearchEngine
|
||||
public interface IITunesSearchEngine : IArtistSearchEngine, IReleaseSearchEngine, IImageSearchEngine
|
||||
{
|
||||
bool IsEnabled { get; }
|
||||
|
||||
RestRequest BuildRequest(string query, int resultsCount);
|
||||
|
||||
Task<OperationResult<IEnumerable<ArtistSearchResult>>> PerformArtistSearch(string query, int resultsCount);
|
||||
|
||||
Task<IEnumerable<ImageSearchResult>> PerformImageSearch(string query, int resultsCount);
|
||||
|
||||
Task<OperationResult<IEnumerable<ReleaseSearchResult>>> PerformReleaseSearch(string artistName, string query,
|
||||
int resultsCount);
|
||||
}
|
||||
}
|
|
@ -6,6 +6,8 @@ namespace Roadie.Library.SearchEngines.Imaging
|
|||
{
|
||||
public interface IImageSearchEngine
|
||||
{
|
||||
bool IsEnabled { get; }
|
||||
|
||||
RestRequest BuildRequest(string query, int resultsCount);
|
||||
|
||||
Task<IEnumerable<ImageSearchResult>> PerformImageSearch(string query, int resultsCount);
|
||||
|
|
|
@ -13,11 +13,9 @@ using System.Threading.Tasks;
|
|||
|
||||
namespace Roadie.Library.SearchEngines.Imaging
|
||||
{
|
||||
public class ITunesSearchEngine : ImageSearchEngineBase, IArtistSearchEngine, IReleaseSearchEngine,
|
||||
IITunesSearchEngine
|
||||
public class ITunesSearchEngine : ImageSearchEngineBase, IITunesSearchEngine
|
||||
{
|
||||
public ITunesSearchEngine(IRoadieSettings configuration, ICacheManager cacheManager, ILogger logger,
|
||||
string requestIp = null, string referrer = null)
|
||||
public ITunesSearchEngine(IRoadieSettings configuration, ICacheManager cacheManager, ILogger<ITunesSearchEngine> logger, string requestIp = null, string referrer = null)
|
||||
: base(configuration, logger, "http://itunes.apple.com", requestIp, referrer)
|
||||
{
|
||||
CacheManager = cacheManager;
|
||||
|
@ -25,10 +23,9 @@ namespace Roadie.Library.SearchEngines.Imaging
|
|||
|
||||
private ICacheManager CacheManager { get; }
|
||||
|
||||
public bool IsEnabled => true;
|
||||
public override bool IsEnabled => Configuration.Integrations.ITunesProviderEnabled;
|
||||
|
||||
public async Task<OperationResult<IEnumerable<ArtistSearchResult>>> PerformArtistSearch(string query,
|
||||
int resultsCount)
|
||||
public async Task<OperationResult<IEnumerable<ArtistSearchResult>>> PerformArtistSearch(string query, int resultsCount)
|
||||
{
|
||||
ArtistSearchResult data = null;
|
||||
|
||||
|
@ -118,8 +115,7 @@ namespace Roadie.Library.SearchEngines.Imaging
|
|||
return result;
|
||||
}
|
||||
|
||||
public async Task<OperationResult<IEnumerable<ReleaseSearchResult>>> PerformReleaseSearch(string artistName,
|
||||
string query, int resultsCount)
|
||||
public async Task<OperationResult<IEnumerable<ReleaseSearchResult>>> PerformReleaseSearch(string artistName, string query, int resultsCount)
|
||||
{
|
||||
var request = BuildRequest(query, 1, "album");
|
||||
var response = await _client.ExecuteTaskAsync<ITunesSearchResult>(request);
|
||||
|
|
|
@ -18,6 +18,8 @@ namespace Roadie.Library.SearchEngines.Imaging
|
|||
protected IApiKey _apiKey = null;
|
||||
protected ILogger _logger;
|
||||
|
||||
public abstract bool IsEnabled { get; }
|
||||
|
||||
protected IApiKey ApiKey => _apiKey;
|
||||
|
||||
protected IRoadieSettings Configuration => _configuratio;
|
||||
|
|
|
@ -13,30 +13,44 @@ namespace Roadie.Library.SearchEngines.Imaging
|
|||
private readonly IImageSearchEngine _bingSearchEngine;
|
||||
private readonly IImageSearchEngine _itunesSearchEngine;
|
||||
|
||||
private IRoadieSettings Configuration { get; }
|
||||
|
||||
private int DefaultResultsCount => 10;
|
||||
|
||||
public ImageSearchManager(IRoadieSettings configuration, ICacheManager cacheManager, ILogger logger,
|
||||
string requestIp = null, string referrer = null)
|
||||
public ImageSearchManager(IRoadieSettings configuration, ICacheManager cacheManager, ILogger<ImageSearchManager> logger,
|
||||
IBingImageSearchEngine bingImageSearchEngine, IITunesSearchEngine iTunesSearchEngine,
|
||||
string requestIp = null, string referrer = null)
|
||||
{
|
||||
_bingSearchEngine = new BingImageSearchEngine(configuration, logger, requestIp, referrer);
|
||||
_itunesSearchEngine = new ITunesSearchEngine(configuration, cacheManager, logger, requestIp, referrer);
|
||||
Configuration = configuration;
|
||||
_bingSearchEngine = bingImageSearchEngine;
|
||||
_itunesSearchEngine = iTunesSearchEngine;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ImageSearchResult>> ImageSearch(string query, int? resultsCount = null)
|
||||
{
|
||||
var count = resultsCount ?? DefaultResultsCount;
|
||||
var result = new List<ImageSearchResult>();
|
||||
|
||||
if (WebHelper.IsStringUrl(query))
|
||||
{
|
||||
var s = ImageHelper.ImageSearchResultForImageUrl(query);
|
||||
if (s != null) result.Add(s);
|
||||
}
|
||||
|
||||
var bingResults = await _bingSearchEngine.PerformImageSearch(query, count);
|
||||
if (bingResults != null) result.AddRange(bingResults);
|
||||
var iTunesResults = await _itunesSearchEngine.PerformImageSearch(query, count);
|
||||
if (iTunesResults != null) result.AddRange(iTunesResults);
|
||||
if (Configuration.Integrations.BingImageSearchEngineEnabled)
|
||||
{
|
||||
var bingResults = await _bingSearchEngine.PerformImageSearch(query, count);
|
||||
if (bingResults != null)
|
||||
{
|
||||
result.AddRange(bingResults);
|
||||
}
|
||||
}
|
||||
if (Configuration.Integrations.ITunesProviderEnabled)
|
||||
{
|
||||
var iTunesResults = await _itunesSearchEngine.PerformImageSearch(query, count);
|
||||
if (iTunesResults != null)
|
||||
{
|
||||
result.AddRange(iTunesResults);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ using Roadie.Library.Data;
|
|||
using Roadie.Library.Encoding;
|
||||
using Roadie.Library.Engines;
|
||||
using Roadie.Library.Extensions;
|
||||
using Roadie.Library.Factories;
|
||||
using Roadie.Library.MetaData.FileName;
|
||||
using Roadie.Library.MetaData.ID3Tags;
|
||||
using Roadie.Library.MetaData.LastFm;
|
||||
|
@ -46,8 +45,6 @@ namespace Roadie.Library.MetaData.Audio
|
|||
|
||||
private IID3TagsHelper ID3TagsHelper { get; }
|
||||
|
||||
private IImageFactory ImageFactory { get; }
|
||||
|
||||
private ILastFmHelper LastFmHelper { get; }
|
||||
|
||||
private ILogger Logger { get; }
|
||||
|
@ -55,16 +52,14 @@ namespace Roadie.Library.MetaData.Audio
|
|||
private IMusicBrainzProvider MusicBrainzProvider { get; }
|
||||
|
||||
public AudioMetaDataHelper(IRoadieSettings configuration, IHttpEncoder httpEncoder, IRoadieDbContext context,
|
||||
IMusicBrainzProvider musicBrainzHelper,
|
||||
ILastFmHelper lastFmHelper, ICacheManager cacheManager, ILogger logger,
|
||||
IArtistLookupEngine artistLookupEngine,
|
||||
IImageFactory imageFactory, IFileNameHelper filenameHelper, IID3TagsHelper id3TagsHelper)
|
||||
IMusicBrainzProvider musicBrainzHelper, ILastFmHelper lastFmHelper, ICacheManager cacheManager,
|
||||
ILogger<AudioMetaDataHelper> logger, IArtistLookupEngine artistLookupEngine, IFileNameHelper filenameHelper,
|
||||
IID3TagsHelper id3TagsHelper)
|
||||
{
|
||||
Configuration = configuration;
|
||||
HttpEncoder = httpEncoder;
|
||||
CacheManager = cacheManager;
|
||||
Logger = logger;
|
||||
ImageFactory = ImageFactory;
|
||||
FileNameHelper = filenameHelper;
|
||||
ID3TagsHelper = id3TagsHelper;
|
||||
|
||||
|
@ -74,8 +69,7 @@ namespace Roadie.Library.MetaData.Audio
|
|||
ArtistLookupEngine = artistLookupEngine;
|
||||
|
||||
DoParseFromFileName = configuration.Processing.DoParseFromFileName;
|
||||
DoParseFromDiscogsDBFindingTrackForArtist =
|
||||
configuration.Processing.DoParseFromDiscogsDBFindingTrackForArtist;
|
||||
DoParseFromDiscogsDBFindingTrackForArtist = configuration.Processing.DoParseFromDiscogsDBFindingTrackForArtist;
|
||||
DoParseFromDiscogsDB = configuration.Processing.DoParseFromDiscogsDB;
|
||||
DoParseFromMusicBrainz = configuration.Processing.DoParseFromMusicBrainz;
|
||||
DoParseFromLastFM = configuration.Processing.DoParseFromLastFM;
|
||||
|
@ -128,7 +122,7 @@ namespace Roadie.Library.MetaData.Audio
|
|||
{
|
||||
if (result.Images == null || !result.Images.Any())
|
||||
{
|
||||
var imageMetaData = ImageFactory.GetPictureForMetaData(fileInfo.FullName, result);
|
||||
var imageMetaData = Imaging.ImageHelper.GetPictureForMetaData(Configuration, fileInfo.FullName, result);
|
||||
var tagImages = imageMetaData == null ? null : new List<AudioMetaDataImage> { imageMetaData };
|
||||
result.Images = tagImages != null && tagImages.Any() ? tagImages : null;
|
||||
if (result.Images == null || !result.Images.Any())
|
||||
|
|
|
@ -14,19 +14,17 @@ using System.Threading.Tasks;
|
|||
|
||||
namespace Roadie.Library.SearchEngines.MetaData.Discogs
|
||||
{
|
||||
public class DiscogsHelper : MetaDataProviderBase, IArtistSearchEngine, IReleaseSearchEngine, ILabelSearchEngine
|
||||
public class DiscogsHelper : MetaDataProviderBase, IDiscogsHelper
|
||||
{
|
||||
public override bool IsEnabled => Configuration.Integrations.DiscogsProviderEnabled;
|
||||
|
||||
public DiscogsHelper(IRoadieSettings configuration, ICacheManager cacheManager, ILogger logger) : base(
|
||||
public DiscogsHelper(IRoadieSettings configuration, ICacheManager cacheManager, ILogger<DiscogsHelper> logger) : base(
|
||||
configuration, cacheManager, logger)
|
||||
{
|
||||
_apiKey = configuration.Integrations.ApiKeys.FirstOrDefault(x => x.ApiName == "DiscogsConsumerKey") ??
|
||||
new ApiKey();
|
||||
_apiKey = configuration.Integrations.ApiKeys.FirstOrDefault(x => x.ApiName == "DiscogsConsumerKey") ?? new ApiKey();
|
||||
}
|
||||
|
||||
public async Task<OperationResult<IEnumerable<ArtistSearchResult>>> PerformArtistSearch(string query,
|
||||
int resultsCount)
|
||||
public async Task<OperationResult<IEnumerable<ArtistSearchResult>>> PerformArtistSearch(string query, int resultsCount)
|
||||
{
|
||||
ArtistSearchResult data = null;
|
||||
try
|
||||
|
@ -101,8 +99,7 @@ namespace Roadie.Library.SearchEngines.MetaData.Discogs
|
|||
};
|
||||
}
|
||||
|
||||
public async Task<OperationResult<IEnumerable<LabelSearchResult>>> PerformLabelSearch(string labelName,
|
||||
int resultsCount)
|
||||
public async Task<OperationResult<IEnumerable<LabelSearchResult>>> PerformLabelSearch(string labelName, int resultsCount)
|
||||
{
|
||||
LabelSearchResult data = null;
|
||||
try
|
||||
|
@ -174,8 +171,7 @@ namespace Roadie.Library.SearchEngines.MetaData.Discogs
|
|||
};
|
||||
}
|
||||
|
||||
public async Task<OperationResult<IEnumerable<ReleaseSearchResult>>> PerformReleaseSearch(string artistName,
|
||||
string query, int resultsCount)
|
||||
public async Task<OperationResult<IEnumerable<ReleaseSearchResult>>> PerformReleaseSearch(string artistName, string query, int resultsCount)
|
||||
{
|
||||
ReleaseSearchResult data = null;
|
||||
try
|
||||
|
@ -185,30 +181,30 @@ namespace Roadie.Library.SearchEngines.MetaData.Discogs
|
|||
var client = new RestClient("https://api.discogs.com/database")
|
||||
{
|
||||
UserAgent = WebHelper.UserAgent,
|
||||
ReadWriteTimeout = (int)Configuration.Integrations.DiscogsReadWriteTimeout,
|
||||
Timeout = (int)Configuration.Integrations.DiscogsTimeout
|
||||
ReadWriteTimeout = SafeParser.ToNumber<int>(Configuration.Integrations.DiscogsReadWriteTimeout),
|
||||
Timeout = SafeParser.ToNumber<int>(Configuration.Integrations.DiscogsTimeout)
|
||||
};
|
||||
|
||||
var response = await client.ExecuteTaskAsync<DiscogsReleaseSearchResult>(request);
|
||||
|
||||
if (response.ResponseStatus == ResponseStatus.Error)
|
||||
if (response?.ResponseStatus == null || response.ResponseStatus == ResponseStatus.Error)
|
||||
{
|
||||
if (response.StatusCode == HttpStatusCode.Unauthorized)
|
||||
{
|
||||
throw new AuthenticationException("Unauthorized");
|
||||
throw new Exception(string.Format("Request Error Message: {0}. Content: {1}.",
|
||||
response.ErrorMessage, response.Content));
|
||||
}
|
||||
throw new Exception($"Request Error Message: {response?.ErrorMessage}. Content: {response?.Content}.");
|
||||
}
|
||||
|
||||
var responseData = response.Data != null && response.Data.results.Any()
|
||||
? response.Data.results.OrderBy(x => x.year).First()
|
||||
: null;
|
||||
if (responseData != null)
|
||||
var responseData = response?.Data?.results?.OrderBy(x => x.year).FirstOrDefault();
|
||||
if (responseData?.id != null)
|
||||
{
|
||||
request = BuildReleaseRequest(responseData.id);
|
||||
var c2 = new RestClient("https://api.discogs.com/");
|
||||
c2.UserAgent = WebHelper.UserAgent;
|
||||
var c2 = new RestClient("https://api.discogs.com/")
|
||||
{
|
||||
UserAgent = WebHelper.UserAgent
|
||||
};
|
||||
var releaseResult = await c2.ExecuteTaskAsync<DiscogReleaseDetail>(request);
|
||||
var release = releaseResult != null && releaseResult.Data != null ? releaseResult.Data : null;
|
||||
var release = releaseResult?.Data;
|
||||
if (release != null)
|
||||
{
|
||||
var urls = new List<string>();
|
||||
|
@ -278,16 +274,18 @@ namespace Roadie.Library.SearchEngines.MetaData.Discogs
|
|||
|
||||
if (release.identifiers != null)
|
||||
{
|
||||
var barcode = release.identifiers.FirstOrDefault(x => x.type == "Barcode");
|
||||
if (barcode != null && !string.IsNullOrEmpty(barcode.value))
|
||||
var barcode = release.identifiers.FirstOrDefault(x => (x.type ?? string.Empty) == "Barcode");
|
||||
if (barcode?.value != null)
|
||||
{
|
||||
data.Tags = new[] { "barcode:" + barcode.value };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex);
|
||||
Logger.LogError(ex, $"DiscogsHelper: Error in PerformReleaseSearch artistname [{ artistName }], query [{ query }], resultsCount [{ resultsCount }]");
|
||||
}
|
||||
|
||||
return new OperationResult<IEnumerable<ReleaseSearchResult>>
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Roadie.Library.SearchEngines.MetaData.Discogs
|
||||
{
|
||||
public interface IDiscogsHelper : IArtistSearchEngine, IReleaseSearchEngine, ILabelSearchEngine
|
||||
{
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@ namespace Roadie.Library.MetaData.FileName
|
|||
{
|
||||
public class FileNameHelper : MetaDataProviderBase, IFileNameHelper
|
||||
{
|
||||
public FileNameHelper(IRoadieSettings configuration, ICacheManager cacheManager, ILogger logger)
|
||||
public FileNameHelper(IRoadieSettings configuration, ICacheManager cacheManager, ILogger<FileNameHelper> logger)
|
||||
: base(configuration, cacheManager, logger)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using ATL;
|
||||
using ATL.CatalogDataReaders;
|
||||
using ATL.Playlist;
|
||||
using ATL.PlaylistReaders;
|
||||
using IdSharp.AudioInfo;
|
||||
using IdSharp.Tagging.ID3v1;
|
||||
|
@ -21,7 +22,7 @@ namespace Roadie.Library.MetaData.ID3Tags
|
|||
{
|
||||
public class ID3TagsHelper : MetaDataProviderBase, IID3TagsHelper
|
||||
{
|
||||
public ID3TagsHelper(IRoadieSettings configuration, ICacheManager cacheManager, ILogger logger)
|
||||
public ID3TagsHelper(IRoadieSettings configuration, ICacheManager cacheManager, ILogger<ID3TagsHelper> logger)
|
||||
: base(configuration, cacheManager, logger)
|
||||
{
|
||||
}
|
||||
|
@ -86,8 +87,8 @@ namespace Roadie.Library.MetaData.ID3Tags
|
|||
if (m3uFiles != null && m3uFiles.Any())
|
||||
try
|
||||
{
|
||||
var theReader = PlaylistReaderFactory.GetInstance().GetPlaylistReader(m3uFiles.First());
|
||||
result = (short)theReader.GetFiles().Count();
|
||||
var theReader = PlaylistIOFactory.GetInstance().GetPlaylistIO(m3uFiles.First());
|
||||
result = (short)theReader.FilePaths.Count();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
|
@ -6,17 +6,10 @@ using System.Threading.Tasks;
|
|||
|
||||
namespace Roadie.Library.MetaData.LastFm
|
||||
{
|
||||
public interface ILastFmHelper : IScrobblerIntegration
|
||||
public interface ILastFmHelper : IScrobblerIntegration, IArtistSearchEngine, IReleaseSearchEngine
|
||||
{
|
||||
bool IsEnabled { get; }
|
||||
|
||||
Task<OperationResult<string>> GetSessionKeyForUserToken(string token);
|
||||
|
||||
Task<OperationResult<IEnumerable<ArtistSearchResult>>> PerformArtistSearch(string query, int resultsCount);
|
||||
|
||||
Task<OperationResult<IEnumerable<ReleaseSearchResult>>> PerformReleaseSearch(string artistName, string query,
|
||||
int resultsCount);
|
||||
|
||||
Task<IEnumerable<AudioMetaData>> TracksForRelease(string artist, string Release);
|
||||
}
|
||||
}
|
|
@ -25,7 +25,7 @@ using data = Roadie.Library.Data;
|
|||
|
||||
namespace Roadie.Library.MetaData.LastFm
|
||||
{
|
||||
public class LastFmHelper : MetaDataProviderBase, IArtistSearchEngine, IReleaseSearchEngine, ILastFmHelper
|
||||
public class LastFmHelper : MetaDataProviderBase, ILastFmHelper
|
||||
{
|
||||
private const string LastFmErrorCodeXPath = "/lfm/error/@code";
|
||||
private const string LastFmErrorXPath = "/lfm/error";
|
||||
|
@ -41,7 +41,7 @@ namespace Roadie.Library.MetaData.LastFm
|
|||
|
||||
private IHttpEncoder HttpEncoder { get; }
|
||||
|
||||
public LastFmHelper(IRoadieSettings configuration, ICacheManager cacheManager, ILogger logger,
|
||||
public LastFmHelper(IRoadieSettings configuration, ICacheManager cacheManager, ILogger<LastFmHelper> logger,
|
||||
data.IRoadieDbContext dbContext, IHttpEncoder httpEncoder)
|
||||
: base(configuration, cacheManager, logger)
|
||||
{
|
||||
|
@ -121,43 +121,45 @@ namespace Roadie.Library.MetaData.LastFm
|
|||
var user = DbContext.Users.FirstOrDefault(x => x.RoadieId == roadieUser.UserId);
|
||||
|
||||
if (user == null || string.IsNullOrEmpty(user.LastFMSessionKey))
|
||||
{
|
||||
return new OperationResult<bool>("User does not have LastFM Integration setup");
|
||||
var method = "track.updateNowPlaying";
|
||||
var parameters = new RequestParameters
|
||||
{
|
||||
{"artist", scrobble.ArtistName},
|
||||
{"track", scrobble.TrackTitle},
|
||||
{"album", scrobble.ReleaseTitle},
|
||||
{"duration", ((int) scrobble.TrackDuration.TotalSeconds).ToString()}
|
||||
};
|
||||
var url = "http://ws.audioscrobbler.com/2.0/";
|
||||
var signature = GenerateMethodSignature(method, parameters, user.LastFMSessionKey);
|
||||
parameters.Add("api_sig", signature);
|
||||
|
||||
ServicePointManager.Expect100Continue = false;
|
||||
var request = WebRequest.Create(url);
|
||||
request.Method = "POST";
|
||||
var postData = parameters.ToString();
|
||||
var byteArray = System.Text.Encoding.UTF8.GetBytes(postData);
|
||||
request.ContentType = "application/x-www-form-urlencoded";
|
||||
request.ContentLength = byteArray.Length;
|
||||
using (var dataStream = request.GetRequestStream())
|
||||
{
|
||||
dataStream.Write(byteArray, 0, byteArray.Length);
|
||||
dataStream.Close();
|
||||
}
|
||||
await Task.Run(() =>
|
||||
{
|
||||
var method = "track.updateNowPlaying";
|
||||
var parameters = new RequestParameters
|
||||
{
|
||||
{"artist", scrobble.ArtistName},
|
||||
{"track", scrobble.TrackTitle},
|
||||
{"album", scrobble.ReleaseTitle},
|
||||
{"duration", ((int) scrobble.TrackDuration.TotalSeconds).ToString()}
|
||||
};
|
||||
var url = "http://ws.audioscrobbler.com/2.0/";
|
||||
var signature = GenerateMethodSignature(method, parameters, user.LastFMSessionKey);
|
||||
parameters.Add("api_sig", signature);
|
||||
|
||||
var xp = GetResponseAsXml(request);
|
||||
Logger.LogInformation(
|
||||
$"LastFmHelper: RoadieUser `{roadieUser}` NowPlaying `{scrobble}` LastFmResult [{xp.InnerXml}]");
|
||||
result = true;
|
||||
ServicePointManager.Expect100Continue = false;
|
||||
var request = WebRequest.Create(url);
|
||||
request.Method = "POST";
|
||||
var postData = parameters.ToString();
|
||||
var byteArray = System.Text.Encoding.UTF8.GetBytes(postData);
|
||||
request.ContentType = "application/x-www-form-urlencoded";
|
||||
request.ContentLength = byteArray.Length;
|
||||
using (var dataStream = request.GetRequestStream())
|
||||
{
|
||||
dataStream.Write(byteArray, 0, byteArray.Length);
|
||||
dataStream.Close();
|
||||
}
|
||||
var xp = GetResponseAsXml(request);
|
||||
Logger.LogInformation($"LastFmHelper: RoadieUser `{roadieUser}` NowPlaying `{scrobble}` LastFmResult [{xp.InnerXml}]");
|
||||
result = true;
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex,
|
||||
$"Error in LastFmHelper NowPlaying: RoadieUser `{roadieUser}` Scrobble `{scrobble}`");
|
||||
}
|
||||
|
||||
return new OperationResult<bool>
|
||||
{
|
||||
IsSuccess = result,
|
||||
|
|
|
@ -5,21 +5,14 @@ using System.Threading.Tasks;
|
|||
|
||||
namespace Roadie.Library.MetaData.MusicBrainz
|
||||
{
|
||||
public interface IMusicBrainzProvider
|
||||
public interface IMusicBrainzProvider : IArtistSearchEngine, IReleaseSearchEngine
|
||||
{
|
||||
bool IsEnabled { get; }
|
||||
|
||||
Task<CoverArtArchivesResult> CoverArtForMusicBrainzReleaseById(string musicBrainzId);
|
||||
|
||||
Task<Release> MusicBrainzReleaseById(string musicBrainzId);
|
||||
|
||||
Task<IEnumerable<AudioMetaData>> MusicBrainzReleaseTracks(string artistName, string releaseTitle);
|
||||
|
||||
Task<OperationResult<IEnumerable<ArtistSearchResult>>> PerformArtistSearch(string query, int resultsCount);
|
||||
|
||||
Task<OperationResult<IEnumerable<ReleaseSearchResult>>> PerformReleaseSearch(string artistName, string query,
|
||||
int resultsCount);
|
||||
|
||||
Task<Data.Release> ReleaseForMusicBrainzReleaseById(string musicBrainzId);
|
||||
|
||||
Task<IEnumerable<Release>> ReleasesForArtist(string artist, string artistMusicBrainzId = null);
|
||||
|
|
|
@ -15,20 +15,18 @@ using System.Threading.Tasks;
|
|||
|
||||
namespace Roadie.Library.MetaData.MusicBrainz
|
||||
{
|
||||
public class MusicBrainzProvider : MetaDataProviderBase, IArtistSearchEngine, IReleaseSearchEngine,
|
||||
IMusicBrainzProvider
|
||||
public class MusicBrainzProvider : MetaDataProviderBase, IMusicBrainzProvider
|
||||
{
|
||||
public override bool IsEnabled => Configuration.Integrations.MusicBrainzProviderEnabled;
|
||||
|
||||
public MusicBrainzProvider(IRoadieSettings configuration, ICacheManager cacheManager, ILogger logger)
|
||||
public MusicBrainzProvider(IRoadieSettings configuration, ICacheManager cacheManager, ILogger<MusicBrainzProvider> logger)
|
||||
: base(configuration, cacheManager, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public async Task<CoverArtArchivesResult> CoverArtForMusicBrainzReleaseById(string musicBrainzId)
|
||||
{
|
||||
return await MusicBrainzRequestHelper.GetAsync<CoverArtArchivesResult>(
|
||||
MusicBrainzRequestHelper.CreateCoverArtReleaseUrl(musicBrainzId));
|
||||
return await MusicBrainzRequestHelper.GetAsync<CoverArtArchivesResult>(MusicBrainzRequestHelper.CreateCoverArtReleaseUrl(musicBrainzId));
|
||||
}
|
||||
|
||||
public async Task<Release> MusicBrainzReleaseById(string musicBrainzId)
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Roadie.Library.SearchEngines.MetaData.Spotify
|
||||
{
|
||||
public interface ISpotifyHelper : IArtistSearchEngine, IReleaseSearchEngine
|
||||
{
|
||||
}
|
||||
}
|
|
@ -14,11 +14,11 @@ using System.Threading.Tasks;
|
|||
|
||||
namespace Roadie.Library.SearchEngines.MetaData.Spotify
|
||||
{
|
||||
public class SpotifyHelper : MetaDataProviderBase, IArtistSearchEngine, IReleaseSearchEngine
|
||||
public class SpotifyHelper : MetaDataProviderBase, ISpotifyHelper
|
||||
{
|
||||
public override bool IsEnabled => Configuration.Integrations.SpotifyProviderEnabled;
|
||||
|
||||
public SpotifyHelper(IRoadieSettings configuration, ICacheManager cacheManager, ILogger logger)
|
||||
public SpotifyHelper(IRoadieSettings configuration, ICacheManager cacheManager, ILogger<SpotifyHelper> logger)
|
||||
: base(configuration, cacheManager, logger)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Roadie.Library.SearchEngines.MetaData.Wikipedia
|
||||
{
|
||||
public interface IWikipediaHelper : IArtistSearchEngine, IReleaseSearchEngine
|
||||
{
|
||||
}
|
||||
}
|
|
@ -10,11 +10,11 @@ using System.Threading.Tasks;
|
|||
|
||||
namespace Roadie.Library.SearchEngines.MetaData.Wikipedia
|
||||
{
|
||||
public class WikipediaHelper : MetaDataProviderBase, IArtistSearchEngine, IReleaseSearchEngine
|
||||
public class WikipediaHelper : MetaDataProviderBase, IWikipediaHelper
|
||||
{
|
||||
private IHttpEncoder HttpEncoder { get; }
|
||||
|
||||
public WikipediaHelper(IRoadieSettings configuration, ICacheManager cacheManager, ILogger logger,
|
||||
public WikipediaHelper(IRoadieSettings configuration, ICacheManager cacheManager, ILogger<WikipediaHelper> logger,
|
||||
IHttpEncoder httpEncoder)
|
||||
: base(configuration, cacheManager, logger)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace Roadie.Library.SearchEngines.MetaData.iTunes
|
||||
{
|
||||
public interface IiTunesHelper : IArtistSearchEngine, IReleaseSearchEngine
|
||||
{
|
||||
}
|
||||
}
|
|
@ -3,31 +3,27 @@ using Roadie.Library.Caching;
|
|||
using Roadie.Library.Configuration;
|
||||
using Roadie.Library.MetaData;
|
||||
using Roadie.Library.SearchEngines.Imaging;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Roadie.Library.SearchEngines.MetaData.iTunes
|
||||
{
|
||||
public class iTunesHelper : MetaDataProviderBase
|
||||
public class iTunesHelper : MetaDataProviderBase, IiTunesHelper
|
||||
{
|
||||
private readonly ITunesSearchEngine _iTunesSearchEngine;
|
||||
|
||||
public override bool IsEnabled => Configuration.Integrations.ITunesProviderEnabled;
|
||||
|
||||
public iTunesHelper(IRoadieSettings configuration, ICacheManager cacheManager, ILogger logger)
|
||||
public iTunesHelper(IRoadieSettings configuration, ICacheManager cacheManager, ILogger<iTunesHelper> logger,
|
||||
ITunesSearchEngine iTunesSearchEngine)
|
||||
: base(configuration, cacheManager, logger)
|
||||
{
|
||||
_iTunesSearchEngine = new ITunesSearchEngine(configuration, cacheManager, logger);
|
||||
_iTunesSearchEngine = iTunesSearchEngine;
|
||||
}
|
||||
|
||||
public async Task<OperationResult<ArtistSearchResult>> SearchForArtist(string artistName)
|
||||
{
|
||||
var r = await _iTunesSearchEngine.PerformArtistSearch(artistName, 1);
|
||||
return new OperationResult<ArtistSearchResult>
|
||||
{
|
||||
Data = r.Data != null ? r.Data.First() : null,
|
||||
IsSuccess = r.Data != null
|
||||
};
|
||||
}
|
||||
public async Task<OperationResult<IEnumerable<ArtistSearchResult>>> PerformArtistSearch(string query, int resultsCount) => await _iTunesSearchEngine.PerformArtistSearch(query, resultsCount);
|
||||
|
||||
public async Task<OperationResult<IEnumerable<ReleaseSearchResult>>> PerformReleaseSearch(string artistName, string query, int resultsCount) => await _iTunesSearchEngine.PerformReleaseSearch(artistName, query, resultsCount);
|
||||
}
|
||||
}
|
|
@ -15,19 +15,15 @@ namespace Roadie.Library.Utility
|
|||
public static class FolderPathHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Full path to Artist folder using destinationFolder as folder Root
|
||||
/// Full path to Artist folder
|
||||
/// </summary>
|
||||
/// <param name="artistSortName">Sort name of Artist to use for folder name</param>
|
||||
/// <param name="destinationFolder">Optional Root folder defaults to Library Folder from Settings</param>
|
||||
public static string ArtistPath(IRoadieSettings configuration, string artistSortName,
|
||||
string destinationFolder = null)
|
||||
public static string ArtistPath(IRoadieSettings configuration, string artistSortName)
|
||||
{
|
||||
SimpleContract.Requires<ArgumentException>(!string.IsNullOrEmpty(artistSortName),
|
||||
"Invalid Artist Sort Name");
|
||||
SimpleContract.Requires<ArgumentException>(!string.IsNullOrEmpty(artistSortName),"Invalid Artist Sort Name");
|
||||
|
||||
var artistFolder = artistSortName.ToTitleCase(false);
|
||||
destinationFolder = destinationFolder ?? configuration.LibraryFolder;
|
||||
var directoryInfo = new DirectoryInfo(Path.Combine(destinationFolder, artistFolder.ToFolderNameFriendly()));
|
||||
var directoryInfo = new DirectoryInfo(Path.Combine(configuration.LibraryFolder, artistFolder.ToFolderNameFriendly()));
|
||||
return directoryInfo.FullName;
|
||||
}
|
||||
|
||||
|
@ -63,15 +59,41 @@ namespace Roadie.Library.Utility
|
|||
/// <returns></returns>
|
||||
public static bool DeleteEmptyFolders(DirectoryInfo processingFolder)
|
||||
{
|
||||
if (processingFolder == null || !processingFolder.Exists) return true;
|
||||
foreach (var folder in processingFolder.GetDirectories("*.*", SearchOption.AllDirectories))
|
||||
if (folder.Exists)
|
||||
if (!folder.GetFiles("*.*", SearchOption.AllDirectories).Any())
|
||||
if (processingFolder == null || !processingFolder.Exists)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
try
|
||||
{
|
||||
foreach (var folder in processingFolder.GetDirectories("*.*", SearchOption.AllDirectories))
|
||||
{
|
||||
try
|
||||
{
|
||||
folder.Delete(true);
|
||||
Trace.WriteLine(string.Format("Deleting Empty Folder [{0}]", folder.FullName), "Debug");
|
||||
if (folder.Exists)
|
||||
{
|
||||
if (!folder.GetFiles("*.*", SearchOption.AllDirectories).Any())
|
||||
{
|
||||
folder.Delete(true);
|
||||
Trace.WriteLine(string.Format("Deleting Empty Folder [{0}]", folder.FullName), "Debug");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
catch (DirectoryNotFoundException)
|
||||
{
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(DirectoryNotFoundException)
|
||||
{
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -86,7 +108,7 @@ namespace Roadie.Library.Utility
|
|||
{
|
||||
destinationFolder = destinationFolder ?? configuration.LibraryFolder;
|
||||
SimpleContract.Requires<ArgumentException>(artist != null, "Invalid Artist");
|
||||
return DeleteEmptyFolders(new DirectoryInfo(artist.ArtistFileFolder(configuration, destinationFolder)));
|
||||
return DeleteEmptyFolders(new DirectoryInfo(artist.ArtistFileFolder(configuration)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -94,11 +116,10 @@ namespace Roadie.Library.Utility
|
|||
/// </summary>
|
||||
/// <param name="track">Populate track database record</param>
|
||||
/// <param name="destinationFolder">Optional Root folder defaults to Library Folder from Settings</param>
|
||||
public static string PathForTrack(IRoadieSettings configuration, Track track, string destinationFolder = null)
|
||||
public static string PathForTrack(IRoadieSettings configuration, Track track)
|
||||
{
|
||||
destinationFolder = destinationFolder ?? configuration.LibraryFolder;
|
||||
if (string.IsNullOrEmpty(track.FilePath) || string.IsNullOrEmpty(track.FileName)) return null;
|
||||
var directoryInfo = new DirectoryInfo(Path.Combine(destinationFolder, track.FilePath, track.FileName));
|
||||
var directoryInfo = new DirectoryInfo(Path.Combine(configuration.LibraryFolder, track.FilePath, track.FileName));
|
||||
return directoryInfo.FullName;
|
||||
}
|
||||
|
||||
|
@ -235,7 +256,7 @@ namespace Roadie.Library.Utility
|
|||
string artistFolder = null, string releaseFolder = null)
|
||||
{
|
||||
destinationFolder = destinationFolder ?? configuration.LibraryFolder;
|
||||
artistFolder = artistFolder ?? ArtistPath(configuration, artistSortName, destinationFolder);
|
||||
artistFolder = artistFolder ?? ArtistPath(configuration, artistSortName);
|
||||
releaseFolder = releaseFolder ?? ReleasePath(artistFolder, releaseTitle, releaseDate);
|
||||
var trackFileName = TrackFileName(configuration, trackTitle, trackNumber, discNumber, totalTrackNumber,
|
||||
fileExtension);
|
||||
|
|
|
@ -12,17 +12,23 @@ namespace Roadie.Library.Utility
|
|||
{
|
||||
public static class WebHelper
|
||||
{
|
||||
public const string UserAgent =
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36";
|
||||
public const string UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36";
|
||||
|
||||
public static byte[] BytesForImageUrl(string url)
|
||||
{
|
||||
if (string.IsNullOrEmpty(url)) return null;
|
||||
try
|
||||
{
|
||||
using (var webClient = new WebClient())
|
||||
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
|
||||
request.Referer = "http://www.roadie.rocks";
|
||||
request.UserAgent = UserAgent;
|
||||
|
||||
using (var response = (HttpWebResponse)request.GetResponse())
|
||||
{
|
||||
return webClient.DownloadData(url);
|
||||
using (BinaryReader reader = new BinaryReader(response.GetResponseStream()))
|
||||
{
|
||||
return reader.ReadBytes(1 * 1024 * 1024 * 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (WebException wex)
|
||||
|
|
|
@ -10,7 +10,6 @@ using Roadie.Library.Encoding;
|
|||
using Roadie.Library.Engines;
|
||||
using Roadie.Library.Enums;
|
||||
using Roadie.Library.Extensions;
|
||||
using Roadie.Library.Factories;
|
||||
using Roadie.Library.Identity;
|
||||
using Roadie.Library.Imaging;
|
||||
using Roadie.Library.MetaData.Audio;
|
||||
|
@ -35,8 +34,6 @@ namespace Roadie.Api.Services
|
|||
{
|
||||
protected IHubContext<ScanActivityHub> ScanActivityHub { get; }
|
||||
|
||||
private IArtistFactory ArtistFactory { get; }
|
||||
|
||||
private IArtistLookupEngine ArtistLookupEngine { get; }
|
||||
|
||||
private IAudioMetaDataHelper AudioMetaDataHelper { get; }
|
||||
|
@ -47,10 +44,6 @@ namespace Roadie.Api.Services
|
|||
|
||||
private IID3TagsHelper ID3TagsHelper { get; }
|
||||
|
||||
private IImageFactory ImageFactory { get; }
|
||||
|
||||
private ILabelFactory LabelFactory { get; }
|
||||
|
||||
private ILabelLookupEngine LabelLookupEngine { get; }
|
||||
|
||||
private ILastFmHelper LastFmHelper { get; }
|
||||
|
@ -59,45 +52,53 @@ namespace Roadie.Api.Services
|
|||
|
||||
private IMusicBrainzProvider MusicBrainzProvider { get; }
|
||||
|
||||
private IReleaseFactory ReleaseFactory { get; }
|
||||
|
||||
private IReleaseLookupEngine ReleaseLookupEngine { get; }
|
||||
|
||||
private IArtistService ArtistService { get; }
|
||||
private IReleaseService ReleaseService { get; }
|
||||
|
||||
private IFileDirectoryProcessorService FileDirectoryProcessorService { get; }
|
||||
|
||||
public AdminService(IRoadieSettings configuration,
|
||||
IHttpEncoder httpEncoder,
|
||||
IHttpContext httpContext,
|
||||
data.IRoadieDbContext context,
|
||||
ICacheManager cacheManager,
|
||||
ILogger<ArtistService> logger,
|
||||
IHubContext<ScanActivityHub> scanActivityHub
|
||||
IHubContext<ScanActivityHub> scanActivityHub,
|
||||
IMusicBrainzProvider musicbrainzProvider,
|
||||
ILastFmHelper lastFmHelper,
|
||||
IFileNameHelper fileNameHelper,
|
||||
IID3TagsHelper iD3TagsHelper,
|
||||
IAudioMetaDataHelper audioMetaDataHelper,
|
||||
IArtistService artistService,
|
||||
IReleaseService releaseService,
|
||||
IArtistLookupEngine artistLookupEngine,
|
||||
IReleaseLookupEngine releaseLookupEngine,
|
||||
ILabelLookupEngine labelLookupEngine,
|
||||
IFileDirectoryProcessorService fileDirectoryProcessorService
|
||||
)
|
||||
: base(configuration, httpEncoder, context, cacheManager, logger, httpContext)
|
||||
{
|
||||
ScanActivityHub = scanActivityHub;
|
||||
EventMessageLogger = new EventMessageLogger();
|
||||
EventMessageLogger = new EventMessageLogger<AdminService>();
|
||||
EventMessageLogger.Messages += EventMessageLogger_Messages;
|
||||
|
||||
MusicBrainzProvider = new MusicBrainzProvider(configuration, cacheManager, MessageLogger);
|
||||
LastFmHelper = new LastFmHelper(configuration, cacheManager, MessageLogger, context, httpEncoder);
|
||||
FileNameHelper = new FileNameHelper(configuration, cacheManager, MessageLogger);
|
||||
ID3TagsHelper = new ID3TagsHelper(configuration, cacheManager, MessageLogger);
|
||||
MusicBrainzProvider = musicbrainzProvider;
|
||||
LastFmHelper = lastFmHelper;
|
||||
FileNameHelper = fileNameHelper;
|
||||
ID3TagsHelper = iD3TagsHelper;
|
||||
|
||||
ArtistLookupEngine = artistLookupEngine;
|
||||
ReleaseLookupEngine = releaseLookupEngine;
|
||||
LabelLookupEngine = labelLookupEngine;
|
||||
|
||||
AudioMetaDataHelper = audioMetaDataHelper;
|
||||
|
||||
ArtistService = artistService;
|
||||
ReleaseService = releaseService;
|
||||
FileDirectoryProcessorService = fileDirectoryProcessorService;
|
||||
|
||||
ArtistLookupEngine =
|
||||
new ArtistLookupEngine(configuration, httpEncoder, context, cacheManager, MessageLogger);
|
||||
LabelLookupEngine = new LabelLookupEngine(configuration, httpEncoder, context, cacheManager, MessageLogger);
|
||||
ReleaseLookupEngine = new ReleaseLookupEngine(configuration, httpEncoder, context, cacheManager,
|
||||
MessageLogger, ArtistLookupEngine, LabelLookupEngine);
|
||||
ImageFactory = new ImageFactory(configuration, httpEncoder, context, cacheManager, MessageLogger,
|
||||
ArtistLookupEngine, ReleaseLookupEngine);
|
||||
LabelFactory = new LabelFactory(configuration, httpEncoder, context, cacheManager, MessageLogger,
|
||||
ArtistLookupEngine, ReleaseLookupEngine);
|
||||
AudioMetaDataHelper = new AudioMetaDataHelper(configuration, httpEncoder, context, MusicBrainzProvider,
|
||||
LastFmHelper, cacheManager,
|
||||
MessageLogger, ArtistLookupEngine, ImageFactory, FileNameHelper, ID3TagsHelper);
|
||||
ReleaseFactory = new ReleaseFactory(configuration, httpEncoder, context, cacheManager, MessageLogger,
|
||||
ArtistLookupEngine, LabelFactory, AudioMetaDataHelper, ReleaseLookupEngine);
|
||||
ArtistFactory = new ArtistFactory(configuration, httpEncoder, context, cacheManager, MessageLogger,
|
||||
ArtistLookupEngine, ReleaseFactory, ImageFactory, ReleaseLookupEngine, AudioMetaDataHelper);
|
||||
}
|
||||
|
||||
public async Task<OperationResult<bool>> DeleteArtist(ApplicationUser user, Guid artistId)
|
||||
|
@ -114,7 +115,7 @@ namespace Roadie.Api.Services
|
|||
|
||||
try
|
||||
{
|
||||
var result = await ArtistFactory.Delete(artist);
|
||||
var result = await ArtistService.Delete(user, artist);
|
||||
if (!result.IsSuccess)
|
||||
return new OperationResult<bool>
|
||||
{
|
||||
|
@ -154,7 +155,7 @@ namespace Roadie.Api.Services
|
|||
|
||||
try
|
||||
{
|
||||
await ReleaseFactory.DeleteReleases(
|
||||
await ReleaseService.DeleteReleases(user,
|
||||
DbContext.Releases.Where(x => x.ArtistId == artist.Id).Select(x => x.RoadieId).ToArray(),
|
||||
doDeleteFiles);
|
||||
await DbContext.SaveChangesAsync();
|
||||
|
@ -192,7 +193,7 @@ namespace Roadie.Api.Services
|
|||
|
||||
try
|
||||
{
|
||||
var artistFolder = artist.ArtistFileFolder(Configuration, Configuration.LibraryFolder);
|
||||
var artistFolder = artist.ArtistFileFolder(Configuration);
|
||||
var artistImagesInFolder = ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(artistFolder),
|
||||
ImageType.ArtistSecondary, SearchOption.TopDirectoryOnly);
|
||||
var artistImageFilename = artistImagesInFolder.Skip(index).FirstOrDefault();
|
||||
|
@ -235,7 +236,7 @@ namespace Roadie.Api.Services
|
|||
return new OperationResult<bool>(true, $"Release Not Found [{releaseId}]");
|
||||
}
|
||||
|
||||
await ReleaseFactory.Delete(release, doDeleteFiles ?? false);
|
||||
await ReleaseService.Delete(user, release, doDeleteFiles ?? false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -272,8 +273,7 @@ namespace Roadie.Api.Services
|
|||
try
|
||||
{
|
||||
var releaseFolder =
|
||||
release.ReleaseFileFolder(release.Artist.ArtistFileFolder(Configuration,
|
||||
Configuration.LibraryFolder));
|
||||
release.ReleaseFileFolder(release.Artist.ArtistFileFolder(Configuration));
|
||||
var releaseImagesInFolder = ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(releaseFolder),
|
||||
ImageType.ReleaseSecondary, SearchOption.TopDirectoryOnly);
|
||||
var releaseImageFilename = releaseImagesInFolder.Skip(index).FirstOrDefault();
|
||||
|
@ -325,14 +325,14 @@ namespace Roadie.Api.Services
|
|||
string trackPath = null;
|
||||
try
|
||||
{
|
||||
trackPath = track.PathToTrack(Configuration, Configuration.LibraryFolder);
|
||||
trackPath = track.PathToTrack(Configuration);
|
||||
if (File.Exists(trackPath))
|
||||
{
|
||||
File.Delete(trackPath);
|
||||
Logger.LogWarning($"x For Track `{track}`, Deleted File [{trackPath}]");
|
||||
}
|
||||
|
||||
var trackThumbnailName = track.PathToTrackThumbnail(Configuration, Configuration.LibraryFolder);
|
||||
var trackThumbnailName = track.PathToTrackThumbnail(Configuration);
|
||||
if (File.Exists(trackThumbnailName))
|
||||
{
|
||||
File.Delete(trackThumbnailName);
|
||||
|
@ -347,8 +347,7 @@ namespace Roadie.Api.Services
|
|||
}
|
||||
}
|
||||
|
||||
await ReleaseFactory.ScanReleaseFolder(track.ReleaseMedia.Release.RoadieId, Configuration.LibraryFolder,
|
||||
false, track.ReleaseMedia.Release);
|
||||
await ReleaseService.ScanReleaseFolder(user, track.ReleaseMedia.Release.RoadieId, false, track.ReleaseMedia.Release);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -576,9 +575,7 @@ namespace Roadie.Api.Services
|
|||
|
||||
try
|
||||
{
|
||||
var result =
|
||||
await ArtistFactory.ScanArtistReleasesFolders(artist.RoadieId, Configuration.LibraryFolder,
|
||||
isReadOnly);
|
||||
var result = await ArtistService.ScanArtistReleasesFolders(user, artist.RoadieId, Configuration.LibraryFolder, isReadOnly);
|
||||
CacheManager.ClearRegion(artist.CacheRegion);
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -593,7 +590,7 @@ namespace Roadie.Api.Services
|
|||
UserId = user.Id,
|
||||
ForArtistId = artist.Id,
|
||||
NewReleases = ReleaseLookupEngine.AddedReleaseIds.Count(),
|
||||
NewTracks = ReleaseFactory.AddedTrackIds.Count(),
|
||||
NewTracks = ReleaseService.AddedTrackIds.Count(),
|
||||
TimeSpanInSeconds = (int)sw.Elapsed.TotalSeconds
|
||||
});
|
||||
await DbContext.SaveChangesAsync();
|
||||
|
@ -796,14 +793,14 @@ namespace Roadie.Api.Services
|
|||
{
|
||||
var d = new DirectoryInfo(Configuration.InboundFolder);
|
||||
var dest = new DirectoryInfo(Configuration.LibraryFolder);
|
||||
return await ScanFolder(d, dest, user, isReadOnly);
|
||||
return await ScanFolder(user, d, dest, isReadOnly);
|
||||
}
|
||||
|
||||
public async Task<OperationResult<bool>> ScanLibraryFolder(ApplicationUser user, bool isReadOnly = false)
|
||||
{
|
||||
var d = new DirectoryInfo(Configuration.LibraryFolder);
|
||||
var dest = new DirectoryInfo(Configuration.LibraryFolder);
|
||||
return await ScanFolder(d, dest, user, isReadOnly);
|
||||
return await ScanFolder(user, d, dest, isReadOnly);
|
||||
}
|
||||
|
||||
public async Task<OperationResult<bool>> ScanRelease(ApplicationUser user, Guid releaseId,
|
||||
|
@ -825,8 +822,7 @@ namespace Roadie.Api.Services
|
|||
|
||||
try
|
||||
{
|
||||
var result = await ReleaseFactory.ScanReleaseFolder(release.RoadieId, Configuration.LibraryFolder,
|
||||
isReadOnly, release);
|
||||
var result = await ReleaseService.ScanReleaseFolder(user, release.RoadieId, isReadOnly, release);
|
||||
await UpdateReleaseRank(release.Id);
|
||||
CacheManager.ClearRegion(release.CacheRegion);
|
||||
}
|
||||
|
@ -842,7 +838,7 @@ namespace Roadie.Api.Services
|
|||
{
|
||||
UserId = user.Id,
|
||||
ForReleaseId = release.Id,
|
||||
NewTracks = ReleaseFactory.AddedTrackIds.Count(),
|
||||
NewTracks = ReleaseService.AddedTrackIds.Count(),
|
||||
TimeSpanInSeconds = (int)sw.Elapsed.TotalSeconds
|
||||
});
|
||||
await DbContext.SaveChangesAsync();
|
||||
|
@ -891,8 +887,7 @@ namespace Roadie.Api.Services
|
|||
await ScanActivityHub.Clients.All.SendAsync("SendSystemActivity", message);
|
||||
}
|
||||
|
||||
private async Task<OperationResult<bool>> ScanFolder(DirectoryInfo d, DirectoryInfo dest, ApplicationUser user,
|
||||
bool isReadOnly)
|
||||
private async Task<OperationResult<bool>> ScanFolder(ApplicationUser user, DirectoryInfo d, DirectoryInfo dest, bool isReadOnly)
|
||||
{
|
||||
var sw = new Stopwatch();
|
||||
sw.Start();
|
||||
|
@ -901,43 +896,28 @@ namespace Roadie.Api.Services
|
|||
await LogAndPublish($"** Processing Folder: [{d.FullName}]");
|
||||
|
||||
long processedFolders = 0;
|
||||
var folderProcessor = new FolderProcessor(Configuration, HttpEncoder, Configuration.LibraryFolder,
|
||||
DbContext, CacheManager, MessageLogger, ArtistLookupEngine, ArtistFactory, ReleaseFactory, ImageFactory,
|
||||
ReleaseLookupEngine, AudioMetaDataHelper);
|
||||
|
||||
var newArtists = 0;
|
||||
var newReleases = 0;
|
||||
var newTracks = 0;
|
||||
OperationResult<bool> result = null;
|
||||
foreach (var folder in Directory.EnumerateDirectories(d.FullName).ToArray())
|
||||
{
|
||||
result = await folderProcessor.Process(new DirectoryInfo(folder), isReadOnly);
|
||||
// Between folders flush cache, the caching for folder processing was intended for caching artist metadata lookups. Most of the time artists are in the same folder.
|
||||
await FileDirectoryProcessorService.Process(user, new DirectoryInfo(folder), isReadOnly);
|
||||
// Between folders flush cache, the caching for folder processing was intended for caching artist metadata lookups. Most of the time artists releases are in the same folder.
|
||||
CacheManager.Clear();
|
||||
processedFolders++;
|
||||
}
|
||||
|
||||
if (result.AdditionalData != null)
|
||||
if (!isReadOnly)
|
||||
{
|
||||
newArtists = SafeParser.ToNumber<int>(result.AdditionalData["newArtists"]);
|
||||
newReleases = SafeParser.ToNumber<int>(result.AdditionalData["newReleases"]);
|
||||
newTracks = SafeParser.ToNumber<int>(result.AdditionalData["newTracks"]);
|
||||
Services.FileDirectoryProcessorService.DeleteEmptyFolders(d, Logger);
|
||||
}
|
||||
|
||||
if (!isReadOnly) FolderProcessor.DeleteEmptyFolders(d, Logger);
|
||||
sw.Stop();
|
||||
DbContext.ScanHistories.Add(new data.ScanHistory
|
||||
var newScanHistory = new data.ScanHistory
|
||||
{
|
||||
UserId = user.Id,
|
||||
NewArtists = newArtists,
|
||||
NewReleases = newReleases,
|
||||
NewTracks = newTracks,
|
||||
NewArtists = FileDirectoryProcessorService.AddedArtistIds.Count(),
|
||||
NewReleases = FileDirectoryProcessorService.AddedReleaseIds.Count(),
|
||||
NewTracks = FileDirectoryProcessorService.AddedTrackIds.Count(),
|
||||
TimeSpanInSeconds = (int)sw.Elapsed.TotalSeconds
|
||||
});
|
||||
};
|
||||
DbContext.ScanHistories.Add(newScanHistory);
|
||||
await DbContext.SaveChangesAsync();
|
||||
CacheManager.Clear();
|
||||
await LogAndPublish(
|
||||
$"**Completed!Processed Folders[{processedFolders}], Processed Files[{processedFiles}] : Elapsed Time[{sw.Elapsed}]");
|
||||
await LogAndPublish($"** Completed! Processed Folders [{processedFolders}], Processed Files [{processedFiles}], New Artists [{ newScanHistory.NewArtists }], New Releases [{ newScanHistory.NewReleases }], New Tracks [{ newScanHistory.NewTracks }] : Elapsed Time [{sw.Elapsed}]");
|
||||
return new OperationResult<bool>
|
||||
{
|
||||
Data = true,
|
||||
|
|
|
@ -10,7 +10,7 @@ using Roadie.Library.Encoding;
|
|||
using Roadie.Library.Engines;
|
||||
using Roadie.Library.Enums;
|
||||
using Roadie.Library.Extensions;
|
||||
using Roadie.Library.Factories;
|
||||
using Roadie.Library.Identity;
|
||||
using Roadie.Library.Imaging;
|
||||
using Roadie.Library.MetaData.Audio;
|
||||
using Roadie.Library.MetaData.FileName;
|
||||
|
@ -37,8 +37,6 @@ namespace Roadie.Api.Services
|
|||
{
|
||||
public class ArtistService : ServiceBase, IArtistService
|
||||
{
|
||||
private IArtistFactory ArtistFactory { get; }
|
||||
|
||||
private IArtistLookupEngine ArtistLookupEngine { get; }
|
||||
|
||||
private IAudioMetaDataHelper AudioMetaDataHelper { get; }
|
||||
|
@ -51,10 +49,6 @@ namespace Roadie.Api.Services
|
|||
|
||||
private IID3TagsHelper ID3TagsHelper { get; }
|
||||
|
||||
private IImageFactory ImageFactory { get; }
|
||||
|
||||
private ILabelFactory LabelFactory { get; }
|
||||
|
||||
private ILabelLookupEngine LabelLookupEngine { get; }
|
||||
|
||||
private ILastFmHelper LastFmHelper { get; }
|
||||
|
@ -63,19 +57,31 @@ namespace Roadie.Api.Services
|
|||
|
||||
private IPlaylistService PlaylistService { get; }
|
||||
|
||||
private IReleaseFactory ReleaseFactory { get; }
|
||||
|
||||
private IReleaseLookupEngine ReleaseLookupEngine { get; }
|
||||
|
||||
public ArtistService(IRoadieSettings configuration,
|
||||
IHttpEncoder httpEncoder,
|
||||
private IReleaseService ReleaseService { get; }
|
||||
|
||||
private IFileDirectoryProcessorService FileDirectoryProcessorService { get; }
|
||||
|
||||
public ArtistService(IRoadieSettings configuration,
|
||||
IHttpEncoder httpEncoder,
|
||||
IHttpContext httpContext,
|
||||
data.IRoadieDbContext dbContext,
|
||||
ICacheManager cacheManager,
|
||||
ILogger<ArtistService> logger,
|
||||
ICollectionService collectionService,
|
||||
IPlaylistService playlistService,
|
||||
IBookmarkService bookmarkService
|
||||
IBookmarkService bookmarkService,
|
||||
IReleaseService releaseService,
|
||||
IArtistLookupEngine artistLookupEngine,
|
||||
mb.IMusicBrainzProvider musicBrainzProvider,
|
||||
ILastFmHelper lastFmHelper,
|
||||
IFileNameHelper fileNameHelper,
|
||||
IID3TagsHelper id3tagsHelper,
|
||||
IAudioMetaDataHelper audioMetaDataHelper,
|
||||
IReleaseLookupEngine releaseLookupEngine,
|
||||
ILabelLookupEngine labelLookupEngine,
|
||||
IFileDirectoryProcessorService fileDirectoryProcessorService
|
||||
)
|
||||
: base(configuration, httpEncoder, dbContext, cacheManager, logger, httpContext)
|
||||
{
|
||||
|
@ -83,26 +89,19 @@ namespace Roadie.Api.Services
|
|||
PlaylistService = playlistService;
|
||||
BookmarkService = bookmarkService;
|
||||
|
||||
MusicBrainzProvider = new mb.MusicBrainzProvider(configuration, cacheManager, logger);
|
||||
LastFmHelper = new LastFmHelper(configuration, cacheManager, logger, dbContext, httpEncoder);
|
||||
FileNameHelper = new FileNameHelper(configuration, cacheManager, logger);
|
||||
ID3TagsHelper = new ID3TagsHelper(configuration, cacheManager, logger);
|
||||
ArtistLookupEngine = new ArtistLookupEngine(configuration, httpEncoder, dbContext, cacheManager, logger);
|
||||
LabelLookupEngine = new LabelLookupEngine(configuration, httpEncoder, dbContext, cacheManager, logger);
|
||||
ReleaseLookupEngine = new ReleaseLookupEngine(configuration, httpEncoder, dbContext, cacheManager, logger,
|
||||
ArtistLookupEngine, LabelLookupEngine);
|
||||
ImageFactory = new ImageFactory(configuration, httpEncoder, dbContext, cacheManager, logger,
|
||||
ArtistLookupEngine, ReleaseLookupEngine);
|
||||
LabelFactory = new LabelFactory(configuration, httpEncoder, dbContext, cacheManager, logger,
|
||||
ArtistLookupEngine, ReleaseLookupEngine);
|
||||
AudioMetaDataHelper = new AudioMetaDataHelper(configuration, httpEncoder, dbContext, MusicBrainzProvider,
|
||||
LastFmHelper, cacheManager,
|
||||
logger, ArtistLookupEngine, ImageFactory, FileNameHelper, ID3TagsHelper);
|
||||
MusicBrainzProvider = musicBrainzProvider;
|
||||
LastFmHelper = lastFmHelper;
|
||||
FileNameHelper = fileNameHelper;
|
||||
ID3TagsHelper = id3tagsHelper;
|
||||
|
||||
ReleaseFactory = new ReleaseFactory(configuration, httpEncoder, dbContext, cacheManager, logger,
|
||||
ArtistLookupEngine, LabelFactory, AudioMetaDataHelper, ReleaseLookupEngine);
|
||||
ArtistFactory = new ArtistFactory(configuration, httpEncoder, dbContext, cacheManager, logger,
|
||||
ArtistLookupEngine, ReleaseFactory, ImageFactory, ReleaseLookupEngine, AudioMetaDataHelper);
|
||||
ArtistLookupEngine = artistLookupEngine;
|
||||
LabelLookupEngine = labelLookupEngine;
|
||||
ReleaseLookupEngine = releaseLookupEngine;
|
||||
|
||||
AudioMetaDataHelper = audioMetaDataHelper;
|
||||
|
||||
ReleaseService = releaseService;
|
||||
FileDirectoryProcessorService = fileDirectoryProcessorService;
|
||||
}
|
||||
|
||||
public async Task<OperationResult<Artist>> ById(User roadieUser, Guid id, IEnumerable<string> includes)
|
||||
|
@ -184,8 +183,61 @@ namespace Roadie.Api.Services
|
|||
};
|
||||
}
|
||||
|
||||
public async Task<OperationResult<bool>> Delete(ApplicationUser user, data.Artist Artist)
|
||||
{
|
||||
var isSuccess = false;
|
||||
try
|
||||
{
|
||||
if (Artist != null)
|
||||
{
|
||||
DbContext.Artists.Remove(Artist);
|
||||
await DbContext.SaveChangesAsync();
|
||||
CacheManager.ClearRegion(Artist.CacheRegion);
|
||||
Logger.LogInformation(string.Format("x DeleteArtist [{0}]", Artist.Id));
|
||||
isSuccess = true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex, ex.Serialize());
|
||||
return new OperationResult<bool>
|
||||
{
|
||||
Errors = new Exception[1] { ex }
|
||||
};
|
||||
}
|
||||
|
||||
return new OperationResult<bool>
|
||||
{
|
||||
IsSuccess = isSuccess,
|
||||
Data = isSuccess
|
||||
};
|
||||
}
|
||||
|
||||
private OperationResult<data.Artist> GetByExternalIds(string musicBrainzId = null, string iTunesId = null, string amgId = null, string spotifyId = null)
|
||||
{
|
||||
var sw = new Stopwatch();
|
||||
sw.Start();
|
||||
var artist = (from a in DbContext.Artists
|
||||
where a.MusicBrainzId != null && musicBrainzId != null && a.MusicBrainzId == musicBrainzId ||
|
||||
a.ITunesId != null || iTunesId != null && a.ITunesId == iTunesId || a.AmgId != null ||
|
||||
amgId != null && a.AmgId == amgId || a.SpotifyId != null ||
|
||||
spotifyId != null && a.SpotifyId == spotifyId
|
||||
select a).FirstOrDefault();
|
||||
sw.Stop();
|
||||
if (artist == null || !artist.IsValid)
|
||||
Logger.LogTrace(
|
||||
"ArtistFactory: Artist Not Found By External Ids: MusicbrainzId [{0}], iTunesIs [{1}], AmgId [{2}], SpotifyId [{3}]",
|
||||
musicBrainzId, iTunesId, amgId, spotifyId);
|
||||
return new OperationResult<data.Artist>
|
||||
{
|
||||
IsSuccess = artist != null,
|
||||
OperationTime = sw.ElapsedMilliseconds,
|
||||
Data = artist
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<Library.Models.Pagination.PagedResult<ArtistList>> List(User roadieUser, PagedRequest request,
|
||||
bool? doRandomize = false, bool? onlyIncludeWithReleases = true)
|
||||
bool? doRandomize = false, bool? onlyIncludeWithReleases = true)
|
||||
{
|
||||
var sw = new Stopwatch();
|
||||
sw.Start();
|
||||
|
@ -338,7 +390,13 @@ namespace Roadie.Api.Services
|
|||
};
|
||||
}
|
||||
|
||||
public async Task<OperationResult<bool>> MergeArtists(User user, Guid artistToMergeId, Guid artistToMergeIntoId)
|
||||
/// <summary>
|
||||
/// Merge one Artist into another one
|
||||
/// </summary>
|
||||
/// <param name="artistToMerge">The Artist to be merged</param>
|
||||
/// <param name="artistToMergeInto">The Artist to merge into</param>
|
||||
/// <returns></returns>
|
||||
public async Task<OperationResult<bool>> MergeArtists(ApplicationUser user, Guid artistToMergeId, Guid artistToMergeIntoId)
|
||||
{
|
||||
var sw = new Stopwatch();
|
||||
sw.Start();
|
||||
|
@ -366,7 +424,7 @@ namespace Roadie.Api.Services
|
|||
|
||||
try
|
||||
{
|
||||
var result = await ArtistFactory.MergeArtists(artistToMerge, mergeIntoArtist, true);
|
||||
var result = await MergeArtists(user, artistToMerge, mergeIntoArtist);
|
||||
if (!result.IsSuccess)
|
||||
{
|
||||
CacheManager.ClearRegion(artistToMerge.CacheRegion);
|
||||
|
@ -392,12 +450,190 @@ namespace Roadie.Api.Services
|
|||
};
|
||||
}
|
||||
|
||||
public async Task<OperationResult<Image>> SetReleaseImageByUrl(User user, Guid id, string imageUrl)
|
||||
|
||||
async Task<OperationResult<data.Artist>> MergeArtists(ApplicationUser user, data.Artist artistToMerge, data.Artist artistToMergeInto)
|
||||
{
|
||||
SimpleContract.Requires<ArgumentNullException>(artistToMerge != null, "Invalid Artist");
|
||||
SimpleContract.Requires<ArgumentNullException>(artistToMergeInto != null, "Invalid Artist");
|
||||
|
||||
var result = false;
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
var sw = new Stopwatch();
|
||||
sw.Start();
|
||||
|
||||
artistToMergeInto.RealName = artistToMerge.RealName ?? artistToMergeInto.RealName;
|
||||
artistToMergeInto.MusicBrainzId = artistToMerge.MusicBrainzId ?? artistToMergeInto.MusicBrainzId;
|
||||
artistToMergeInto.ITunesId = artistToMerge.ITunesId ?? artistToMergeInto.ITunesId;
|
||||
artistToMergeInto.AmgId = artistToMerge.AmgId ?? artistToMergeInto.AmgId;
|
||||
artistToMergeInto.SpotifyId = artistToMerge.SpotifyId ?? artistToMergeInto.SpotifyId;
|
||||
artistToMergeInto.Thumbnail = artistToMerge.Thumbnail ?? artistToMergeInto.Thumbnail;
|
||||
artistToMergeInto.Profile = artistToMerge.Profile ?? artistToMergeInto.Profile;
|
||||
artistToMergeInto.BirthDate = artistToMerge.BirthDate ?? artistToMergeInto.BirthDate;
|
||||
artistToMergeInto.BeginDate = artistToMerge.BeginDate ?? artistToMergeInto.BeginDate;
|
||||
artistToMergeInto.EndDate = artistToMerge.EndDate ?? artistToMergeInto.EndDate;
|
||||
if (!string.IsNullOrEmpty(artistToMerge.ArtistType) && !artistToMerge.ArtistType.Equals("Other", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
artistToMergeInto.ArtistType = artistToMerge.ArtistType;
|
||||
}
|
||||
artistToMergeInto.BioContext = artistToMerge.BioContext ?? artistToMergeInto.BioContext;
|
||||
artistToMergeInto.DiscogsId = artistToMerge.DiscogsId ?? artistToMergeInto.DiscogsId;
|
||||
artistToMergeInto.Tags = artistToMergeInto.Tags.AddToDelimitedList(artistToMerge.Tags.ToListFromDelimited());
|
||||
var altNames = artistToMerge.AlternateNames.ToListFromDelimited().ToList();
|
||||
altNames.Add(artistToMerge.Name);
|
||||
altNames.Add(artistToMerge.SortName);
|
||||
artistToMergeInto.AlternateNames = artistToMergeInto.AlternateNames.AddToDelimitedList(altNames);
|
||||
artistToMergeInto.URLs = artistToMergeInto.URLs.AddToDelimitedList(artistToMerge.URLs.ToListFromDelimited());
|
||||
artistToMergeInto.ISNI = artistToMergeInto.ISNI.AddToDelimitedList(artistToMerge.ISNI.ToListFromDelimited());
|
||||
artistToMergeInto.LastUpdated = now;
|
||||
|
||||
try
|
||||
{
|
||||
var artistGenres = DbContext.ArtistGenres.Where(x => x.ArtistId == artistToMerge.Id).ToArray();
|
||||
if (artistGenres != null)
|
||||
{
|
||||
foreach (var artistGenre in artistGenres)
|
||||
{
|
||||
artistGenre.ArtistId = artistToMergeInto.Id;
|
||||
}
|
||||
}
|
||||
var artistImages = DbContext.Images.Where(x => x.ArtistId == artistToMerge.Id).ToArray();
|
||||
if (artistImages != null)
|
||||
{
|
||||
foreach (var artistImage in artistImages)
|
||||
{
|
||||
artistImage.ArtistId = artistToMergeInto.Id;
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
foreach (var artistTrack in artistTracks)
|
||||
{
|
||||
artistTrack.ArtistId = artistToMergeInto.Id;
|
||||
}
|
||||
}
|
||||
var artistReleases = DbContext.Releases.Where(x => x.ArtistId == artistToMerge.Id).ToArray();
|
||||
if (artistReleases != null)
|
||||
{
|
||||
foreach (var artistRelease in artistReleases)
|
||||
{
|
||||
// See if there is already a release by the same name for the artist to merge into, if so then merge releases
|
||||
var artistToMergeHasRelease = DbContext.Releases.FirstOrDefault(x => x.ArtistId == artistToMerge.Id && x.Title == artistRelease.Title);
|
||||
if (artistToMergeHasRelease != null)
|
||||
{
|
||||
await ReleaseService.MergeReleases(user, artistRelease, artistToMergeHasRelease, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
artistRelease.ArtistId = artistToMerge.Id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogWarning(ex.ToString());
|
||||
}
|
||||
|
||||
var artistFolder = artistToMerge.ArtistFileFolder(Configuration);
|
||||
foreach (var release in DbContext.Releases.Include("Artist").Where(x => x.ArtistId == artistToMerge.Id).ToArray())
|
||||
{
|
||||
var originalReleaseFolder = release.ReleaseFileFolder(artistFolder);
|
||||
await ReleaseService.UpdateRelease(user, release.Adapt<Release>(), originalReleaseFolder);
|
||||
}
|
||||
await Delete(user, artistToMerge);
|
||||
|
||||
|
||||
result = true;
|
||||
|
||||
sw.Stop();
|
||||
return new OperationResult<data.Artist>
|
||||
{
|
||||
Data = artistToMergeInto,
|
||||
IsSuccess = result,
|
||||
OperationTime = sw.ElapsedMilliseconds
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<OperationResult<bool>> RefreshArtistMetadata(ApplicationUser user, Guid artistId)
|
||||
{
|
||||
SimpleContract.Requires<ArgumentOutOfRangeException>(artistId != Guid.Empty, "Invalid ArtistId");
|
||||
|
||||
var result = true;
|
||||
var resultErrors = new List<Exception>();
|
||||
var sw = new Stopwatch();
|
||||
sw.Start();
|
||||
try
|
||||
{
|
||||
var artist = DbContext.Artists.FirstOrDefault(x => x.RoadieId == artistId);
|
||||
if (artist == null)
|
||||
{
|
||||
Logger.LogWarning("Unable To Find Artist [{0}]", artistId);
|
||||
return new OperationResult<bool>();
|
||||
}
|
||||
|
||||
OperationResult<data.Artist> artistSearch = null;
|
||||
try
|
||||
{
|
||||
artistSearch = await ArtistLookupEngine.PerformMetaDataProvidersArtistSearch(new AudioMetaData
|
||||
{
|
||||
Artist = artist.Name
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex, ex.Serialize());
|
||||
}
|
||||
|
||||
if (artistSearch.IsSuccess)
|
||||
{
|
||||
// Do metadata search for Artist like if new Artist then set some overides and merge
|
||||
var mergeResult = await MergeArtists(user, artistSearch.Data, artist);
|
||||
if (mergeResult.IsSuccess)
|
||||
{
|
||||
artist = mergeResult.Data;
|
||||
await DbContext.SaveChangesAsync();
|
||||
sw.Stop();
|
||||
CacheManager.ClearRegion(artist.CacheRegion);
|
||||
Logger.LogInformation("Scanned RefreshArtistMetadata [{0}], OperationTime [{1}]",
|
||||
artist.ToString(), sw.ElapsedMilliseconds);
|
||||
}
|
||||
else
|
||||
{
|
||||
sw.Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex, ex.Serialize());
|
||||
resultErrors.Add(ex);
|
||||
}
|
||||
|
||||
return new OperationResult<bool>
|
||||
{
|
||||
Data = result,
|
||||
IsSuccess = result,
|
||||
Errors = resultErrors,
|
||||
OperationTime = sw.ElapsedMilliseconds
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<OperationResult<Image>> SetReleaseImageByUrl(ApplicationUser user, Guid id, string imageUrl)
|
||||
{
|
||||
return await SaveImageBytes(user, id, WebHelper.BytesForImageUrl(imageUrl));
|
||||
}
|
||||
|
||||
public async Task<OperationResult<bool>> UpdateArtist(User user, Artist model)
|
||||
public async Task<OperationResult<bool>> UpdateArtist(ApplicationUser user, Artist model)
|
||||
{
|
||||
var didRenameArtist = false;
|
||||
var didChangeThumbnail = false;
|
||||
|
@ -413,7 +649,7 @@ namespace Roadie.Api.Services
|
|||
try
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
var originalArtistFolder = artist.ArtistFileFolder(Configuration, Configuration.LibraryFolder);
|
||||
var originalArtistFolder = artist.ArtistFileFolder(Configuration);
|
||||
var specialArtistName = model.Name.ToAlphanumericName();
|
||||
var alt = new List<string>(model.AlternateNamesList);
|
||||
if (!model.AlternateNamesList.Contains(specialArtistName, StringComparer.OrdinalIgnoreCase))
|
||||
|
@ -440,7 +676,7 @@ namespace Roadie.Api.Services
|
|||
artist.Tags = model.TagsList.ToDelimitedList();
|
||||
artist.URLs = model.URLsList.ToDelimitedList();
|
||||
|
||||
var newArtistFolder = artist.ArtistFileFolder(Configuration, Configuration.LibraryFolder);
|
||||
var newArtistFolder = artist.ArtistFileFolder(Configuration);
|
||||
if (!newArtistFolder.Equals(originalArtistFolder, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
didRenameArtist = true;
|
||||
|
@ -516,8 +752,7 @@ namespace Roadie.Api.Services
|
|||
{
|
||||
ArtistId = artist.Id,
|
||||
GenreId = g.Id,
|
||||
Genre = g
|
||||
|
||||
Genre = g
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -557,7 +792,6 @@ namespace Roadie.Api.Services
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else if (model.AssociatedArtistsTokens == null || !model.AssociatedArtistsTokens.Any())
|
||||
{
|
||||
|
@ -565,7 +799,7 @@ namespace Roadie.Api.Services
|
|||
DbContext.ArtistAssociations.RemoveRange(associatedArtists);
|
||||
}
|
||||
|
||||
if(model.SimilarArtistsTokens != null && model.SimilarArtistsTokens.Any())
|
||||
if (model.SimilarArtistsTokens != null && model.SimilarArtistsTokens.Any())
|
||||
{
|
||||
var similarArtists = DbContext.ArtistSimilar.Include(x => x.SimilarArtist)
|
||||
.Where(x => x.ArtistId == artist.Id).ToList();
|
||||
|
@ -622,7 +856,7 @@ namespace Roadie.Api.Services
|
|||
}
|
||||
}
|
||||
|
||||
await ScanArtistReleasesFolders(artist.RoadieId, Configuration.LibraryFolder, false);
|
||||
await ScanArtistReleasesFolders(user, artist.RoadieId, Configuration.LibraryFolder, false);
|
||||
}
|
||||
|
||||
CacheManager.ClearRegion(artist.CacheRegion);
|
||||
|
@ -646,7 +880,7 @@ namespace Roadie.Api.Services
|
|||
};
|
||||
}
|
||||
|
||||
public async Task<OperationResult<Image>> UploadArtistImage(User user, Guid id, IFormFile file)
|
||||
public async Task<OperationResult<Image>> UploadArtistImage(ApplicationUser user, Guid id, IFormFile file)
|
||||
{
|
||||
var bytes = new byte[0];
|
||||
using (var ms = new MemoryStream())
|
||||
|
@ -786,7 +1020,7 @@ namespace Roadie.Api.Services
|
|||
result.Images = DbContext.Images.Where(x => x.ArtistId == artist.Id)
|
||||
.Select(x => MakeFullsizeImage(x.RoadieId, x.Caption)).ToArray();
|
||||
|
||||
var artistFolder = artist.ArtistFileFolder(Configuration, Configuration.LibraryFolder);
|
||||
var artistFolder = artist.ArtistFileFolder(Configuration);
|
||||
var artistImagesInFolder = ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(artistFolder),
|
||||
ImageType.ArtistSecondary, SearchOption.TopDirectoryOnly);
|
||||
if (artistImagesInFolder.Any())
|
||||
|
@ -858,52 +1092,52 @@ namespace Roadie.Api.Services
|
|||
{
|
||||
tsw.Restart();
|
||||
var similarWithArtists = (from aa in DbContext.ArtistSimilar
|
||||
join a in DbContext.Artists on aa.SimilarArtistId equals a.Id
|
||||
where aa.ArtistId == artist.Id
|
||||
select new ArtistList
|
||||
{
|
||||
DatabaseId = a.Id,
|
||||
Id = a.RoadieId,
|
||||
Artist = new DataToken
|
||||
{
|
||||
Text = a.Name,
|
||||
Value = a.RoadieId.ToString()
|
||||
},
|
||||
Thumbnail = MakeArtistThumbnailImage(a.RoadieId),
|
||||
Rating = a.Rating,
|
||||
Rank = a.Rank,
|
||||
CreatedDate = a.CreatedDate,
|
||||
LastUpdated = a.LastUpdated,
|
||||
LastPlayed = a.LastPlayed,
|
||||
PlayedCount = a.PlayedCount,
|
||||
ReleaseCount = a.ReleaseCount,
|
||||
TrackCount = a.TrackCount,
|
||||
SortName = a.SortName
|
||||
}).ToArray();
|
||||
join a in DbContext.Artists on aa.SimilarArtistId equals a.Id
|
||||
where aa.ArtistId == artist.Id
|
||||
select new ArtistList
|
||||
{
|
||||
DatabaseId = a.Id,
|
||||
Id = a.RoadieId,
|
||||
Artist = new DataToken
|
||||
{
|
||||
Text = a.Name,
|
||||
Value = a.RoadieId.ToString()
|
||||
},
|
||||
Thumbnail = MakeArtistThumbnailImage(a.RoadieId),
|
||||
Rating = a.Rating,
|
||||
Rank = a.Rank,
|
||||
CreatedDate = a.CreatedDate,
|
||||
LastUpdated = a.LastUpdated,
|
||||
LastPlayed = a.LastPlayed,
|
||||
PlayedCount = a.PlayedCount,
|
||||
ReleaseCount = a.ReleaseCount,
|
||||
TrackCount = a.TrackCount,
|
||||
SortName = a.SortName
|
||||
}).ToArray();
|
||||
|
||||
var similarArtists = (from aa in DbContext.ArtistSimilar
|
||||
join a in DbContext.Artists on aa.ArtistId equals a.Id
|
||||
where aa.SimilarArtistId == artist.Id
|
||||
select new ArtistList
|
||||
{
|
||||
DatabaseId = a.Id,
|
||||
Id = a.RoadieId,
|
||||
Artist = new DataToken
|
||||
{
|
||||
Text = a.Name,
|
||||
Value = a.RoadieId.ToString()
|
||||
},
|
||||
Thumbnail = MakeArtistThumbnailImage(a.RoadieId),
|
||||
Rating = a.Rating,
|
||||
Rank = a.Rank,
|
||||
CreatedDate = a.CreatedDate,
|
||||
LastUpdated = a.LastUpdated,
|
||||
LastPlayed = a.LastPlayed,
|
||||
PlayedCount = a.PlayedCount,
|
||||
ReleaseCount = a.ReleaseCount,
|
||||
TrackCount = a.TrackCount,
|
||||
SortName = a.SortName
|
||||
}).ToArray();
|
||||
join a in DbContext.Artists on aa.ArtistId equals a.Id
|
||||
where aa.SimilarArtistId == artist.Id
|
||||
select new ArtistList
|
||||
{
|
||||
DatabaseId = a.Id,
|
||||
Id = a.RoadieId,
|
||||
Artist = new DataToken
|
||||
{
|
||||
Text = a.Name,
|
||||
Value = a.RoadieId.ToString()
|
||||
},
|
||||
Thumbnail = MakeArtistThumbnailImage(a.RoadieId),
|
||||
Rating = a.Rating,
|
||||
Rank = a.Rank,
|
||||
CreatedDate = a.CreatedDate,
|
||||
LastUpdated = a.LastUpdated,
|
||||
LastPlayed = a.LastPlayed,
|
||||
PlayedCount = a.PlayedCount,
|
||||
ReleaseCount = a.ReleaseCount,
|
||||
TrackCount = a.TrackCount,
|
||||
SortName = a.SortName
|
||||
}).ToArray();
|
||||
result.SimilarArtists = similarWithArtists.Union(similarArtists, new ArtistListComparer())
|
||||
.OrderBy(x => x.SortName);
|
||||
result.SimilarArtistsTokens = result.SimilarArtists.Select(x => x.Artist).ToArray();
|
||||
|
@ -1023,7 +1257,7 @@ namespace Roadie.Api.Services
|
|||
};
|
||||
}
|
||||
|
||||
private async Task<OperationResult<Image>> SaveImageBytes(User user, Guid id, byte[] imageBytes)
|
||||
private async Task<OperationResult<Image>> SaveImageBytes(ApplicationUser user, Guid id, byte[] imageBytes)
|
||||
{
|
||||
var sw = new Stopwatch();
|
||||
sw.Start();
|
||||
|
@ -1040,7 +1274,7 @@ namespace Roadie.Api.Services
|
|||
artist.Thumbnail = ImageHelper.ConvertToJpegFormat(artist.Thumbnail);
|
||||
|
||||
// Ensure artist folder exists
|
||||
var artistFolder = artist.ArtistFileFolder(Configuration, Configuration.LibraryFolder);
|
||||
var artistFolder = artist.ArtistFileFolder(Configuration);
|
||||
if (!Directory.Exists(artistFolder))
|
||||
{
|
||||
Directory.CreateDirectory(artistFolder);
|
||||
|
@ -1079,8 +1313,7 @@ namespace Roadie.Api.Services
|
|||
};
|
||||
}
|
||||
|
||||
private async Task<OperationResult<bool>> ScanArtistReleasesFolders(Guid artistId, string destinationFolder,
|
||||
bool doJustInfo)
|
||||
public async Task<OperationResult<bool>> ScanArtistReleasesFolders(ApplicationUser user, Guid artistId, string destinationFolder, bool doJustInfo)
|
||||
{
|
||||
SimpleContract.Requires<ArgumentOutOfRangeException>(artistId != Guid.Empty, "Invalid ArtistId");
|
||||
|
||||
|
@ -1101,15 +1334,14 @@ namespace Roadie.Api.Services
|
|||
}
|
||||
|
||||
var releaseScannedCount = 0;
|
||||
var artistFolder = artist.ArtistFileFolder(Configuration, destinationFolder);
|
||||
var artistFolder = artist.ArtistFileFolder(Configuration);
|
||||
var scannedArtistFolders = new List<string>();
|
||||
// Scan known releases for changes
|
||||
if (artist.Releases != null)
|
||||
foreach (var release in artist.Releases)
|
||||
try
|
||||
{
|
||||
result = result && (await ReleaseFactory.ScanReleaseFolder(Guid.Empty, destinationFolder,
|
||||
doJustInfo, release)).Data;
|
||||
result = result && (await ReleaseService.ScanReleaseFolder(user, Guid.Empty, doJustInfo, release)).Data;
|
||||
releaseScannedCount++;
|
||||
scannedArtistFolders.Add(release.ReleaseFileFolder(artistFolder));
|
||||
}
|
||||
|
@ -1119,17 +1351,18 @@ namespace Roadie.Api.Services
|
|||
}
|
||||
|
||||
// Any folder found in Artist folder not already scanned scan
|
||||
var folderProcessor = new FolderProcessor(Configuration, HttpEncoder, destinationFolder, DbContext,
|
||||
CacheManager, Logger, ArtistLookupEngine, ArtistFactory, ReleaseFactory, ImageFactory,
|
||||
ReleaseLookupEngine, AudioMetaDataHelper);
|
||||
var nonReleaseFolders = from d in Directory.EnumerateDirectories(artistFolder)
|
||||
where !(from r in scannedArtistFolders select r).Contains(d)
|
||||
orderby d
|
||||
select d;
|
||||
foreach (var folder in nonReleaseFolders)
|
||||
await folderProcessor.Process(new DirectoryInfo(folder), doJustInfo);
|
||||
if (!doJustInfo) FolderProcessor.DeleteEmptyFolders(new DirectoryInfo(artistFolder), Logger);
|
||||
|
||||
{
|
||||
await FileDirectoryProcessorService.Process(user, new DirectoryInfo(folder), doJustInfo);
|
||||
}
|
||||
if (!doJustInfo)
|
||||
{
|
||||
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())
|
||||
|
|
165
Roadie.Api.Services/FileDirectoryProcessorService.cs
Normal file
165
Roadie.Api.Services/FileDirectoryProcessorService.cs
Normal file
|
@ -0,0 +1,165 @@
|
|||
using Microsoft.Extensions.Logging;
|
||||
using Roadie.Library;
|
||||
using Roadie.Library.Caching;
|
||||
using Roadie.Library.Configuration;
|
||||
using Roadie.Library.Encoding;
|
||||
using Roadie.Library.Engines;
|
||||
using Roadie.Library.Extensions;
|
||||
using Roadie.Library.FilePlugins;
|
||||
using Roadie.Library.Identity;
|
||||
using Roadie.Library.MetaData.Audio;
|
||||
using Roadie.Library.Processors;
|
||||
using Roadie.Library.Utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Linq.Dynamic.Core;
|
||||
using System.Threading.Tasks;
|
||||
using data = Roadie.Library.Data;
|
||||
|
||||
namespace Roadie.Api.Services
|
||||
{
|
||||
public class FileDirectoryProcessorService : ServiceBase, IFileDirectoryProcessorService
|
||||
{
|
||||
private List<int> _addedArtistIds = new List<int>();
|
||||
private List<int> _addedReleaseIds = new List<int>();
|
||||
private List<int> _addedTrackIds = new List<int>();
|
||||
|
||||
public IEnumerable<int> AddedArtistIds => _addedArtistIds.Distinct();
|
||||
public IEnumerable<int> AddedReleaseIds => _addedReleaseIds.Distinct();
|
||||
public IEnumerable<int> AddedTrackIds => _addedTrackIds.Distinct();
|
||||
|
||||
public int? ProcessLimit { get; set; }
|
||||
|
||||
private IArtistLookupEngine ArtistLookupEngine { get; }
|
||||
|
||||
private IAudioMetaDataHelper AudioMetaDataHelper { get; }
|
||||
|
||||
private IReleaseLookupEngine ReleaseLookupEngine { get; }
|
||||
private IReleaseService ReleaseService { get; }
|
||||
|
||||
private IFileProcessor FileProcessor { get; }
|
||||
|
||||
public FileDirectoryProcessorService(IRoadieSettings configuration,
|
||||
IHttpEncoder httpEncoder,
|
||||
IHttpContext httpContext,
|
||||
data.IRoadieDbContext context,
|
||||
ICacheManager cacheManager,
|
||||
ILogger<FileDirectoryProcessorService> logger,
|
||||
IArtistLookupEngine artistLookupEngine,
|
||||
IFileProcessor fileProcessor,
|
||||
IReleaseLookupEngine releaseLookupEngine,
|
||||
IAudioMetaDataHelper audioMetaDataHelper,
|
||||
IReleaseService releaseService)
|
||||
: base(configuration, httpEncoder, context, cacheManager, logger, httpContext)
|
||||
{
|
||||
ArtistLookupEngine = artistLookupEngine;
|
||||
AudioMetaDataHelper = audioMetaDataHelper;
|
||||
ReleaseLookupEngine = releaseLookupEngine;
|
||||
ReleaseService = releaseService;
|
||||
FileProcessor = fileProcessor;
|
||||
}
|
||||
|
||||
public static OperationResult<bool> DeleteEmptyFolders(DirectoryInfo processingFolder, ILogger logger)
|
||||
{
|
||||
var result = new OperationResult<bool>();
|
||||
try
|
||||
{
|
||||
result.IsSuccess = FolderPathHelper.DeleteEmptyFolders(processingFolder);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, string.Format("Error Deleting Empty Folder [{0}] Error [{1}]", processingFolder.FullName, ex.Serialize()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<OperationResult<bool>> Process(ApplicationUser user, DirectoryInfo folder, bool doJustInfo, int? submissionId = null)
|
||||
{
|
||||
var sw = new Stopwatch();
|
||||
sw.Start();
|
||||
await PreProcessFolder(folder, doJustInfo);
|
||||
var processedFiles = 0;
|
||||
var pluginResultInfos = new List<PluginResultInfo>();
|
||||
var errors = new List<string>();
|
||||
|
||||
_addedArtistIds.Clear();
|
||||
_addedReleaseIds.Clear();
|
||||
_addedTrackIds.Clear();
|
||||
|
||||
FileProcessor.SubmissionId = submissionId;
|
||||
|
||||
foreach (var file in Directory.EnumerateFiles(folder.FullName, "*.*", SearchOption.AllDirectories)
|
||||
.ToArray())
|
||||
{
|
||||
var operation = await FileProcessor.Process(file, doJustInfo);
|
||||
if (operation != null && operation.AdditionalData != null &&
|
||||
operation.AdditionalData.ContainsKey(PluginResultInfo.AdditionalDataKeyPluginResultInfo))
|
||||
{
|
||||
pluginResultInfos.Add(operation.AdditionalData[PluginResultInfo.AdditionalDataKeyPluginResultInfo] as PluginResultInfo);
|
||||
processedFiles++;
|
||||
}
|
||||
if (ProcessLimit.HasValue && processedFiles > ProcessLimit.Value) break;
|
||||
}
|
||||
|
||||
await PostProcessFolder(user, folder, pluginResultInfos, doJustInfo);
|
||||
sw.Stop();
|
||||
_addedArtistIds.AddRange(ArtistLookupEngine.AddedArtistIds);
|
||||
_addedReleaseIds.AddRange(ReleaseLookupEngine.AddedReleaseIds);
|
||||
_addedTrackIds.AddRange(ReleaseLookupEngine.AddedTrackIds);
|
||||
Logger.LogInformation("** Completed! Processed Folder [{0}]: Processed Files [{1}] : Elapsed Time [{2}]", folder.FullName, processedFiles, sw.Elapsed);
|
||||
return new OperationResult<bool>
|
||||
{
|
||||
IsSuccess = !errors.Any(),
|
||||
OperationTime = sw.ElapsedMilliseconds
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform any operations to the given folder and the plugin results after processing
|
||||
/// </summary>
|
||||
private async Task<bool> PostProcessFolder(ApplicationUser user, DirectoryInfo inboundFolder, IEnumerable<PluginResultInfo> pluginResults, bool doJustInfo)
|
||||
{
|
||||
SimpleContract.Requires<ArgumentNullException>(inboundFolder != null, "Invalid InboundFolder");
|
||||
if (pluginResults != null)
|
||||
{
|
||||
foreach (var releasesInfo in pluginResults.GroupBy(x => x.ReleaseId).Select(x => x.First()))
|
||||
{
|
||||
await ReleaseService.ScanReleaseFolder(user, releasesInfo.ReleaseId, doJustInfo);
|
||||
_addedTrackIds.AddRange(ReleaseService.AddedTrackIds);
|
||||
}
|
||||
}
|
||||
if (!doJustInfo)
|
||||
{
|
||||
var fileExtensionsToDelete = Configuration.FileExtensionsToDelete ?? new string[0];
|
||||
if (fileExtensionsToDelete.Any())
|
||||
foreach (var fileInFolder in inboundFolder.GetFiles("*.*", SearchOption.AllDirectories))
|
||||
{
|
||||
if (fileExtensionsToDelete.Any(x => x.Equals(fileInFolder.Extension, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
if (!doJustInfo)
|
||||
{
|
||||
fileInFolder.Delete();
|
||||
Logger.LogInformation("x Deleted File [{0}], Was found in in FileExtensionsToDelete",
|
||||
fileInFolder.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DeleteEmptyFolders(inboundFolder, Logger);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform any operations to the given folder before processing
|
||||
/// </summary>
|
||||
private Task<bool> PreProcessFolder(DirectoryInfo inboundFolder, bool doJustInfo = false)
|
||||
{
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
using Microsoft.AspNetCore.Http;
|
||||
using Roadie.Library;
|
||||
using Roadie.Library.Identity;
|
||||
using Roadie.Library.Models;
|
||||
using Roadie.Library.Models.Pagination;
|
||||
using Roadie.Library.Models.Users;
|
||||
|
@ -11,17 +12,22 @@ namespace Roadie.Api.Services
|
|||
{
|
||||
public interface IArtistService
|
||||
{
|
||||
Task<OperationResult<Artist>> ById(User roadieUser, Guid id, IEnumerable<string> includes);
|
||||
Task<OperationResult<Artist>> ById(User user, Guid id, IEnumerable<string> includes);
|
||||
|
||||
Task<PagedResult<ArtistList>> List(User roadieUser, PagedRequest request, bool? doRandomize = false,
|
||||
bool? onlyIncludeWithReleases = true);
|
||||
Task<OperationResult<bool>> Delete(ApplicationUser user, Library.Data.Artist Artist);
|
||||
|
||||
Task<OperationResult<bool>> MergeArtists(User user, Guid artistToMergeId, Guid artistToMergeIntoId);
|
||||
Task<PagedResult<ArtistList>> List(User user, PagedRequest request, bool? doRandomize = false, bool? onlyIncludeWithReleases = true);
|
||||
|
||||
Task<OperationResult<Image>> SetReleaseImageByUrl(User user, Guid id, string imageUrl);
|
||||
Task<OperationResult<bool>> MergeArtists(ApplicationUser user, Guid artistToMergeId, Guid artistToMergeIntoId);
|
||||
|
||||
Task<OperationResult<bool>> UpdateArtist(User user, Artist artist);
|
||||
Task<OperationResult<bool>> RefreshArtistMetadata(ApplicationUser user, Guid ArtistId);
|
||||
|
||||
Task<OperationResult<Image>> UploadArtistImage(User user, Guid id, IFormFile file);
|
||||
Task<OperationResult<bool>> ScanArtistReleasesFolders(ApplicationUser user, Guid artistId, string destinationFolder, bool doJustInfo);
|
||||
|
||||
Task<OperationResult<Image>> SetReleaseImageByUrl(ApplicationUser user, Guid id, string imageUrl);
|
||||
|
||||
Task<OperationResult<bool>> UpdateArtist(ApplicationUser user, Artist artist);
|
||||
|
||||
Task<OperationResult<Image>> UploadArtistImage(ApplicationUser user, Guid id, IFormFile file);
|
||||
}
|
||||
}
|
19
Roadie.Api.Services/IFileDirectoryProcessorService.cs
Normal file
19
Roadie.Api.Services/IFileDirectoryProcessorService.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Roadie.Library;
|
||||
using Roadie.Library.Identity;
|
||||
|
||||
namespace Roadie.Api.Services
|
||||
{
|
||||
public interface IFileDirectoryProcessorService
|
||||
{
|
||||
IEnumerable<int> AddedArtistIds { get; }
|
||||
IEnumerable<int> AddedReleaseIds { get; }
|
||||
IEnumerable<int> AddedTrackIds { get; }
|
||||
|
||||
int? ProcessLimit { get; set; }
|
||||
|
||||
Task<OperationResult<bool>> Process(ApplicationUser user, DirectoryInfo folder, bool doJustInfo, int? submissionId = null);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
using Microsoft.Net.Http.Headers;
|
||||
using Roadie.Library;
|
||||
using Roadie.Library.Identity;
|
||||
using Roadie.Library.Models;
|
||||
using Roadie.Library.Models.Users;
|
||||
using Roadie.Library.SearchEngines.Imaging;
|
||||
|
@ -11,31 +12,27 @@ namespace Roadie.Api.Services
|
|||
{
|
||||
public interface IImageService
|
||||
{
|
||||
Task<FileOperationResult<Image>>
|
||||
ArtistImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null);
|
||||
string Referrer { get; set; }
|
||||
|
||||
Task<FileOperationResult<Image>> ArtistSecondaryImage(Guid id, int imageId, int? width, int? height,
|
||||
EntityTagHeaderValue etag = null);
|
||||
string RequestIp { get; set; }
|
||||
|
||||
Task<FileOperationResult<Image>> ArtistImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null);
|
||||
|
||||
Task<FileOperationResult<Image>> ArtistSecondaryImage(Guid id, int imageId, int? width, int? height, EntityTagHeaderValue etag = null);
|
||||
|
||||
Task<FileOperationResult<Image>> ById(Guid id, int? width, int? height, EntityTagHeaderValue etag = null);
|
||||
|
||||
Task<FileOperationResult<Image>> CollectionImage(Guid id, int? width, int? height,
|
||||
EntityTagHeaderValue etag = null);
|
||||
Task<FileOperationResult<Image>> CollectionImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null);
|
||||
|
||||
Task<OperationResult<bool>> Delete(User user, Guid id);
|
||||
|
||||
Task<OperationResult<IEnumerable<ImageSearchResult>>> ImageProvidersSearch(string query);
|
||||
Task<OperationResult<bool>> Delete(ApplicationUser user, Guid id);
|
||||
|
||||
Task<FileOperationResult<Image>> LabelImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null);
|
||||
|
||||
Task<FileOperationResult<Image>> PlaylistImage(Guid id, int? width, int? height,
|
||||
EntityTagHeaderValue etag = null);
|
||||
Task<FileOperationResult<Image>> PlaylistImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null);
|
||||
|
||||
Task<FileOperationResult<Image>> ReleaseImage(Guid id, int? width, int? height,
|
||||
EntityTagHeaderValue etag = null);
|
||||
Task<FileOperationResult<Image>> ReleaseImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null);
|
||||
|
||||
Task<FileOperationResult<Image>> ReleaseSecondaryImage(Guid id, int imageId, int? width, int? height,
|
||||
EntityTagHeaderValue etag = null);
|
||||
Task<FileOperationResult<Image>> ReleaseSecondaryImage(Guid id, int imageId, int? width, int? height, EntityTagHeaderValue etag = null);
|
||||
|
||||
Task<OperationResult<IEnumerable<ImageSearchResult>>> Search(string query, int resultsCount = 10);
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using Microsoft.AspNetCore.Http;
|
||||
using Roadie.Library;
|
||||
using Roadie.Library.Identity;
|
||||
using Roadie.Library.Models;
|
||||
using Roadie.Library.Models.Pagination;
|
||||
using Roadie.Library.Models.Releases;
|
||||
|
@ -12,20 +13,28 @@ namespace Roadie.Api.Services
|
|||
{
|
||||
public interface IReleaseService
|
||||
{
|
||||
IEnumerable<int> AddedTrackIds { get; }
|
||||
|
||||
Task<OperationResult<Release>> ById(User roadieUser, Guid id, IEnumerable<string> includes = null);
|
||||
|
||||
Task<PagedResult<ReleaseList>> List(User user, PagedRequest request, bool? doRandomize = false,
|
||||
IEnumerable<string> includes = null);
|
||||
Task<OperationResult<bool>> Delete(ApplicationUser user, Library.Data.Release release, bool doDeleteFiles = false, bool doUpdateArtistCounts = true);
|
||||
|
||||
Task<OperationResult<bool>> MergeReleases(User user, Guid releaseToMergeId, Guid releaseToMergeIntoId,
|
||||
bool addAsMedia);
|
||||
Task<OperationResult<bool>> DeleteReleases(ApplicationUser user, IEnumerable<Guid> releaseIds, bool doDeleteFiles = false);
|
||||
|
||||
Task<PagedResult<ReleaseList>> List(User user, PagedRequest request, bool? doRandomize = false, IEnumerable<string> includes = null);
|
||||
|
||||
Task<OperationResult<bool>> MergeReleases(ApplicationUser user, Guid releaseToMergeId, Guid releaseToMergeIntoId, bool addAsMedia);
|
||||
|
||||
Task<OperationResult<bool>> MergeReleases(ApplicationUser user, Library.Data.Release releaseToMerge, Library.Data.Release releaseToMergeInto, bool addAsMedia);
|
||||
|
||||
Task<FileOperationResult<byte[]>> ReleaseZipped(User roadieUser, Guid id);
|
||||
|
||||
Task<OperationResult<Image>> SetReleaseImageByUrl(User user, Guid id, string imageUrl);
|
||||
Task<OperationResult<bool>> ScanReleaseFolder(ApplicationUser user, Guid releaseId, bool doJustInfo, Library.Data.Release releaseToScan = null);
|
||||
|
||||
Task<OperationResult<bool>> UpdateRelease(User user, Release release);
|
||||
Task<OperationResult<Image>> SetReleaseImageByUrl(ApplicationUser user, Guid id, string imageUrl);
|
||||
|
||||
Task<OperationResult<Image>> UploadReleaseImage(User user, Guid id, IFormFile file);
|
||||
Task<OperationResult<bool>> UpdateRelease(ApplicationUser user, Release release, string originalReleaseFolder = null);
|
||||
|
||||
Task<OperationResult<Image>> UploadReleaseImage(ApplicationUser user, Guid id, IFormFile file);
|
||||
}
|
||||
}
|
|
@ -7,7 +7,7 @@ namespace Roadie.Api.Services
|
|||
{
|
||||
public interface IStatisticsService
|
||||
{
|
||||
OperationResult<LibraryStats> LibraryStatistics();
|
||||
Task<OperationResult<LibraryStats>> LibraryStatistics();
|
||||
|
||||
Task<OperationResult<IEnumerable<DateAndCount>>> ReleasesByDate();
|
||||
}
|
||||
|
|
|
@ -7,8 +7,10 @@ using Roadie.Library.Caching;
|
|||
using Roadie.Library.Configuration;
|
||||
using Roadie.Library.Encoding;
|
||||
using Roadie.Library.Enums;
|
||||
using Roadie.Library.Extensions;
|
||||
using Roadie.Library.Identity;
|
||||
using Roadie.Library.Imaging;
|
||||
using Roadie.Library.MetaData.Audio;
|
||||
using Roadie.Library.Models;
|
||||
using Roadie.Library.Models.Users;
|
||||
using Roadie.Library.SearchEngines.Imaging;
|
||||
|
@ -25,32 +27,29 @@ namespace Roadie.Api.Services
|
|||
{
|
||||
public class ImageService : ServiceBase, IImageService
|
||||
{
|
||||
private IImageSearchEngine BingSearchEngine { get; }
|
||||
|
||||
private IDefaultNotFoundImages DefaultNotFoundImages { get; }
|
||||
|
||||
private IImageSearchEngine ITunesSearchEngine { get; }
|
||||
private IImageSearchManager ImageSearchManager { get; }
|
||||
|
||||
private string Referrer { get; }
|
||||
public string Referrer { get; set; }
|
||||
|
||||
private string RequestIp { get; }
|
||||
public string RequestIp { get; set; }
|
||||
|
||||
public ImageService(IRoadieSettings configuration,
|
||||
IHttpEncoder httpEncoder,
|
||||
IHttpEncoder httpEncoder,
|
||||
IHttpContext httpContext,
|
||||
data.IRoadieDbContext context,
|
||||
ICacheManager cacheManager,
|
||||
ILogger<ImageService> logger,
|
||||
IDefaultNotFoundImages defaultNotFoundImages)
|
||||
IDefaultNotFoundImages defaultNotFoundImages,
|
||||
IImageSearchManager imageSearchManager)
|
||||
: base(configuration, httpEncoder, context, cacheManager, logger, httpContext)
|
||||
{
|
||||
DefaultNotFoundImages = defaultNotFoundImages;
|
||||
BingSearchEngine = new BingImageSearchEngine(configuration, logger, RequestIp, Referrer);
|
||||
ITunesSearchEngine = new ITunesSearchEngine(configuration, cacheManager, logger, RequestIp, Referrer);
|
||||
ImageSearchManager = imageSearchManager;
|
||||
}
|
||||
|
||||
public async Task<FileOperationResult<Image>> ArtistImage(Guid id, int? width, int? height,
|
||||
EntityTagHeaderValue etag = null)
|
||||
public async Task<FileOperationResult<Image>> ArtistImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null)
|
||||
{
|
||||
return await GetImageFileOperation("ArtistImage",
|
||||
data.Artist.CacheRegionUrn(id),
|
||||
|
@ -61,8 +60,7 @@ namespace Roadie.Api.Services
|
|||
etag);
|
||||
}
|
||||
|
||||
public async Task<FileOperationResult<Image>> ArtistSecondaryImage(Guid id, int imageId, int? width,
|
||||
int? height, EntityTagHeaderValue etag = null)
|
||||
public async Task<FileOperationResult<Image>> ArtistSecondaryImage(Guid id, int imageId, int? width, int? height, EntityTagHeaderValue etag = null)
|
||||
{
|
||||
return await GetImageFileOperation($"ArtistSecondaryThumbnail-{imageId}",
|
||||
data.Release.CacheRegionUrn(id),
|
||||
|
@ -73,8 +71,7 @@ namespace Roadie.Api.Services
|
|||
etag);
|
||||
}
|
||||
|
||||
public async Task<FileOperationResult<Image>> ById(Guid id, int? width, int? height,
|
||||
EntityTagHeaderValue etag = null)
|
||||
public async Task<FileOperationResult<Image>> ById(Guid id, int? width, int? height, EntityTagHeaderValue etag = null)
|
||||
{
|
||||
return await GetImageFileOperation("ImageById",
|
||||
data.Image.CacheRegionUrn(id),
|
||||
|
@ -85,8 +82,7 @@ namespace Roadie.Api.Services
|
|||
etag);
|
||||
}
|
||||
|
||||
public async Task<FileOperationResult<Image>> CollectionImage(Guid id, int? width, int? height,
|
||||
EntityTagHeaderValue etag = null)
|
||||
public async Task<FileOperationResult<Image>> CollectionImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null)
|
||||
{
|
||||
return await GetImageFileOperation("CollectionThumbnail",
|
||||
data.Collection.CacheRegionUrn(id),
|
||||
|
@ -97,7 +93,7 @@ namespace Roadie.Api.Services
|
|||
etag);
|
||||
}
|
||||
|
||||
public async Task<OperationResult<bool>> Delete(User user, Guid id)
|
||||
public async Task<OperationResult<bool>> Delete(ApplicationUser user, Guid id)
|
||||
{
|
||||
var sw = Stopwatch.StartNew();
|
||||
var image = DbContext.Images
|
||||
|
@ -105,10 +101,10 @@ namespace Roadie.Api.Services
|
|||
.Include("Artist")
|
||||
.FirstOrDefault(x => x.RoadieId == id);
|
||||
if (image == null) return new OperationResult<bool>(true, string.Format("Image Not Found [{0}]", id));
|
||||
if (image.ArtistId.HasValue) CacheManager.ClearRegion(data.Artist.CacheRegionUrn(image.Artist.RoadieId));
|
||||
if (image.ReleaseId.HasValue) CacheManager.ClearRegion(data.Release.CacheRegionUrn(image.Release.RoadieId));
|
||||
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.LogInformation($"Deleted Image [{id}], By User [{user}]");
|
||||
sw.Stop();
|
||||
|
@ -120,7 +116,52 @@ namespace Roadie.Api.Services
|
|||
};
|
||||
}
|
||||
|
||||
public async Task<OperationResult<IEnumerable<ImageSearchResult>>> ImageProvidersSearch(string query)
|
||||
|
||||
public async Task<FileOperationResult<Image>> LabelImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null)
|
||||
{
|
||||
return await GetImageFileOperation("LabelThumbnail",
|
||||
data.Label.CacheRegionUrn(id),
|
||||
id,
|
||||
width,
|
||||
height,
|
||||
async () => { return await LabelImageAction(id, etag); },
|
||||
etag);
|
||||
}
|
||||
|
||||
public async Task<FileOperationResult<Image>> PlaylistImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null)
|
||||
{
|
||||
return await GetImageFileOperation("PlaylistThumbnail",
|
||||
data.Playlist.CacheRegionUrn(id),
|
||||
id,
|
||||
width,
|
||||
height,
|
||||
async () => { return await PlaylistImageAction(id, etag); },
|
||||
etag);
|
||||
}
|
||||
|
||||
public async Task<FileOperationResult<Image>> ReleaseImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null)
|
||||
{
|
||||
return await GetImageFileOperation("ReleaseThumbnail",
|
||||
data.Release.CacheRegionUrn(id),
|
||||
id,
|
||||
width,
|
||||
height,
|
||||
async () => { return await ReleaseImageAction(id, etag); },
|
||||
etag);
|
||||
}
|
||||
|
||||
public async Task<FileOperationResult<Image>> ReleaseSecondaryImage(Guid id, int imageId, int? width, int? height, EntityTagHeaderValue etag = null)
|
||||
{
|
||||
return await GetImageFileOperation($"ReleaseSecondaryThumbnail-{imageId}",
|
||||
data.Release.CacheRegionUrn(id),
|
||||
id,
|
||||
width,
|
||||
height,
|
||||
async () => { return await ReleaseSecondaryImageAction(id, imageId, etag); },
|
||||
etag);
|
||||
}
|
||||
|
||||
public async Task<OperationResult<IEnumerable<ImageSearchResult>>> Search(string query, int resultsCount = 10)
|
||||
{
|
||||
var sw = new Stopwatch();
|
||||
sw.Start();
|
||||
|
@ -128,8 +169,7 @@ namespace Roadie.Api.Services
|
|||
IEnumerable<ImageSearchResult> searchResults = null;
|
||||
try
|
||||
{
|
||||
var manager = new ImageSearchManager(Configuration, CacheManager, Logger);
|
||||
searchResults = await manager.ImageSearch(query);
|
||||
searchResults = await ImageSearchManager.ImageSearch(query);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -145,83 +185,10 @@ namespace Roadie.Api.Services
|
|||
OperationTime = sw.ElapsedMilliseconds,
|
||||
Errors = errors
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
public async Task<FileOperationResult<Image>> LabelImage(Guid id, int? width, int? height,
|
||||
EntityTagHeaderValue etag = null)
|
||||
{
|
||||
return await GetImageFileOperation("LabelThumbnail",
|
||||
data.Label.CacheRegionUrn(id),
|
||||
id,
|
||||
width,
|
||||
height,
|
||||
async () => { return await LabelImageAction(id, etag); },
|
||||
etag);
|
||||
}
|
||||
|
||||
public async Task<FileOperationResult<Image>> PlaylistImage(Guid id, int? width, int? height,
|
||||
EntityTagHeaderValue etag = null)
|
||||
{
|
||||
return await GetImageFileOperation("PlaylistThumbnail",
|
||||
data.Playlist.CacheRegionUrn(id),
|
||||
id,
|
||||
width,
|
||||
height,
|
||||
async () => { return await PlaylistImageAction(id, etag); },
|
||||
etag);
|
||||
}
|
||||
|
||||
public async Task<FileOperationResult<Image>> ReleaseImage(Guid id, int? width, int? height,
|
||||
EntityTagHeaderValue etag = null)
|
||||
{
|
||||
return await GetImageFileOperation("ReleaseThumbnail",
|
||||
data.Release.CacheRegionUrn(id),
|
||||
id,
|
||||
width,
|
||||
height,
|
||||
async () => { return await ReleaseImageAction(id, etag); },
|
||||
etag);
|
||||
}
|
||||
|
||||
public async Task<FileOperationResult<Image>> ReleaseSecondaryImage(Guid id, int imageId, int? width,
|
||||
int? height, EntityTagHeaderValue etag = null)
|
||||
{
|
||||
return await GetImageFileOperation($"ReleaseSecondaryThumbnail-{imageId}",
|
||||
data.Release.CacheRegionUrn(id),
|
||||
id,
|
||||
width,
|
||||
height,
|
||||
async () => { return await ReleaseSecondaryImageAction(id, imageId, etag); },
|
||||
etag);
|
||||
}
|
||||
|
||||
public async Task<OperationResult<IEnumerable<ImageSearchResult>>> Search(string query, int resultsCount = 10)
|
||||
{
|
||||
var sw = Stopwatch.StartNew();
|
||||
|
||||
var result = new List<ImageSearchResult>();
|
||||
|
||||
if (WebHelper.IsStringUrl(query))
|
||||
{
|
||||
var s = ImageHelper.ImageSearchResultForImageUrl(query);
|
||||
if (s != null) result.Add(s);
|
||||
}
|
||||
|
||||
var bingResults = await BingSearchEngine.PerformImageSearch(query, resultsCount);
|
||||
if (bingResults != null) result.AddRange(bingResults);
|
||||
var iTunesResults = await ITunesSearchEngine.PerformImageSearch(query, resultsCount);
|
||||
if (iTunesResults != null) result.AddRange(iTunesResults);
|
||||
sw.Stop();
|
||||
return new OperationResult<IEnumerable<ImageSearchResult>>
|
||||
{
|
||||
IsSuccess = true,
|
||||
Data = result,
|
||||
OperationTime = sw.ElapsedMilliseconds
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<FileOperationResult<Image>> TrackImage(Guid id, int? width, int? height,
|
||||
EntityTagHeaderValue etag = null)
|
||||
public async Task<FileOperationResult<Image>> TrackImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null)
|
||||
{
|
||||
return await GetImageFileOperation("TrackThumbnail",
|
||||
data.Track.CacheRegionUrn(id),
|
||||
|
@ -232,8 +199,7 @@ namespace Roadie.Api.Services
|
|||
etag);
|
||||
}
|
||||
|
||||
public async Task<FileOperationResult<Image>> UserImage(Guid id, int? width, int? height,
|
||||
EntityTagHeaderValue etag = null)
|
||||
public async Task<FileOperationResult<Image>> UserImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null)
|
||||
{
|
||||
return await GetImageFileOperation("UserById",
|
||||
ApplicationUser.CacheRegionUrn(id),
|
||||
|
@ -257,7 +223,7 @@ namespace Roadie.Api.Services
|
|||
try
|
||||
{
|
||||
// See if artist images exists in artist folder
|
||||
artistFolder = artist.ArtistFileFolder(Configuration, Configuration.LibraryFolder);
|
||||
artistFolder = artist.ArtistFileFolder(Configuration);
|
||||
if (!Directory.Exists(artistFolder))
|
||||
{
|
||||
Logger.LogWarning($"Artist Folder [{artistFolder}], Not Found For Artist `{artist}`");
|
||||
|
@ -292,8 +258,7 @@ namespace Roadie.Api.Services
|
|||
return Task.FromResult(new FileOperationResult<Image>(OperationMessages.ErrorOccured));
|
||||
}
|
||||
|
||||
private Task<FileOperationResult<Image>> ArtistSecondaryImageAction(Guid id, int imageId,
|
||||
EntityTagHeaderValue etag = null)
|
||||
private Task<FileOperationResult<Image>> ArtistSecondaryImageAction(Guid id, int imageId, EntityTagHeaderValue etag = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -306,7 +271,7 @@ namespace Roadie.Api.Services
|
|||
try
|
||||
{
|
||||
// See if cover art file exists in release folder
|
||||
artistFolder = artist.ArtistFileFolder(Configuration, Configuration.LibraryFolder);
|
||||
artistFolder = artist.ArtistFileFolder(Configuration);
|
||||
if (!Directory.Exists(artistFolder))
|
||||
{
|
||||
Logger.LogWarning($"Artist Folder [{artistFolder}], Not Found For Artist `{artist}`");
|
||||
|
@ -504,15 +469,16 @@ namespace Roadie.Api.Services
|
|||
{
|
||||
var release = GetRelease(id);
|
||||
if (release == null)
|
||||
return Task.FromResult(new FileOperationResult<Image>(true,
|
||||
string.Format("Release Not Found [{0}]", id)));
|
||||
{
|
||||
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(Configuration, Configuration.LibraryFolder);
|
||||
artistFolder = release.Artist.ArtistFileFolder(Configuration);
|
||||
if (!Directory.Exists(artistFolder))
|
||||
{
|
||||
Logger.LogWarning($"Artist Folder [{artistFolder}], Not Found For Artist `{release.Artist}`");
|
||||
|
@ -526,11 +492,11 @@ namespace Roadie.Api.Services
|
|||
}
|
||||
else
|
||||
{
|
||||
var releaseCoverFiles =
|
||||
ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(releaseFolder),
|
||||
ImageType.Release);
|
||||
var releaseCoverFiles = ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(releaseFolder), ImageType.Release);
|
||||
if (releaseCoverFiles.Any())
|
||||
{
|
||||
imageBytes = File.ReadAllBytes(releaseCoverFiles.First().FullName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -573,7 +539,7 @@ namespace Roadie.Api.Services
|
|||
try
|
||||
{
|
||||
// See if cover art file exists in release folder
|
||||
artistFolder = release.Artist.ArtistFileFolder(Configuration, Configuration.LibraryFolder);
|
||||
artistFolder = release.Artist.ArtistFileFolder(Configuration);
|
||||
if (!Directory.Exists(artistFolder))
|
||||
{
|
||||
Logger.LogWarning($"Artist Folder [{artistFolder}], Not Found For Artist `{release.Artist}`");
|
||||
|
@ -627,7 +593,7 @@ namespace Roadie.Api.Services
|
|||
return new FileOperationResult<Image>(true, string.Format("Track Not Found [{0}]", id));
|
||||
var imageBytes = track.Thumbnail;
|
||||
var trackThumbnailImages = ImageHelper.FindImageTypeInDirectory(
|
||||
new DirectoryInfo(track.PathToTrackThumbnail(Configuration, Configuration.LibraryFolder)),
|
||||
new DirectoryInfo(track.PathToTrackThumbnail(Configuration)),
|
||||
ImageType.Track, SearchOption.TopDirectoryOnly);
|
||||
if (trackThumbnailImages.Any()) imageBytes = File.ReadAllBytes(trackThumbnailImages.First().FullName);
|
||||
var image = new data.Image
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -9,6 +9,7 @@
|
|||
<PackageReference Include="Hashids.net" Version="1.2.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="2.2.5" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="5.5.0" />
|
||||
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.0.18" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -27,7 +27,7 @@ namespace Roadie.Api.Services
|
|||
{
|
||||
}
|
||||
|
||||
public OperationResult<LibraryStats> LibraryStatistics()
|
||||
public Task<OperationResult<LibraryStats>> LibraryStatistics()
|
||||
{
|
||||
LibraryStats result = null;
|
||||
var sw = new Stopwatch();
|
||||
|
@ -58,12 +58,12 @@ namespace Roadie.Api.Services
|
|||
Logger.LogError(ex);
|
||||
}
|
||||
|
||||
return new OperationResult<LibraryStats>
|
||||
return Task.FromResult(new OperationResult<LibraryStats>
|
||||
{
|
||||
IsSuccess = result != null,
|
||||
OperationTime = sw.ElapsedMilliseconds,
|
||||
IsSuccess = result != null,
|
||||
Data = result
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
public Task<OperationResult<IEnumerable<DateAndCount>>> ReleasesByDate()
|
||||
|
|
|
@ -681,7 +681,7 @@ namespace Roadie.Api.Services
|
|||
string trackPath = null;
|
||||
try
|
||||
{
|
||||
trackPath = track.PathToTrack(Configuration, Configuration.LibraryFolder);
|
||||
trackPath = track.PathToTrack(Configuration);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -712,7 +712,7 @@ namespace Roadie.Api.Services
|
|||
|
||||
try
|
||||
{
|
||||
trackPath = track.PathToTrack(Configuration, Configuration.LibraryFolder);
|
||||
trackPath = track.PathToTrack(Configuration);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -834,7 +834,7 @@ namespace Roadie.Api.Services
|
|||
track.Thumbnail = ImageHelper.ConvertToJpegFormat(trackImage);
|
||||
|
||||
// Save unaltered image to cover file
|
||||
var trackThumbnailName = track.PathToTrackThumbnail(Configuration, Configuration.LibraryFolder);
|
||||
var trackThumbnailName = track.PathToTrackThumbnail(Configuration);
|
||||
File.WriteAllBytes(trackThumbnailName, track.Thumbnail);
|
||||
|
||||
// Resize to store in database as thumbnail
|
||||
|
|
|
@ -35,16 +35,17 @@ namespace Roadie.Api.Services
|
|||
|
||||
public UserService(IRoadieSettings configuration,
|
||||
IHttpEncoder httpEncoder,
|
||||
IHttpContext httpContext,
|
||||
data.IRoadieDbContext context,
|
||||
ICacheManager cacheManager,
|
||||
ILogger<ArtistService> logger,
|
||||
UserManager<ApplicationUser> userManager
|
||||
IHttpContext httpContext,
|
||||
data.IRoadieDbContext context,
|
||||
ICacheManager cacheManager,
|
||||
ILogger<ArtistService> logger,
|
||||
UserManager<ApplicationUser> userManager,
|
||||
ILastFmHelper lastFmHelper
|
||||
)
|
||||
: base(configuration, httpEncoder, context, cacheManager, logger, httpContext)
|
||||
{
|
||||
UserManager = userManager;
|
||||
LastFmHelper = new LastFmHelper(Configuration, CacheManager, Logger, context, httpEncoder);
|
||||
LastFmHelper = lastFmHelper;
|
||||
;
|
||||
}
|
||||
|
||||
|
|
|
@ -78,7 +78,7 @@ namespace Roadie.Api.Controllers
|
|||
public async Task<IActionResult> MergeArtists(Guid artistToMergeId, Guid artistToMergeIntoId)
|
||||
{
|
||||
var result =
|
||||
await ArtistService.MergeArtists(await CurrentUserModel(), artistToMergeId, artistToMergeIntoId);
|
||||
await ArtistService.MergeArtists(await UserManager.GetUserAsync(User), artistToMergeId, artistToMergeIntoId);
|
||||
if (result == null || result.IsNotFoundResult) return NotFound();
|
||||
if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError);
|
||||
return Ok(result);
|
||||
|
@ -91,7 +91,7 @@ namespace Roadie.Api.Controllers
|
|||
public async Task<IActionResult> SetArtistImageByUrl(Guid id, string imageUrl)
|
||||
{
|
||||
var result =
|
||||
await ArtistService.SetReleaseImageByUrl(await CurrentUserModel(), id, HttpUtility.UrlDecode(imageUrl));
|
||||
await ArtistService.SetReleaseImageByUrl(await UserManager.GetUserAsync(User), id, HttpUtility.UrlDecode(imageUrl));
|
||||
if (result == null || result.IsNotFoundResult) return NotFound();
|
||||
if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError);
|
||||
return Ok(result);
|
||||
|
@ -104,7 +104,7 @@ namespace Roadie.Api.Controllers
|
|||
public async Task<IActionResult> Update(models.Artist artist)
|
||||
{
|
||||
if (!ModelState.IsValid) return BadRequest(ModelState);
|
||||
var result = await ArtistService.UpdateArtist(await CurrentUserModel(), artist);
|
||||
var result = await ArtistService.UpdateArtist(await UserManager.GetUserAsync(User), artist);
|
||||
if (result == null || result.IsNotFoundResult) return NotFound();
|
||||
if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError);
|
||||
return Ok(result);
|
||||
|
@ -116,7 +116,7 @@ namespace Roadie.Api.Controllers
|
|||
[Authorize(Policy = "Editor")]
|
||||
public async Task<IActionResult> UploadImage(Guid id, IFormFile file)
|
||||
{
|
||||
var result = await ArtistService.UploadArtistImage(await CurrentUserModel(), id, file);
|
||||
var result = await ArtistService.UploadArtistImage(await UserManager.GetUserAsync(User), id, file);
|
||||
if (result == null || result.IsNotFoundResult) return NotFound();
|
||||
if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError);
|
||||
return Ok(result);
|
||||
|
|
|
@ -81,7 +81,7 @@ namespace Roadie.Api.Controllers
|
|||
[ProducesResponseType(200)]
|
||||
public async Task<IActionResult> Delete(Guid id)
|
||||
{
|
||||
var result = await ImageService.Delete(await CurrentUserModel(), 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);
|
||||
|
|
|
@ -76,7 +76,7 @@ namespace Roadie.Api.Controllers
|
|||
[Authorize(Policy = "Editor")]
|
||||
public async Task<IActionResult> MergeReleases(Guid releaseToMergeId, Guid releaseToMergeIntoId, bool addAsMedia)
|
||||
{
|
||||
var result = await ReleaseService.MergeReleases(await CurrentUserModel(), releaseToMergeId, releaseToMergeIntoId, addAsMedia);
|
||||
var result = await ReleaseService.MergeReleases(await UserManager.GetUserAsync(User), releaseToMergeId, releaseToMergeIntoId, addAsMedia);
|
||||
if (result == null || result.IsNotFoundResult) return NotFound();
|
||||
if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError);
|
||||
return Ok(result);
|
||||
|
@ -88,7 +88,7 @@ namespace Roadie.Api.Controllers
|
|||
[Authorize(Policy = "Editor")]
|
||||
public async Task<IActionResult> SetReleaseImageByUrl(Guid id, string imageUrl)
|
||||
{
|
||||
var result = await ReleaseService.SetReleaseImageByUrl(await CurrentUserModel(), id, HttpUtility.UrlDecode(imageUrl));
|
||||
var result = await ReleaseService.SetReleaseImageByUrl(await UserManager.GetUserAsync(User), id, HttpUtility.UrlDecode(imageUrl));
|
||||
if (result == null || result.IsNotFoundResult) return NotFound();
|
||||
if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError);
|
||||
return Ok(result);
|
||||
|
@ -101,7 +101,7 @@ namespace Roadie.Api.Controllers
|
|||
public async Task<IActionResult> Update(Release release)
|
||||
{
|
||||
if (!ModelState.IsValid) return BadRequest(ModelState);
|
||||
var result = await ReleaseService.UpdateRelease(await CurrentUserModel(), release);
|
||||
var result = await ReleaseService.UpdateRelease(await UserManager.GetUserAsync(User), release);
|
||||
if (result == null || result.IsNotFoundResult) return NotFound();
|
||||
if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError);
|
||||
return Ok(result);
|
||||
|
@ -113,7 +113,7 @@ namespace Roadie.Api.Controllers
|
|||
[Authorize(Policy = "Editor")]
|
||||
public async Task<IActionResult> UploadImage(Guid id, IFormFile file)
|
||||
{
|
||||
var result = await ReleaseService.UploadReleaseImage(await CurrentUserModel(), id, file);
|
||||
var result = await ReleaseService.UploadReleaseImage(await UserManager.GetUserAsync(User), id, file);
|
||||
if (result == null || result.IsNotFoundResult) return NotFound();
|
||||
if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError);
|
||||
return Ok(result);
|
||||
|
|
|
@ -36,10 +36,12 @@ namespace Roadie.Api.Controllers
|
|||
var proc = Process.GetCurrentProcess();
|
||||
var mem = proc.WorkingSet64;
|
||||
var cpu = proc.TotalProcessorTime;
|
||||
var messages = new List<string>();
|
||||
messages.Add("▜ Memory Information: ");
|
||||
messages.Add(string.Format("My process used working set {0:n3} K of working set and CPU {1:n} msec",
|
||||
mem / 1024.0, cpu.TotalMilliseconds));
|
||||
var messages = new List<string>
|
||||
{
|
||||
"▜ Memory Information: ",
|
||||
string.Format("My process used working set {0:n3} K of working set and CPU {1:n} msec",
|
||||
mem / 1024.0, cpu.TotalMilliseconds)
|
||||
};
|
||||
foreach (var aProc in Process.GetProcesses())
|
||||
messages.Add(string.Format("Proc {0,30} CPU {1,-20:n} msec", aProc.ProcessName,
|
||||
cpu.TotalMilliseconds));
|
||||
|
@ -50,24 +52,15 @@ namespace Roadie.Api.Controllers
|
|||
|
||||
[HttpGet("library")]
|
||||
[ProducesResponseType(200)]
|
||||
public async Task<IActionResult> Library()
|
||||
{
|
||||
return Ok(StatisticsService.LibraryStatistics());
|
||||
}
|
||||
public async Task<IActionResult> Library() => Ok(await StatisticsService.LibraryStatistics());
|
||||
|
||||
[HttpGet("ping")]
|
||||
[ProducesResponseType(200)]
|
||||
[AllowAnonymous]
|
||||
public IActionResult Ping()
|
||||
{
|
||||
return Ok("pong");
|
||||
}
|
||||
public IActionResult Ping() => Ok("pong");
|
||||
|
||||
[HttpGet("releasesByDate")]
|
||||
[ProducesResponseType(200)]
|
||||
public async Task<IActionResult> ReleasesByDate()
|
||||
{
|
||||
return Ok(await StatisticsService.ReleasesByDate());
|
||||
}
|
||||
public async Task<IActionResult> ReleasesByDate() => Ok(await StatisticsService.ReleasesByDate());
|
||||
}
|
||||
}
|
|
@ -2,6 +2,8 @@
|
|||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Serilog;
|
||||
using Serilog.Events;
|
||||
using Serilog.Sinks.RollingFileAlternate;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
@ -23,6 +25,7 @@ namespace Roadie.Api
|
|||
{
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
.ReadFrom.Configuration(Configuration)
|
||||
.WriteTo.RollingFileAlternate("logs", "errors", LogEventLevel.Error)
|
||||
.CreateLogger();
|
||||
|
||||
try
|
||||
|
@ -72,14 +75,8 @@ namespace Roadie.Api
|
|||
|
||||
public class LoggingTraceListener : TraceListener
|
||||
{
|
||||
public override void Write(string message)
|
||||
{
|
||||
Log.Verbose(message);
|
||||
}
|
||||
public override void Write(string message) => Log.Verbose(message);
|
||||
|
||||
public override void WriteLine(string message)
|
||||
{
|
||||
Log.Verbose(message);
|
||||
}
|
||||
public override void WriteLine(string message) => Log.Verbose(message);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||
|
@ -24,10 +24,15 @@
|
|||
<ItemGroup>
|
||||
<PackageReference Include="BCrypt-Core" Version="2.0.0" />
|
||||
<PackageReference Include="Mapster" Version="4.1.0" />
|
||||
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.10.0" />
|
||||
<PackageReference Include="Microsoft.AspNet.SignalR" Version="2.4.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.All" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.OData" Version="7.1.0" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Common" Version="3.1.0" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.1.0" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="3.1.0" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.2.3" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="2.1.1" />
|
||||
<PackageReference Include="Serilog.Exceptions" Version="5.3.0" />
|
||||
<PackageReference Include="Serilog.Settings.Configuration" Version="3.1.0" />
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using Mapster;
|
||||
#region Usings
|
||||
using Mapster;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
|
@ -21,13 +22,25 @@ using Roadie.Library.Caching;
|
|||
using Roadie.Library.Configuration;
|
||||
using Roadie.Library.Data;
|
||||
using Roadie.Library.Encoding;
|
||||
using Roadie.Library.Engines;
|
||||
using Roadie.Library.Identity;
|
||||
using Roadie.Library.Imaging;
|
||||
using Roadie.Library.MetaData.Audio;
|
||||
using Roadie.Library.MetaData.FileName;
|
||||
using Roadie.Library.MetaData.ID3Tags;
|
||||
using Roadie.Library.MetaData.LastFm;
|
||||
using Roadie.Library.MetaData.MusicBrainz;
|
||||
using Roadie.Library.Processors;
|
||||
using Roadie.Library.Scrobble;
|
||||
using Roadie.Library.SearchEngines.Imaging;
|
||||
using Roadie.Library.SearchEngines.MetaData.Discogs;
|
||||
using Roadie.Library.SearchEngines.MetaData.Spotify;
|
||||
using Roadie.Library.SearchEngines.MetaData.Wikipedia;
|
||||
using Roadie.Library.Utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
#endregion
|
||||
|
||||
namespace Roadie.Api
|
||||
{
|
||||
|
@ -45,7 +58,7 @@ namespace Roadie.Api
|
|||
|
||||
Logger = _loggerFactory.CreateLogger<Startup>();
|
||||
|
||||
TypeAdapterConfig<Image, Library.Models.Image>
|
||||
TypeAdapterConfig<Library.Data.Image, Library.Models.Image>
|
||||
.NewConfig()
|
||||
.Map(i => i.ArtistId,
|
||||
src => src.Artist == null ? null : (Guid?)src.Artist.RoadieId)
|
||||
|
@ -178,10 +191,29 @@ namespace Roadie.Api
|
|||
|
||||
services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
|
||||
services.AddSingleton<IDefaultNotFoundImages, DefaultNotFoundImages>();
|
||||
services.AddSingleton<IImageSearchManager, ImageSearchManager>();
|
||||
services.AddSingleton<IITunesSearchEngine, ITunesSearchEngine>();
|
||||
services.AddSingleton<IBingImageSearchEngine, BingImageSearchEngine>();
|
||||
services.AddSingleton<IMusicBrainzProvider, MusicBrainzProvider>();
|
||||
services.AddSingleton<ISpotifyHelper, SpotifyHelper>();
|
||||
services.AddSingleton<IDiscogsHelper, DiscogsHelper>();
|
||||
services.AddSingleton<IWikipediaHelper, WikipediaHelper>();
|
||||
services.AddSingleton<IFileNameHelper, FileNameHelper>();
|
||||
services.AddSingleton<IID3TagsHelper, ID3TagsHelper>();
|
||||
|
||||
services.AddScoped<ILastFmHelper, LastFmHelper>();
|
||||
services.AddScoped<IRoadieScrobbler, RoadieScrobbler>();
|
||||
services.AddScoped<ILastFMScrobbler, LastFMScrobbler>();
|
||||
services.AddScoped<IStatisticsService, StatisticsService>();
|
||||
services.AddScoped<ICollectionService, CollectionService>();
|
||||
services.AddScoped<IPlaylistService, PlaylistService>();
|
||||
services.AddScoped<IBookmarkService, BookmarkService>();
|
||||
services.AddScoped<IArtistLookupEngine, ArtistLookupEngine>();
|
||||
services.AddScoped<IReleaseLookupEngine, ReleaseLookupEngine>();
|
||||
services.AddScoped<ILabelLookupEngine, LabelLookupEngine>();
|
||||
services.AddScoped<IAudioMetaDataHelper, AudioMetaDataHelper>();
|
||||
services.AddScoped<IFileProcessor, FileProcessor>();
|
||||
services.AddScoped<IFileDirectoryProcessorService, FileDirectoryProcessorService>();
|
||||
services.AddScoped<IArtistService, ArtistService>();
|
||||
services.AddScoped<IImageService, ImageService>();
|
||||
services.AddScoped<IReleaseService, ReleaseService>();
|
||||
|
|
Loading…
Reference in a new issue