roadie/Roadie.Api.Library/Engines/ReleaseLookupEngine.cs

860 lines
44 KiB
C#
Raw Normal View History

2018-12-15 22:35:20 +00:00
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.Enums;
using Roadie.Library.Extensions;
using Roadie.Library.Imaging;
using Roadie.Library.MetaData.Audio;
2019-06-03 13:37:13 +00:00
using Roadie.Library.MetaData.ID3Tags;
2018-12-15 22:35:20 +00:00
using Roadie.Library.SearchEngines.Imaging;
using Roadie.Library.SearchEngines.MetaData;
using Roadie.Library.Utility;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using discogs = Roadie.Library.SearchEngines.MetaData.Discogs;
using lastfm = Roadie.Library.MetaData.LastFm;
using musicbrainz = Roadie.Library.MetaData.MusicBrainz;
using spotify = Roadie.Library.SearchEngines.MetaData.Spotify;
using wikipedia = Roadie.Library.SearchEngines.MetaData.Wikipedia;
namespace Roadie.Library.Engines
{
public class ReleaseLookupEngine : LookupEngineBase, IReleaseLookupEngine
{
2018-12-15 23:22:02 +00:00
public List<int> _addedReleaseIds = new List<int>();
public List<int> _addedTrackIds = new List<int>();
2019-07-03 16:21:29 +00:00
public IEnumerable<int> AddedReleaseIds => _addedReleaseIds;
2018-12-15 23:22:02 +00:00
2019-07-03 16:21:29 +00:00
public IEnumerable<int> AddedTrackIds => _addedTrackIds;
2018-12-15 22:35:20 +00:00
public IReleaseSearchEngine DiscogsReleaseSearchEngine { get; }
2019-07-03 16:21:29 +00:00
2018-12-15 22:35:20 +00:00
public IReleaseSearchEngine ITunesReleaseSearchEngine { get; }
2019-07-03 16:21:29 +00:00
2018-12-15 22:35:20 +00:00
public IReleaseSearchEngine LastFmReleaseSearchEngine { get; }
2019-07-03 16:21:29 +00:00
public IReleaseSearchEngine MusicBrainzReleaseSearchEngine { get; }
2018-12-15 22:35:20 +00:00
2019-07-03 16:21:29 +00:00
public IReleaseSearchEngine SpotifyReleaseSearchEngine { get; }
2019-02-25 02:26:54 +00:00
2019-07-03 16:21:29 +00:00
public IReleaseSearchEngine WikipediaReleaseSearchEngine { get; }
2019-02-25 02:26:54 +00:00
2019-07-03 16:21:29 +00:00
private IArtistLookupEngine ArtistLookupEngine { get; }
2019-01-27 22:49:55 +00:00
2019-07-03 16:21:29 +00:00
private ILabelLookupEngine LabelLookupEngine { get; }
public ReleaseLookupEngine(IRoadieSettings configuration, IHttpEncoder httpEncoder, IRoadieDbContext context,
ICacheManager cacheManager, ILogger logger, IArtistLookupEngine artistLookupEngine,
ILabelLookupEngine labelLookupEngine)
: base(configuration, httpEncoder, context, cacheManager, logger)
{
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);
2018-12-15 22:35:20 +00:00
}
2019-07-03 16:21:29 +00:00
public async Task<OperationResult<Release>> Add(Release release, bool doAddTracksInDatabase = false)
2018-12-15 22:35:20 +00:00
{
SimpleContract.Requires<ArgumentNullException>(release != null, "Invalid Release");
try
{
var releaseGenreTables = release.Genres;
var releaseImages = release.Images;
var releaseMedias = release.Medias;
var releaseLabels = release.Labels;
var now = DateTime.UtcNow;
2019-07-03 16:21:29 +00:00
release.AlternateNames =
release.AlternateNames.AddToDelimitedList(new[] { release.Title.ToAlphanumericName() });
2018-12-15 22:35:20 +00:00
release.Images = null;
release.Labels = null;
release.Medias = null;
release.Genres = null;
release.LibraryStatus = LibraryStatus.Incomplete;
release.Status = Statuses.New;
if (!release.IsValid)
2019-07-03 16:21:29 +00:00
return new OperationResult<Release>
2018-12-15 22:35:20 +00:00
{
Errors = new Exception[1] { new Exception("Release is Invalid") }
};
2019-07-03 16:21:29 +00:00
DbContext.Releases.Add(release);
var inserted = 0;
2018-12-15 22:35:20 +00:00
try
{
2019-07-03 16:21:29 +00:00
inserted = await DbContext.SaveChangesAsync();
2018-12-15 22:35:20 +00:00
}
catch (Exception ex)
{
2019-07-03 16:21:29 +00:00
Logger.LogError(ex, ex.Serialize());
2018-12-15 22:35:20 +00:00
}
2019-07-03 16:21:29 +00:00
2018-12-15 22:35:20 +00:00
if (inserted > 0 && release.Id > 0)
{
2019-07-03 16:21:29 +00:00
_addedReleaseIds.Add(release.Id);
2018-12-15 22:35:20 +00:00
if (releaseGenreTables != null && releaseGenreTables.Any(x => x.GenreId == null))
foreach (var releaseGenreTable in releaseGenreTables)
{
var genreName = releaseGenreTable.Genre?.Name?.ToAlphanumericName();
2019-07-03 16:21:29 +00:00
if (string.IsNullOrEmpty(genreName)) continue;
if (releaseGenreTable.Genre.Name.Length > 100)
2019-06-03 13:37:13 +00:00
{
var originalName = releaseGenreTable.Genre.Name;
releaseGenreTable.Genre.Name = releaseGenreTable.Genre.Name.Substring(0, 99);
2019-06-03 13:37:13 +00:00
genreName = genreName.Substring(0, 99);
Logger.LogWarning($"Genre Name Too long was [{originalName}] truncated to [{releaseGenreTable.Genre.Name}]");
2019-06-03 13:37:13 +00:00
}
2019-07-03 16:21:29 +00:00
var genre = DbContext.Genres.FirstOrDefault(x => x.NormalizedName == genreName);
2018-12-15 22:35:20 +00:00
if (genre == null)
{
genre = new Genre
{
Name = releaseGenreTable.Genre.Name,
NormalizedName = genreName
2018-12-15 22:35:20 +00:00
};
2019-07-03 16:21:29 +00:00
DbContext.Genres.Add(genre);
await DbContext.SaveChangesAsync();
2018-12-15 22:35:20 +00:00
}
2019-07-03 16:21:29 +00:00
2018-12-15 22:35:20 +00:00
if (genre != null && genre.Id > 0)
{
DbContext.ReleaseGenres.Add(new ReleaseGenre
2018-12-15 22:35:20 +00:00
{
ReleaseId = release.Id,
GenreId = genre.Id
});
2018-12-15 22:35:20 +00:00
}
}
2019-07-03 16:21:29 +00:00
2018-12-15 22:35:20 +00:00
if (releaseImages != null && releaseImages.Any(x => x.Status == Statuses.New))
{
foreach (var releaseImage in releaseImages)
{
2019-07-03 16:21:29 +00:00
DbContext.Images.Add(new Image
2018-12-15 22:35:20 +00:00
{
ReleaseId = release.Id,
Url = releaseImage.Url,
Signature = releaseImage.Signature,
Bytes = releaseImage.Bytes
});
}
2018-12-15 22:35:20 +00:00
try
{
2019-07-03 16:21:29 +00:00
await DbContext.SaveChangesAsync();
2018-12-15 22:35:20 +00:00
}
catch (Exception ex)
{
2019-07-03 16:21:29 +00:00
Logger.LogError(ex);
2018-12-15 22:35:20 +00:00
}
}
if (releaseLabels != null && releaseLabels.Any(x => x.Status == Statuses.New))
{
foreach (var neweleaseLabel in releaseLabels.Where(x => x.Status == Statuses.New))
{
2019-07-03 16:21:29 +00:00
var labelFetch = await LabelLookupEngine.GetByName(neweleaseLabel.Label.Name, true);
2018-12-15 22:35:20 +00:00
if (labelFetch.IsSuccess)
2019-07-03 16:21:29 +00:00
DbContext.ReleaseLabels.Add(new ReleaseLabel
2018-12-15 22:35:20 +00:00
{
CatalogNumber = neweleaseLabel.CatalogNumber,
BeginDate = neweleaseLabel.BeginDate,
EndDate = neweleaseLabel.EndDate,
ReleaseId = release.Id,
LabelId = labelFetch.Data.Id
});
}
2019-07-03 16:21:29 +00:00
2018-12-15 22:35:20 +00:00
try
{
2019-07-03 16:21:29 +00:00
await DbContext.SaveChangesAsync();
2018-12-15 22:35:20 +00:00
}
catch (Exception ex)
{
2019-07-03 16:21:29 +00:00
Logger.LogError(ex);
2018-12-15 22:35:20 +00:00
}
}
2019-07-03 16:21:29 +00:00
2018-12-15 22:35:20 +00:00
if (doAddTracksInDatabase)
if (releaseMedias != null && releaseMedias.Any(x => x.Status == Statuses.New))
{
foreach (var newReleaseMedia in releaseMedias.Where(x => x.Status == Statuses.New))
{
2019-07-03 16:21:29 +00:00
var releasemedia = new ReleaseMedia
2018-12-15 22:35:20 +00:00
{
Status = Statuses.Incomplete,
MediaNumber = newReleaseMedia.MediaNumber,
SubTitle = newReleaseMedia.SubTitle,
TrackCount = newReleaseMedia.TrackCount,
ReleaseId = release.Id
};
2019-07-03 16:21:29 +00:00
var releasemediatracks = new List<Track>();
2018-12-15 22:35:20 +00:00
foreach (var newTrack in newReleaseMedia.Tracks)
{
int? trackArtistId = null;
string partTitles = null;
if (newTrack.TrackArtist != null)
{
if (!release.IsCastRecording)
{
2019-07-03 16:21:29 +00:00
var trackArtistData =
await ArtistLookupEngine.GetByName(
new AudioMetaData { Artist = newTrack.TrackArtist.Name }, true);
if (trackArtistData.IsSuccess) trackArtistId = trackArtistData.Data.Id;
2018-12-15 22:35:20 +00:00
}
else if (newTrack.TrackArtists != null && newTrack.TrackArtists.Any())
{
partTitles = string.Join("/", newTrack.TrackArtists);
}
else
{
partTitles = newTrack.TrackArtist.Name;
}
}
2019-07-03 16:21:29 +00:00
releasemediatracks.Add(new Track
2018-12-15 22:35:20 +00:00
{
ArtistId = trackArtistId,
PartTitles = partTitles,
Status = Statuses.Incomplete,
TrackNumber = newTrack.TrackNumber,
MusicBrainzId = newTrack.MusicBrainzId,
SpotifyId = newTrack.SpotifyId,
AmgId = newTrack.AmgId,
Title = newTrack.Title,
AlternateNames = newTrack.AlternateNames,
Duration = newTrack.Duration,
Tags = newTrack.Tags,
ISRC = newTrack.ISRC,
LastFMId = newTrack.LastFMId
});
}
2019-07-03 16:21:29 +00:00
2018-12-15 22:35:20 +00:00
releasemedia.Tracks = releasemediatracks;
2019-07-03 16:21:29 +00:00
DbContext.ReleaseMedias.Add(releasemedia);
_addedTrackIds.AddRange(releasemedia.Tracks.Select(x => x.Id));
2018-12-15 22:35:20 +00:00
}
2019-07-03 16:21:29 +00:00
2018-12-15 22:35:20 +00:00
try
{
2019-07-03 16:21:29 +00:00
await DbContext.SaveChangesAsync();
2018-12-15 22:35:20 +00:00
}
catch (Exception ex)
{
2019-07-03 16:21:29 +00:00
Logger.LogError(ex);
2018-12-15 22:35:20 +00:00
}
}
2019-07-03 16:21:29 +00:00
Logger.LogInformation("Added New Release: `{0}`", release.ToString());
2018-12-15 22:35:20 +00:00
}
}
catch (Exception ex)
{
2019-07-03 16:21:29 +00:00
Logger.LogError(ex, ex.Serialize());
2018-12-15 22:35:20 +00:00
}
2019-07-03 16:21:29 +00:00
return new OperationResult<Release>
2018-12-15 22:35:20 +00:00
{
IsSuccess = release.Id > 0,
Data = release
};
}
2019-07-03 16:21:29 +00:00
public async Task<OperationResult<Release>> GetByName(Artist artist, AudioMetaData metaData,
bool doFindIfNotInDatabase = false, bool doAddTracksInDatabase = false, int? submissionId = null)
{
SimpleContract.Requires<ArgumentNullException>(artist != null, "Invalid Artist");
SimpleContract.Requires<ArgumentOutOfRangeException>(artist.Id > 0, "Invalid Artist Id");
try
{
var sw = new Stopwatch();
sw.Start();
var cacheRegion = new Release { Artist = artist, Title = metaData.Release }.CacheRegion;
var cacheKey = string.Format("urn:release_by_artist_id_and_name:{0}:{1}", artist.Id, metaData.Release);
var resultInCache = CacheManager.Get<Release>(cacheKey, cacheRegion);
if (resultInCache != null)
{
sw.Stop();
return new OperationResult<Release>
{
IsSuccess = true,
OperationTime = sw.ElapsedMilliseconds,
Data = resultInCache
};
}
var searchName = metaData.Release.NormalizeName().ToLower();
var specialSearchName = metaData.Release.ToAlphanumericName();
var altStart = $"{searchName}|";
var altIn = $"|{searchName}|";
var altEnds = $"|{searchName}";
var altStartSpecial = $"{specialSearchName}|";
var altInSpecial = $"|{specialSearchName}|";
var altEndsSpecial = $"|{specialSearchName}";
var release = (from r in DbContext.Releases
where r.ArtistId == artist.Id
where r.Title == searchName ||
r.Title == specialSearchName ||
r.AlternateNames == searchName ||
r.AlternateNames == specialSearchName ||
r.AlternateNames.Contains(altStart) ||
r.AlternateNames.Contains(altIn) ||
r.AlternateNames.Contains(altEnds) ||
r.AlternateNames.Contains(altStartSpecial) ||
r.AlternateNames.Contains(altInSpecial) ||
r.AlternateNames.Contains(altEndsSpecial)
select r
).FirstOrDefault();
sw.Stop();
if (release == null || !release.IsValid)
{
Logger.LogInformation("ReleaseFactory: Release Not Found For Artist `{0}` MetaData [{1}]",
artist.ToString(), metaData.ToString());
if (doFindIfNotInDatabase)
{
var releaseSearch = new OperationResult<Release>();
try
{
releaseSearch = await PerformMetaDataProvidersReleaseSearch(metaData,
artist.ArtistFileFolder(Configuration, Configuration.LibraryFolder), submissionId);
}
catch (Exception ex)
{
sw.Stop();
Logger.LogError(ex);
return new OperationResult<Release>
{
OperationTime = sw.ElapsedMilliseconds,
Errors = new Exception[1] { ex }
};
}
if (releaseSearch.IsSuccess)
{
release = releaseSearch.Data;
release.ArtistId = artist.Id;
var addResult = await Add(release, doAddTracksInDatabase);
if (!addResult.IsSuccess)
{
sw.Stop();
return new OperationResult<Release>
{
OperationTime = sw.ElapsedMilliseconds,
Errors = addResult.Errors
};
}
}
}
}
if (release != null) CacheManager.Add(cacheKey, release);
return new OperationResult<Release>
{
IsSuccess = release != null,
OperationTime = sw.ElapsedMilliseconds,
Data = release
};
}
catch (Exception ex)
{
Logger.LogError(ex);
}
return new OperationResult<Release>();
}
public async Task<OperationResult<Release>> PerformMetaDataProvidersReleaseSearch(AudioMetaData metaData,
string artistFolder = null, int? submissionId = null)
2018-12-15 22:35:20 +00:00
{
SimpleContract.Requires<ArgumentNullException>(metaData != null, "Invalid MetaData");
var sw = new Stopwatch();
sw.Start();
2019-07-03 16:21:29 +00:00
var result = new Release
2018-12-15 22:35:20 +00:00
{
Title = metaData.Release.ToTitleCase(false),
TrackCount = (short)(metaData.TotalTrackNumbers ?? 0),
ReleaseDate = SafeParser.ToDateTime(metaData.Year),
SubmissionId = submissionId
};
var resultsExceptions = new List<Exception>();
var releaseGenres = new List<string>();
// Add any Genre found in the given MetaData
2019-07-03 16:21:29 +00:00
if (metaData.Genres != null) releaseGenres.AddRange(metaData.Genres);
2018-12-15 22:35:20 +00:00
var releaseLabels = new List<ReleaseLabelSearchResult>();
var releaseMedias = new List<ReleaseMediaSearchResult>();
var releaseImageUrls = new List<string>();
2019-07-03 16:21:29 +00:00
var dontDoMetaDataProvidersSearchArtists = Configuration.DontDoMetaDataProvidersSearchArtists;
if (!dontDoMetaDataProvidersSearchArtists.Any(x =>
x.Equals(metaData.Artist, StringComparison.OrdinalIgnoreCase)))
2018-12-15 22:35:20 +00:00
{
try
{
#region ITunes
2019-07-03 16:21:29 +00:00
if (ITunesReleaseSearchEngine.IsEnabled)
2018-12-15 22:35:20 +00:00
{
2019-07-03 16:21:29 +00:00
Logger.LogTrace(
"ITunesReleaseSearchEngine Release Search for ArtistName [{0}], ReleaseTitle [{1}]",
metaData.Artist, result.Title);
var iTunesResult =
await ITunesReleaseSearchEngine.PerformReleaseSearch(metaData.Artist, result.Title, 1);
2018-12-15 22:35:20 +00:00
if (iTunesResult.IsSuccess)
{
var i = iTunesResult.Data.First();
if (i.AlternateNames != null)
result.AlternateNames = result.AlternateNames.AddToDelimitedList(i.AlternateNames);
2019-07-03 16:21:29 +00:00
if (i.Tags != null) result.Tags = result.Tags.AddToDelimitedList(i.Tags);
if (i.Urls != null) result.URLs = result.URLs.AddToDelimitedList(i.Urls);
if (i.ImageUrls != null) releaseImageUrls.AddRange(i.ImageUrls);
if (i.ReleaseGenres != null) releaseGenres.AddRange(i.ReleaseGenres);
result.CopyTo(new Release
2018-12-15 22:35:20 +00:00
{
ReleaseDate = result.ReleaseDate ?? i.ReleaseDate,
AmgId = i.AmgId,
Profile = i.Profile,
ITunesId = i.iTunesId,
Title = result.Title ?? i.ReleaseTitle,
2019-07-03 16:21:29 +00:00
Thumbnail = i.ReleaseThumbnailUrl != null
? WebHelper.BytesForImageUrl(i.ReleaseThumbnailUrl)
: null,
ReleaseType = result.ReleaseType == ReleaseType.Unknown
? SafeParser.ToEnum<ReleaseType>(i.ReleaseType)
: result.ReleaseType
2018-12-15 22:35:20 +00:00
});
2019-07-03 16:21:29 +00:00
if (i.ReleaseLabel != null) releaseLabels.AddRange(i.ReleaseLabel);
if (i.ReleaseMedia != null) releaseMedias.AddRange(i.ReleaseMedia);
2018-12-15 22:35:20 +00:00
}
2019-07-03 16:21:29 +00:00
if (iTunesResult.Errors != null) resultsExceptions.AddRange(iTunesResult.Errors);
2018-12-15 22:35:20 +00:00
}
#endregion ITunes
#region MusicBrainz
2019-07-03 16:21:29 +00:00
if (MusicBrainzReleaseSearchEngine.IsEnabled)
2018-12-15 22:35:20 +00:00
{
2019-07-03 16:21:29 +00:00
Logger.LogTrace(
"MusicBrainzReleaseSearchEngine Release Search for ArtistName [{0}], ReleaseTitle [{1}]",
metaData.Artist, result.Title);
var mbResult =
await MusicBrainzReleaseSearchEngine.PerformReleaseSearch(metaData.Artist, result.Title, 1);
2018-12-15 22:35:20 +00:00
if (mbResult.IsSuccess)
{
var mb = mbResult.Data.First();
if (mb.AlternateNames != null)
result.AlternateNames = result.AlternateNames.AddToDelimitedList(mb.AlternateNames);
2019-07-03 16:21:29 +00:00
if (mb.Tags != null) result.Tags = result.Tags.AddToDelimitedList(mb.Tags);
if (mb.Urls != null) result.URLs = result.URLs.AddToDelimitedList(mb.Urls);
if (mb.ImageUrls != null) releaseImageUrls.AddRange(mb.ImageUrls);
if (mb.ReleaseGenres != null) releaseGenres.AddRange(mb.ReleaseGenres);
if (!string.IsNullOrEmpty(mb.ReleaseTitle) &&
!mb.ReleaseTitle.Equals(result.Title, StringComparison.OrdinalIgnoreCase))
result.AlternateNames.AddToDelimitedList(new[] { mb.ReleaseTitle });
result.CopyTo(new Release
2018-12-15 22:35:20 +00:00
{
ReleaseDate = result.ReleaseDate ?? mb.ReleaseDate,
AmgId = mb.AmgId,
Profile = mb.Profile,
2019-07-03 16:21:29 +00:00
TrackCount = mb.ReleaseMedia != null
? (short)mb.ReleaseMedia.Sum(x => x.TrackCount)
: (short)0,
2018-12-15 22:35:20 +00:00
MusicBrainzId = mb.MusicBrainzId,
ITunesId = mb.iTunesId,
Title = result.Title ?? mb.ReleaseTitle,
2019-07-03 16:21:29 +00:00
Thumbnail = mb.ReleaseThumbnailUrl != null
? WebHelper.BytesForImageUrl(mb.ReleaseThumbnailUrl)
: null,
ReleaseType = result.ReleaseType == ReleaseType.Unknown
? SafeParser.ToEnum<ReleaseType>(mb.ReleaseType)
: result.ReleaseType
2018-12-15 22:35:20 +00:00
});
2019-07-03 16:21:29 +00:00
if (mb.ReleaseLabel != null) releaseLabels.AddRange(mb.ReleaseLabel);
if (mb.ReleaseMedia != null) releaseMedias.AddRange(mb.ReleaseMedia);
2018-12-15 22:35:20 +00:00
}
2019-07-03 16:21:29 +00:00
if (mbResult.Errors != null) resultsExceptions.AddRange(mbResult.Errors);
2018-12-15 22:35:20 +00:00
}
#endregion MusicBrainz
#region LastFm
2019-07-03 16:21:29 +00:00
if (LastFmReleaseSearchEngine.IsEnabled)
2018-12-15 22:35:20 +00:00
{
2019-07-03 16:21:29 +00:00
Logger.LogTrace(
"LastFmReleaseSearchEngine Release Search for ArtistName [{0}], ReleaseTitle [{1}]",
metaData.Artist, result.Title);
var lastFmResult =
await LastFmReleaseSearchEngine.PerformReleaseSearch(metaData.Artist, result.Title, 1);
2018-12-15 22:35:20 +00:00
if (lastFmResult.IsSuccess)
{
var l = lastFmResult.Data.First();
if (l.AlternateNames != null)
result.AlternateNames = result.AlternateNames.AddToDelimitedList(l.AlternateNames);
2019-07-03 16:21:29 +00:00
if (l.Tags != null) result.Tags = result.Tags.AddToDelimitedList(l.Tags);
if (l.Urls != null) result.URLs = result.URLs.AddToDelimitedList(l.Urls);
if (l.ImageUrls != null) releaseImageUrls.AddRange(l.ImageUrls);
if (l.ReleaseGenres != null) releaseGenres.AddRange(l.ReleaseGenres);
if (!string.IsNullOrEmpty(l.ReleaseTitle) &&
!l.ReleaseTitle.Equals(result.Title, StringComparison.OrdinalIgnoreCase))
result.AlternateNames.AddToDelimitedList(new[] { l.ReleaseTitle });
result.CopyTo(new Release
2018-12-15 22:35:20 +00:00
{
ReleaseDate = result.ReleaseDate ?? l.ReleaseDate,
AmgId = l.AmgId,
Profile = l.Profile,
LastFMId = l.LastFMId,
LastFMSummary = l.LastFMSummary,
MusicBrainzId = l.MusicBrainzId,
ITunesId = l.iTunesId,
Title = result.Title ?? l.ReleaseTitle,
2019-07-03 16:21:29 +00:00
Thumbnail = l.ReleaseThumbnailUrl != null
? WebHelper.BytesForImageUrl(l.ReleaseThumbnailUrl)
: null,
ReleaseType = result.ReleaseType == ReleaseType.Unknown
? SafeParser.ToEnum<ReleaseType>(l.ReleaseType)
: result.ReleaseType
2018-12-15 22:35:20 +00:00
});
2019-07-03 16:21:29 +00:00
if (l.ReleaseLabel != null) releaseLabels.AddRange(l.ReleaseLabel);
if (l.ReleaseMedia != null) releaseMedias.AddRange(l.ReleaseMedia);
2018-12-15 22:35:20 +00:00
}
2019-07-03 16:21:29 +00:00
if (lastFmResult.Errors != null) resultsExceptions.AddRange(lastFmResult.Errors);
2018-12-15 22:35:20 +00:00
}
#endregion LastFm
#region Spotify
2019-07-03 16:21:29 +00:00
if (SpotifyReleaseSearchEngine.IsEnabled)
2018-12-15 22:35:20 +00:00
{
2019-07-03 16:21:29 +00:00
Logger.LogTrace(
"SpotifyReleaseSearchEngine Release Search for ArtistName [{0}], ReleaseTitle [{1}]",
metaData.Artist, result.Title);
var spotifyResult =
await SpotifyReleaseSearchEngine.PerformReleaseSearch(metaData.Artist, result.Title, 1);
2018-12-15 22:35:20 +00:00
if (spotifyResult.IsSuccess)
{
var s = spotifyResult.Data.First();
2019-07-03 16:21:29 +00:00
if (s.Tags != null) result.Tags = result.Tags.AddToDelimitedList(s.Tags);
if (s.Urls != null) result.URLs = result.URLs.AddToDelimitedList(s.Urls);
if (s.ImageUrls != null) releaseImageUrls.AddRange(s.ImageUrls);
if (s.ReleaseGenres != null) releaseGenres.AddRange(s.ReleaseGenres);
if (!string.IsNullOrEmpty(s.ReleaseTitle) &&
!s.ReleaseTitle.Equals(result.Title, StringComparison.OrdinalIgnoreCase))
result.AlternateNames.AddToDelimitedList(new[] { s.ReleaseTitle });
result.CopyTo(new Release
2018-12-15 22:35:20 +00:00
{
ReleaseDate = result.ReleaseDate ?? s.ReleaseDate,
AmgId = s.AmgId,
2019-07-03 16:21:29 +00:00
Profile = HttpEncoder.HtmlEncode(s.Profile),
2018-12-15 22:35:20 +00:00
SpotifyId = s.SpotifyId,
MusicBrainzId = s.MusicBrainzId,
ITunesId = s.iTunesId,
Title = result.Title ?? s.ReleaseTitle,
2019-07-03 16:21:29 +00:00
Thumbnail = s.ReleaseThumbnailUrl != null
? WebHelper.BytesForImageUrl(s.ReleaseThumbnailUrl)
: null,
ReleaseType = result.ReleaseType == ReleaseType.Unknown
? SafeParser.ToEnum<ReleaseType>(s.ReleaseType)
: result.ReleaseType
2018-12-15 22:35:20 +00:00
});
2019-07-03 16:21:29 +00:00
if (s.ReleaseLabel != null) releaseLabels.AddRange(s.ReleaseLabel);
if (s.ReleaseMedia != null) releaseMedias.AddRange(s.ReleaseMedia);
2018-12-15 22:35:20 +00:00
}
2019-07-03 16:21:29 +00:00
if (spotifyResult.Errors != null) resultsExceptions.AddRange(spotifyResult.Errors);
2018-12-15 22:35:20 +00:00
}
#endregion Spotify
#region Discogs
2019-07-03 16:21:29 +00:00
if (DiscogsReleaseSearchEngine.IsEnabled)
2018-12-15 22:35:20 +00:00
{
2019-07-03 16:21:29 +00:00
Logger.LogTrace(
"DiscogsReleaseSearchEngine Release Search for ArtistName [{0}], ReleaseTitle [{1}]",
metaData.Artist, result.Title);
var discogsResult =
await DiscogsReleaseSearchEngine.PerformReleaseSearch(metaData.Artist, result.Title, 1);
2018-12-15 22:35:20 +00:00
if (discogsResult.IsSuccess)
{
var d = discogsResult.Data.First();
2019-07-03 16:21:29 +00:00
if (d.Urls != null) result.URLs = result.URLs.AddToDelimitedList(d.Urls);
if (d.ImageUrls != null) releaseImageUrls.AddRange(d.ImageUrls);
2018-12-15 22:35:20 +00:00
if (d.AlternateNames != null)
result.AlternateNames = result.AlternateNames.AddToDelimitedList(d.AlternateNames);
2019-07-03 16:21:29 +00:00
if (!string.IsNullOrEmpty(d.ReleaseTitle) &&
!d.ReleaseTitle.Equals(result.Title, StringComparison.OrdinalIgnoreCase))
result.AlternateNames.AddToDelimitedList(new[] { d.ReleaseTitle });
result.CopyTo(new Release
2018-12-15 22:35:20 +00:00
{
2019-07-03 16:21:29 +00:00
Profile = HttpEncoder.HtmlEncode(d.Profile),
2018-12-15 22:35:20 +00:00
DiscogsId = d.DiscogsId,
Title = result.Title ?? d.ReleaseTitle,
2019-07-03 16:21:29 +00:00
Thumbnail = d.ReleaseThumbnailUrl != null
? WebHelper.BytesForImageUrl(d.ReleaseThumbnailUrl)
: null,
ReleaseType = result.ReleaseType == ReleaseType.Unknown
? SafeParser.ToEnum<ReleaseType>(d.ReleaseType)
: result.ReleaseType
2018-12-15 22:35:20 +00:00
});
2019-07-03 16:21:29 +00:00
if (d.ReleaseLabel != null) releaseLabels.AddRange(d.ReleaseLabel);
if (d.ReleaseMedia != null) releaseMedias.AddRange(d.ReleaseMedia);
2018-12-15 22:35:20 +00:00
}
2019-07-03 16:21:29 +00:00
if (discogsResult.Errors != null) resultsExceptions.AddRange(discogsResult.Errors);
2018-12-15 22:35:20 +00:00
}
#endregion Discogs
}
catch (Exception ex)
{
2019-07-03 16:21:29 +00:00
Logger.LogError(ex);
2018-12-15 22:35:20 +00:00
}
2019-07-03 16:21:29 +00:00
Logger.LogTrace("Metadata Providers Search Complete. [{0}]", sw.ElapsedMilliseconds);
2018-12-15 22:35:20 +00:00
}
else
{
2019-07-03 16:21:29 +00:00
Logger.LogTrace(
"Skipped Metadata Providers Search, DontDoMetaDataProvidersSearchArtists set for Artist [{0}].",
metaData.Artist);
2018-12-15 22:35:20 +00:00
}
if (result.AlternateNames != null)
2019-07-03 16:21:29 +00:00
result.AlternateNames = string.Join("|",
result.AlternateNames.ToListFromDelimited().Distinct().OrderBy(x => x));
2018-12-15 22:35:20 +00:00
if (result.URLs != null)
result.URLs = string.Join("|", result.URLs.ToListFromDelimited().Distinct().OrderBy(x => x));
if (result.Tags != null)
result.Tags = string.Join("|", result.Tags.ToListFromDelimited().Distinct().OrderBy(x => x));
if (releaseGenres.Any())
{
result.Genres = new List<ReleaseGenre>();
foreach (var releaseGenre in releaseGenres.Where(x => !string.IsNullOrEmpty(x)).GroupBy(x => x).Select(x => x.First()))
2018-12-15 22:35:20 +00:00
{
var rg = releaseGenre.Trim();
if (!string.IsNullOrEmpty(rg))
{
2019-06-03 13:37:13 +00:00
foreach (var g in ID3TagsHelper.SplitGenre(rg))
{
2019-07-03 16:21:29 +00:00
result.Genres.Add(new ReleaseGenre
2019-06-03 13:37:13 +00:00
{
Genre = DbContext.Genres.Where(x => x.Name.ToLower() == g.ToLower()).FirstOrDefault() ?? new Genre
{
Name = g,
NormalizedName = g.ToAlphanumericName()
}
2019-06-03 13:37:13 +00:00
});
}
}
};
2018-12-15 22:35:20 +00:00
}
2019-07-03 16:21:29 +00:00
2018-12-15 22:35:20 +00:00
if (releaseImageUrls.Any())
{
2019-07-03 16:21:29 +00:00
var imageBag = new ConcurrentBag<Image>();
2018-12-15 22:35:20 +00:00
var i = releaseImageUrls.Select(async url =>
{
imageBag.Add(await WebHelper.GetImageFromUrlAsync(url));
});
await Task.WhenAll(i);
// If the release has images merge any fetched images
2019-07-03 16:21:29 +00:00
var existingImages = result.Images != null ? result.Images.ToList() : new List<Image>();
2018-12-15 22:35:20 +00:00
existingImages.AddRange(imageBag.ToList());
// Now set release images to be unique image based on image hash
2019-07-03 16:21:29 +00:00
result.Images = existingImages.Where(x => x != null && x.Bytes != null).GroupBy(x => x.Signature)
.Select(x => x.First()).Take(Configuration.Processing.MaximumReleaseImagesToAdd).ToList();
if (result.Thumbnail == null && result.Images != null) result.Thumbnail = result.Images.First().Bytes;
2018-12-15 22:35:20 +00:00
}
if (releaseLabels.Any())
2019-07-03 16:21:29 +00:00
result.Labels = releaseLabels.GroupBy(x => x.CatalogNumber).Select(x => x.First()).Select(x =>
new ReleaseLabel
2018-12-15 22:35:20 +00:00
{
2019-07-03 16:21:29 +00:00
CatalogNumber = x.CatalogNumber,
BeginDate = x.BeginDate,
EndDate = x.EndDate,
Status = Statuses.New,
Label = new Label
{
Name = x.Label.LabelName,
SortName = x.Label.LabelSortName,
MusicBrainzId = x.Label.MusicBrainzId,
BeginDate = x.Label.StartDate,
EndDate = x.Label.EndDate,
ImageUrl = x.Label.LabelImageUrl,
AlternateNames = x.Label.AlternateNames.ToDelimitedList(),
URLs = x.Label.Urls.ToDelimitedList(),
Status = Statuses.New
}
}).ToList();
2018-12-15 22:35:20 +00:00
if (releaseMedias.Any())
{
2019-07-03 16:21:29 +00:00
var resultReleaseMedias = new List<ReleaseMedia>();
2018-12-15 22:35:20 +00:00
foreach (var releaseMedia in releaseMedias.GroupBy(x => x.ReleaseMediaNumber).Select(x => x.First()))
{
2019-07-03 16:21:29 +00:00
var rm = new ReleaseMedia
2018-12-15 22:35:20 +00:00
{
MediaNumber = releaseMedia.ReleaseMediaNumber ?? 0,
SubTitle = releaseMedia.ReleaseMediaSubTitle,
TrackCount = releaseMedia.TrackCount ?? 0,
Status = Statuses.New
};
2019-07-03 16:21:29 +00:00
var rmTracks = new List<Track>();
foreach (var releaseTrack in releaseMedias
.Where(x => x.ReleaseMediaNumber == releaseMedia.ReleaseMediaNumber)
.SelectMany(x => x.Tracks)
.Where(x => x.TrackNumber.HasValue)
.OrderBy(x => x.TrackNumber))
2018-12-15 22:35:20 +00:00
{
var foundTrack = true;
var rmTrack = rmTracks.FirstOrDefault(x => x.TrackNumber == releaseTrack.TrackNumber.Value);
if (rmTrack == null)
{
2019-07-03 16:21:29 +00:00
Artist trackArtist = null;
2018-12-15 22:35:20 +00:00
if (releaseTrack.Artist != null)
2019-07-03 16:21:29 +00:00
trackArtist = new Artist
2018-12-15 22:35:20 +00:00
{
Name = releaseTrack.Artist.ArtistName,
SpotifyId = releaseTrack.Artist.SpotifyId,
ArtistType = releaseTrack.Artist.ArtistType
};
2019-07-03 16:21:29 +00:00
rmTrack = new Track
2018-12-15 22:35:20 +00:00
{
TrackArtist = trackArtist,
TrackArtists = releaseTrack.Artists,
TrackNumber = releaseTrack.TrackNumber.Value,
MusicBrainzId = releaseTrack.MusicBrainzId,
SpotifyId = releaseTrack.SpotifyId,
AmgId = releaseTrack.AmgId,
Title = releaseTrack.Title,
AlternateNames = releaseTrack.AlternateNames.ToDelimitedList(),
Duration = releaseTrack.Duration,
Tags = releaseTrack.Tags.ToDelimitedList(),
ISRC = releaseTrack.ISRC,
LastFMId = releaseTrack.LastFMId,
Status = Statuses.New
};
foundTrack = false;
}
2019-07-03 16:21:29 +00:00
2018-12-15 22:35:20 +00:00
rmTrack.Duration = rmTrack.Duration ?? releaseTrack.Duration;
rmTrack.MusicBrainzId = rmTrack.MusicBrainzId ?? releaseTrack.MusicBrainzId;
rmTrack.SpotifyId = rmTrack.SpotifyId ?? releaseTrack.SpotifyId;
rmTrack.AmgId = rmTrack.AmgId ?? releaseTrack.AmgId;
rmTrack.Title = rmTrack.Title ?? releaseTrack.Title;
rmTrack.Duration = releaseTrack.Duration;
2019-07-03 16:21:29 +00:00
rmTrack.Tags = rmTrack.Tags == null
? releaseTrack.Tags.ToDelimitedList()
: rmTrack.Tags.AddToDelimitedList(releaseTrack.Tags);
rmTrack.AlternateNames = rmTrack.AlternateNames == null
? releaseTrack.AlternateNames.ToDelimitedList()
: rmTrack.AlternateNames.AddToDelimitedList(releaseTrack.AlternateNames);
2018-12-15 22:35:20 +00:00
rmTrack.ISRC = rmTrack.ISRC ?? releaseTrack.ISRC;
rmTrack.LastFMId = rmTrack.LastFMId ?? releaseTrack.LastFMId;
2019-07-03 16:21:29 +00:00
if (!foundTrack) rmTracks.Add(rmTrack);
2018-12-15 22:35:20 +00:00
}
2019-07-03 16:21:29 +00:00
2018-12-15 22:35:20 +00:00
rm.Tracks = rmTracks;
rm.TrackCount = (short)rmTracks.Count();
resultReleaseMedias.Add(rm);
}
2019-07-03 16:21:29 +00:00
2018-12-15 22:35:20 +00:00
result.Medias = resultReleaseMedias;
result.TrackCount = (short)releaseMedias.SelectMany(x => x.Tracks).Count();
}
if (metaData.Images != null && metaData.Images.Any())
{
var image = metaData.Images.FirstOrDefault(x => x.Type == AudioMetaDataImageType.FrontCover);
2019-07-03 16:21:29 +00:00
if (image == null) image = metaData.Images.FirstOrDefault();
2018-12-15 22:35:20 +00:00
// If there is an image on the metadata file itself then that over-rides metadata providers.
2019-07-03 16:21:29 +00:00
if (image != null) result.Thumbnail = image.Data;
2018-12-15 22:35:20 +00:00
}
2019-07-03 16:21:29 +00:00
2018-12-15 22:35:20 +00:00
if (!string.IsNullOrEmpty(artistFolder))
{
// If any file exist for cover that over-rides whatever if found in metadata providers.
2019-02-10 00:19:26 +00:00
var releaseFolder = new DirectoryInfo(result.ReleaseFileFolder(artistFolder));
if (releaseFolder.Exists)
2018-12-15 22:35:20 +00:00
{
2019-06-02 23:52:09 +00:00
string coverFileName = null;
2019-02-10 00:19:26 +00:00
var cover = ImageHelper.FindImageTypeInDirectory(releaseFolder, ImageType.Release);
2019-07-03 16:21:29 +00:00
if (!cover.Any())
2019-06-02 23:52:09 +00:00
{
// See if cover exist by filename
2019-07-03 16:21:29 +00:00
var imageFilesInFolder =
ImageHelper.ImageFilesInFolder(releaseFolder.FullName, SearchOption.AllDirectories);
2019-06-02 23:52:09 +00:00
if (imageFilesInFolder != null && imageFilesInFolder.Any())
{
2019-07-03 16:21:29 +00:00
var imageCoverByReleaseName = imageFilesInFolder.FirstOrDefault(x =>
x == result.Title || x == result.Title.ToFileNameFriendly());
if (imageCoverByReleaseName != null) coverFileName = imageCoverByReleaseName;
2019-06-02 23:52:09 +00:00
}
}
2019-07-03 16:21:29 +00:00
else if (cover.Any())
2019-06-02 23:52:09 +00:00
{
coverFileName = cover.First().FullName;
}
2019-07-03 16:21:29 +00:00
if (!string.IsNullOrEmpty(coverFileName))
2018-12-15 22:35:20 +00:00
{
// Read image and convert to jpeg
result.Thumbnail = File.ReadAllBytes(coverFileName);
2019-07-03 16:21:29 +00:00
Logger.LogDebug("Using Release Cover File [{0}]", coverFileName);
2018-12-15 22:35:20 +00:00
}
}
}
if (result.Thumbnail != null)
{
2019-07-03 16:21:29 +00:00
result.Thumbnail = ImageHelper.ResizeImage(result.Thumbnail, Configuration.ThumbnailImageSize.Width,
Configuration.ThumbnailImageSize.Height);
2018-12-15 22:35:20 +00:00
result.Thumbnail = ImageHelper.ConvertToJpegFormat(result.Thumbnail);
2019-07-03 16:21:29 +00:00
if (result.Thumbnail.Length >= ImageHelper.MaximumThumbnailByteSize)
2019-06-03 03:13:07 +00:00
{
2019-07-03 16:21:29 +00:00
Logger.LogWarning(
$"Release Thumbnail larger than maximum size after resizing to [{Configuration.ThumbnailImageSize.Width}x{Configuration.ThumbnailImageSize.Height}] Thumbnail Size [{result.Thumbnail.Length}]");
2019-06-03 03:13:07 +00:00
result.Thumbnail = null;
}
2018-12-15 22:35:20 +00:00
}
2019-07-03 16:21:29 +00:00
2018-12-15 22:35:20 +00:00
sw.Stop();
2019-07-03 16:21:29 +00:00
return new OperationResult<Release>
2018-12-15 22:35:20 +00:00
{
Data = result,
IsSuccess = result != null,
Errors = resultsExceptions,
OperationTime = sw.ElapsedMilliseconds
};
}
}
2019-07-03 16:21:29 +00:00
}