roadie/Roadie.Api.Library/Engines/ReleaseLookupEngine.cs
2019-11-24 18:19:05 -06:00

953 lines
No EOL
49 KiB
C#

using Microsoft.Extensions.Logging;
using Roadie.Library.Caching;
using Roadie.Library.Configuration;
using Roadie.Library.Data;
using Roadie.Library.Data.Context;
using Roadie.Library.Encoding;
using Roadie.Library.Enums;
using Roadie.Library.Extensions;
using Roadie.Library.Imaging;
using Roadie.Library.MetaData.Audio;
using Roadie.Library.MetaData.ID3Tags;
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
{
public List<int> _addedReleaseIds = new List<int>();
public List<int> _addedTrackIds = new List<int>();
public IEnumerable<int> AddedReleaseIds => _addedReleaseIds;
public IEnumerable<int> AddedTrackIds => _addedTrackIds;
public IReleaseSearchEngine DiscogsReleaseSearchEngine { get; }
public IReleaseSearchEngine ITunesReleaseSearchEngine { get; }
public IReleaseSearchEngine LastFmReleaseSearchEngine { get; }
public IReleaseSearchEngine MusicBrainzReleaseSearchEngine { get; }
public IReleaseSearchEngine SpotifyReleaseSearchEngine { get; }
public IReleaseSearchEngine WikipediaReleaseSearchEngine { get; }
private IArtistLookupEngine ArtistLookupEngine { get; }
private ILabelLookupEngine LabelLookupEngine { get; }
public ReleaseLookupEngine(IRoadieSettings configuration, IHttpEncoder httpEncoder, IRoadieDbContext context,
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;
LabelLookupEngine = labelLookupEngine;
ITunesReleaseSearchEngine = iTunesSearchEngine;
MusicBrainzReleaseSearchEngine = musicBrainzProvider;
LastFmReleaseSearchEngine = lastFmHelper;
DiscogsReleaseSearchEngine = discogsHelper;
SpotifyReleaseSearchEngine = spotifyHelper;
WikipediaReleaseSearchEngine = wikipediaHelper;
}
public async Task<OperationResult<Release>> Add(Release release, bool doAddTracksInDatabase = false)
{
var sw = Stopwatch.StartNew();
SimpleContract.Requires<ArgumentNullException>(release != null, "Invalid Release");
try
{
var releaseGenreTables = release.Genres;
var releaseMedias = release.Medias;
var releaseLabels = release.Labels;
var now = DateTime.UtcNow;
release.AlternateNames = release.AlternateNames.AddToDelimitedList(new[] { release.Title.ToAlphanumericName() });
release.Labels = null;
release.Medias = null;
release.Genres = null;
release.LibraryStatus = LibraryStatus.Incomplete;
release.Status = Statuses.New;
var releaseImages = release.Images ?? new List<Library.Imaging.Image>();
if (!release.IsValid)
{
return new OperationResult<Release>
{
Errors = new Exception[1] { new Exception("Release is Invalid") }
};
}
DbContext.Releases.Add(release);
var inserted = 0;
try
{
inserted = await DbContext.SaveChangesAsync();
}
catch (Exception ex)
{
Logger.LogError(ex, ex.Serialize());
}
if (inserted > 0 && release.Id > 0)
{
_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();
if (string.IsNullOrEmpty(genreName)) continue;
if (releaseGenreTable.Genre.Name.Length > 100)
{
var originalName = releaseGenreTable.Genre.Name;
releaseGenreTable.Genre.Name = releaseGenreTable.Genre.Name.Substring(0, 99);
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)
{
genre = new Genre
{
Name = releaseGenreTable.Genre.Name,
NormalizedName = genreName
};
DbContext.Genres.Add(genre);
await DbContext.SaveChangesAsync();
}
if (genre != null &&
genre.Id > 0 &&
!addedGenreIds.Any(x => x == genre.Id))
{
DbContext.ReleaseGenres.Add(new ReleaseGenre
{
ReleaseId = release.Id,
GenreId = genre.Id
});
addedGenreIds.Add(genre.Id);
}
}
}
if (releaseImages.Any(x => x.Status == Statuses.New))
{
var artistFolder = release.Artist.ArtistFileFolder(Configuration, true);
var releaseFolder = release.ReleaseFileFolder(artistFolder, true);
var looper = -1;
string releaseImageFilename;
foreach (var releaseImage in releaseImages)
{
if(releaseImage?.Bytes == null || releaseImage?.Bytes.Any() == false)
{
continue;
}
releaseImage.Bytes = ImageHelper.ConvertToJpegFormat(releaseImage.Bytes);
if (looper == -1)
{
releaseImageFilename = Path.Combine(releaseFolder, ImageHelper.ReleaseCoverFilename);
}
else
{
releaseImageFilename = Path.Combine(releaseFolder, string.Format(ImageHelper.ReleaseSecondaryImageFilename, looper.ToString("00")));
}
while (File.Exists(releaseImageFilename))
{
looper++;
releaseImageFilename = Path.Combine(releaseFolder,string.Format(ImageHelper.ReleaseSecondaryImageFilename, looper.ToString("00")));
}
File.WriteAllBytes(releaseImageFilename, releaseImage.Bytes);
looper++;
}
}
if (releaseLabels != null && releaseLabels.Any(x => x.Status == Statuses.New))
{
foreach (var neweleaseLabel in releaseLabels.Where(x => x.Status == Statuses.New))
{
var labelFetch = await LabelLookupEngine.GetByName(neweleaseLabel.Label.Name, true);
if (labelFetch.IsSuccess)
DbContext.ReleaseLabels.Add(new ReleaseLabel
{
CatalogNumber = neweleaseLabel.CatalogNumber,
BeginDate = neweleaseLabel.BeginDate,
EndDate = neweleaseLabel.EndDate,
ReleaseId = release.Id,
LabelId = labelFetch.Data.Id
});
}
try
{
await DbContext.SaveChangesAsync();
}
catch (Exception ex)
{
Logger.LogError(ex);
}
}
if (doAddTracksInDatabase)
{
if (releaseMedias != null && releaseMedias.Any(x => x.Status == Statuses.New))
{
foreach (var newReleaseMedia in releaseMedias.Where(x => x.Status == Statuses.New))
{
var releasemedia = new ReleaseMedia
{
Status = Statuses.Incomplete,
MediaNumber = newReleaseMedia.MediaNumber,
SubTitle = newReleaseMedia.SubTitle,
TrackCount = newReleaseMedia.TrackCount,
ReleaseId = release.Id
};
var releasemediatracks = new List<Track>();
foreach (var newTrack in newReleaseMedia.Tracks)
{
int? trackArtistId = null;
string partTitles = null;
if (newTrack.TrackArtist != null)
{
if (!release.IsCastRecording)
{
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())
{
partTitles = string.Join("/", newTrack.TrackArtists);
}
else
{
partTitles = newTrack.TrackArtist.Name;
}
}
releasemediatracks.Add(new Track
{
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
});
}
releasemedia.Tracks = releasemediatracks;
DbContext.ReleaseMedias.Add(releasemedia);
_addedTrackIds.AddRange(releasemedia.Tracks.Select(x => x.Id));
}
try
{
await DbContext.SaveChangesAsync();
}
catch (Exception ex)
{
Logger.LogError(ex);
}
}
}
sw.Stop();
Logger.LogTrace($"Added New Release: Elapsed Time [{ sw.ElapsedMilliseconds }], Release `{ release }`");
}
}
catch (Exception ex)
{
Logger.LogError(ex, ex.Serialize());
}
return new OperationResult<Release>
{
IsSuccess = release.Id > 0,
Data = release,
OperationTime = sw.ElapsedMilliseconds
};
}
public Release DatabaseQueryForReleaseTitle(Artist artist, string title, string sortTitle = null)
{
if (string.IsNullOrEmpty(title))
{
return null;
}
try
{
var searchName = title.NormalizeName();
var searchSortName = !string.IsNullOrEmpty(sortTitle) ? sortTitle.NormalizeName().ToLower() : searchName;
var specialSearchName = title.ToAlphanumericName();
var searchNameStart = $"{searchName}|";
var searchNameIn = $"|{searchName}|";
var searchNameEnd = $"|{searchName}";
var specialSearchNameStart = $"{specialSearchName}|";
var specialSearchNameIn = $"|{specialSearchName}|";
var specialSearchNameEnd = $"|{specialSearchName}";
return (from a in DbContext.Releases
where a.ArtistId == artist.Id
where a.Title == searchName ||
a.Title == specialSearchName ||
a.SortTitle == searchName ||
a.SortTitle == searchSortName ||
a.SortTitle == specialSearchName ||
a.AlternateNames.StartsWith(searchNameStart) ||
a.AlternateNames.Contains(searchNameIn) ||
a.AlternateNames.EndsWith(searchNameEnd) ||
a.AlternateNames.StartsWith(specialSearchNameStart) ||
a.AlternateNames.Contains(specialSearchNameIn) ||
a.AlternateNames.EndsWith(specialSearchNameEnd)
select a
).FirstOrDefault();
}
catch (Exception ex)
{
Logger.LogError(ex, ex.Serialize());
}
return null;
}
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 release = DatabaseQueryForReleaseTitle(artist, metaData.Release);
//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.LogTrace("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), 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)
{
SimpleContract.Requires<ArgumentNullException>(metaData != null, "Invalid MetaData");
var sw = new Stopwatch();
sw.Start();
var result = new Release
{
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
if (metaData.Genres != null) releaseGenres.AddRange(metaData.Genres);
var releaseLabels = new List<ReleaseLabelSearchResult>();
var releaseMedias = new List<ReleaseMediaSearchResult>();
var releaseImages = new List<Imaging.IImage>();
var releaseImageUrls = new List<string>();
var dontDoMetaDataProvidersSearchArtists = Configuration.DontDoMetaDataProvidersSearchArtists;
if (!dontDoMetaDataProvidersSearchArtists.Any(x =>
x.Equals(metaData.Artist, StringComparison.OrdinalIgnoreCase)))
{
try
{
#region ITunes
if (ITunesReleaseSearchEngine.IsEnabled)
{
var sw2 = Stopwatch.StartNew();
Logger.LogTrace("ITunesReleaseSearchEngine Release Search for ArtistName [{0}], ReleaseTitle [{1}]", metaData.Artist, result.Title);
var iTunesResult = await ITunesReleaseSearchEngine.PerformReleaseSearch(metaData.Artist, result.Title, 1);
if (iTunesResult.IsSuccess)
{
var i = iTunesResult.Data.First();
if (i.AlternateNames != null)
result.AlternateNames = result.AlternateNames.AddToDelimitedList(i.AlternateNames);
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);
if(!string.IsNullOrEmpty(i.ReleaseThumbnailUrl))
{
releaseImages.Add(new Imaging.Image()
{
Bytes = WebHelper.BytesForImageUrl(i.ReleaseThumbnailUrl)
});
}
result.CopyTo(new Release
{
ReleaseDate = result.ReleaseDate ?? i.ReleaseDate,
AmgId = i.AmgId,
Profile = i.Profile,
ITunesId = i.iTunesId,
Title = result.Title ?? i.ReleaseTitle,
ReleaseType = result.ReleaseType == ReleaseType.Unknown
? SafeParser.ToEnum<ReleaseType>(i.ReleaseType)
: result.ReleaseType
});
if (i.ReleaseLabel != null) releaseLabels.AddRange(i.ReleaseLabel);
if (i.ReleaseMedia != null) releaseMedias.AddRange(i.ReleaseMedia);
}
if (iTunesResult.Errors != null) resultsExceptions.AddRange(iTunesResult.Errors);
sw2.Stop();
Logger.LogTrace($"PerformMetaDataProvidersReleaseSearch: ITunesArtistSearchEngine Complete [{ sw2.ElapsedMilliseconds }]");
}
#endregion ITunes
#region MusicBrainz
if (MusicBrainzReleaseSearchEngine.IsEnabled)
{
var sw2 = Stopwatch.StartNew();
Logger.LogTrace("MusicBrainzReleaseSearchEngine Release Search for ArtistName [{0}], ReleaseTitle [{1}]", metaData.Artist, result.Title);
var mbResult = await MusicBrainzReleaseSearchEngine.PerformReleaseSearch(metaData.Artist, result.Title, 1);
if (mbResult.IsSuccess)
{
var mb = mbResult.Data.First();
if (mb.AlternateNames != null)
result.AlternateNames = result.AlternateNames.AddToDelimitedList(mb.AlternateNames);
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 });
if (!string.IsNullOrEmpty(mb.ReleaseThumbnailUrl))
{
releaseImages.Add(new Imaging.Image()
{
Bytes = WebHelper.BytesForImageUrl(mb.ReleaseThumbnailUrl)
});
}
result.CopyTo(new Release
{
ReleaseDate = result.ReleaseDate ?? mb.ReleaseDate,
AmgId = mb.AmgId,
Profile = mb.Profile,
TrackCount = mb.ReleaseMedia != null
? (short)mb.ReleaseMedia.Sum(x => x.TrackCount)
: (short)0,
MusicBrainzId = mb.MusicBrainzId,
ITunesId = mb.iTunesId,
Title = result.Title ?? mb.ReleaseTitle,
ReleaseType = result.ReleaseType == ReleaseType.Unknown
? SafeParser.ToEnum<ReleaseType>(mb.ReleaseType)
: result.ReleaseType
});
if (mb.ReleaseLabel != null) releaseLabels.AddRange(mb.ReleaseLabel);
if (mb.ReleaseMedia != null) releaseMedias.AddRange(mb.ReleaseMedia);
}
if (mbResult.Errors != null) resultsExceptions.AddRange(mbResult.Errors);
sw2.Stop();
Logger.LogTrace($"PerformMetaDataProvidersReleaseSearch: MusicBrainzReleaseSearchEngine Complete [{ sw2.ElapsedMilliseconds }]");
}
#endregion MusicBrainz
#region LastFm
if (LastFmReleaseSearchEngine.IsEnabled)
{
var sw2 = Stopwatch.StartNew();
Logger.LogTrace("LastFmReleaseSearchEngine Release Search for ArtistName [{0}], ReleaseTitle [{1}]", metaData.Artist, result.Title);
var lastFmResult =
await LastFmReleaseSearchEngine.PerformReleaseSearch(metaData.Artist, result.Title, 1);
if (lastFmResult.IsSuccess)
{
var l = lastFmResult.Data.First();
if (l.AlternateNames != null)
result.AlternateNames = result.AlternateNames.AddToDelimitedList(l.AlternateNames);
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 });
if (!string.IsNullOrEmpty(l.ReleaseThumbnailUrl))
{
releaseImages.Add(new Imaging.Image()
{
Bytes = WebHelper.BytesForImageUrl(l.ReleaseThumbnailUrl)
});
}
result.CopyTo(new Release
{
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,
ReleaseType = result.ReleaseType == ReleaseType.Unknown
? SafeParser.ToEnum<ReleaseType>(l.ReleaseType)
: result.ReleaseType
});
if (l.ReleaseLabel != null) releaseLabels.AddRange(l.ReleaseLabel);
if (l.ReleaseMedia != null) releaseMedias.AddRange(l.ReleaseMedia);
}
if (lastFmResult.Errors != null) resultsExceptions.AddRange(lastFmResult.Errors);
sw2.Stop();
Logger.LogTrace($"PerformMetaDataProvidersReleaseSearch: LastFmReleaseSearchEngine Complete [{ sw2.ElapsedMilliseconds }]");
}
#endregion LastFm
#region Spotify
if (SpotifyReleaseSearchEngine.IsEnabled)
{
var sw2 = Stopwatch.StartNew();
Logger.LogTrace("SpotifyReleaseSearchEngine Release Search for ArtistName [{0}], ReleaseTitle [{1}]", metaData.Artist, result.Title);
var spotifyResult = await SpotifyReleaseSearchEngine.PerformReleaseSearch(metaData.Artist, result.Title, 1);
if (spotifyResult.IsSuccess)
{
var s = spotifyResult.Data.First();
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 });
if (!string.IsNullOrEmpty(s.ReleaseThumbnailUrl))
{
releaseImages.Add(new Imaging.Image()
{
Bytes = WebHelper.BytesForImageUrl(s.ReleaseThumbnailUrl)
});
}
result.CopyTo(new Release
{
ReleaseDate = result.ReleaseDate ?? s.ReleaseDate,
AmgId = s.AmgId,
Profile = HttpEncoder.HtmlEncode(s.Profile),
SpotifyId = s.SpotifyId,
MusicBrainzId = s.MusicBrainzId,
ITunesId = s.iTunesId,
Title = result.Title ?? s.ReleaseTitle,
ReleaseType = result.ReleaseType == ReleaseType.Unknown
? SafeParser.ToEnum<ReleaseType>(s.ReleaseType)
: result.ReleaseType
});
if (s.ReleaseLabel != null) releaseLabels.AddRange(s.ReleaseLabel);
if (s.ReleaseMedia != null) releaseMedias.AddRange(s.ReleaseMedia);
}
if (spotifyResult.Errors != null) resultsExceptions.AddRange(spotifyResult.Errors);
sw2.Stop();
Logger.LogTrace($"PerformMetaDataProvidersReleaseSearch: SpotifyReleaseSearchEngine Complete [{ sw2.ElapsedMilliseconds }]");
}
#endregion Spotify
#region Discogs
if (DiscogsReleaseSearchEngine.IsEnabled)
{
var sw2 = Stopwatch.StartNew();
Logger.LogTrace("DiscogsReleaseSearchEngine Release Search for ArtistName [{0}], ReleaseTitle [{1}]", metaData.Artist, result.Title);
var discogsResult = await DiscogsReleaseSearchEngine.PerformReleaseSearch(metaData.Artist, result.Title, 1);
if (discogsResult.IsSuccess)
{
var d = discogsResult.Data.First();
if (d.Urls != null) result.URLs = result.URLs.AddToDelimitedList(d.Urls);
if (d.ImageUrls != null) releaseImageUrls.AddRange(d.ImageUrls);
if (d.AlternateNames != null)
result.AlternateNames = result.AlternateNames.AddToDelimitedList(d.AlternateNames);
if (!string.IsNullOrEmpty(d.ReleaseTitle) &&
!d.ReleaseTitle.Equals(result.Title, StringComparison.OrdinalIgnoreCase))
result.AlternateNames.AddToDelimitedList(new[] { d.ReleaseTitle });
if (!string.IsNullOrEmpty(d.ReleaseThumbnailUrl))
{
releaseImages.Add(new Imaging.Image()
{
Bytes = WebHelper.BytesForImageUrl(d.ReleaseThumbnailUrl)
});
}
result.CopyTo(new Release
{
Profile = HttpEncoder.HtmlEncode(d.Profile),
DiscogsId = d.DiscogsId,
Title = result.Title ?? d.ReleaseTitle,
ReleaseType = result.ReleaseType == ReleaseType.Unknown
? SafeParser.ToEnum<ReleaseType>(d.ReleaseType)
: result.ReleaseType
});
if (d.ReleaseLabel != null) releaseLabels.AddRange(d.ReleaseLabel);
if (d.ReleaseMedia != null) releaseMedias.AddRange(d.ReleaseMedia);
}
if (discogsResult.Errors != null) resultsExceptions.AddRange(discogsResult.Errors);
sw2.Stop();
Logger.LogTrace($"PerformMetaDataProvidersReleaseSearch: DiscogsReleaseSearchEngine Complete [{ sw2.ElapsedMilliseconds }]");
}
#endregion Discogs
}
catch (Exception ex)
{
Logger.LogError(ex);
}
Logger.LogTrace("Metadata Providers Search Complete. [{0}]", sw.ElapsedMilliseconds);
}
else
{
Logger.LogTrace("Skipped Metadata Providers Search, DontDoMetaDataProvidersSearchArtists set for Artist [{0}].", metaData.Artist);
}
if (result.AlternateNames != null)
{
result.AlternateNames = string.Join("|", result.AlternateNames.ToListFromDelimited().Distinct().OrderBy(x => x));
}
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())
{
var sw2 = Stopwatch.StartNew();
result.Genres = new List<ReleaseGenre>();
foreach (var releaseGenre in releaseGenres.Where(x => !string.IsNullOrEmpty(x)).GroupBy(x => x).Select(x => x.First()))
{
var rg = releaseGenre.Trim();
if (!string.IsNullOrEmpty(rg))
{
foreach (var g in ID3TagsHelper.SplitGenre(rg))
{
result.Genres.Add(new ReleaseGenre
{
Genre = DbContext.Genres.Where(x => x.Name.ToLower() == g.ToLower()).FirstOrDefault() ?? new Genre
{
Name = g,
NormalizedName = g.ToAlphanumericName()
}
});
}
}
};
sw2.Stop();
Logger.LogTrace($"PerformMetaDataProvidersReleaseSearch: Release Genre Processing Complete [{ sw2.ElapsedMilliseconds }]");
}
if (releaseImageUrls.Any())
{
var sw2 = Stopwatch.StartNew();
var imageBag = new ConcurrentBag<IImage>();
var i = releaseImageUrls.Select(async url =>
{
imageBag.Add(await WebHelper.GetImageFromUrlAsync(url));
});
await Task.WhenAll(i);
releaseImages = imageBag.Where(x => x != null && x.Bytes != null)
.GroupBy(x => x.Signature)
.Select(x => x.First())
.Take(Configuration.Processing.MaximumArtistImagesToAdd)
.ToList();
sw2.Stop();
Logger.LogTrace($"PerformMetaDataProvidersReleaseSearch: Image Url Processing Complete [{ sw2.ElapsedMilliseconds }]");
}
if (metaData.Images != null && metaData.Images.Any())
{
foreach(var metadataImage in metaData.Images)
{
releaseImages.Add(new Imaging.Image()
{
Bytes = metadataImage?.Data
});
}
}
foreach(var releaseImage in releaseImages.Where(x => x.Bytes != null && string.IsNullOrEmpty(x.Signature)))
{
releaseImage.Signature = releaseImage.GenerateSignature();
if(string.IsNullOrEmpty(releaseImage.Signature))
{
releaseImage.Bytes = null;
}
}
result.Images = releaseImages.Where(x => x.Bytes != null)
.GroupBy(x => x.Signature)
.Select(x => x.First()).Take(Configuration.Processing.MaximumReleaseImagesToAdd)
.ToList();
if (releaseLabels.Any())
{
var sw2 = Stopwatch.StartNew();
result.Labels = releaseLabels.GroupBy(x => x.CatalogNumber).Select(x => x.First()).Select(x =>
new ReleaseLabel
{
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();
sw2.Stop();
Logger.LogTrace($"PerformMetaDataProvidersReleaseSearch: Release Labels Processing Complete [{ sw2.ElapsedMilliseconds }]");
}
if (releaseMedias.Any())
{
var sw2 = Stopwatch.StartNew();
var resultReleaseMedias = new List<ReleaseMedia>();
foreach (var releaseMedia in releaseMedias.GroupBy(x => x.ReleaseMediaNumber).Select(x => x.First()))
{
var rm = new ReleaseMedia
{
MediaNumber = releaseMedia.ReleaseMediaNumber ?? 0,
SubTitle = releaseMedia.ReleaseMediaSubTitle,
TrackCount = releaseMedia.TrackCount ?? 0,
Status = Statuses.New
};
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))
{
var foundTrack = true;
var rmTrack = rmTracks.FirstOrDefault(x => x.TrackNumber == releaseTrack.TrackNumber.Value);
if (rmTrack == null)
{
Artist trackArtist = null;
if (releaseTrack.Artist != null)
{
trackArtist = new Artist
{
Name = releaseTrack.Artist.ArtistName,
SpotifyId = releaseTrack.Artist.SpotifyId,
ArtistType = releaseTrack.Artist.ArtistType
};
}
rmTrack = new Track
{
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;
}
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;
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);
rmTrack.ISRC = rmTrack.ISRC ?? releaseTrack.ISRC;
rmTrack.LastFMId = rmTrack.LastFMId ?? releaseTrack.LastFMId;
if (!foundTrack) rmTracks.Add(rmTrack);
}
rm.Tracks = rmTracks;
rm.TrackCount = (short)rmTracks.Count();
resultReleaseMedias.Add(rm);
}
result.Medias = resultReleaseMedias;
result.TrackCount = (short)releaseMedias.SelectMany(x => x.Tracks).Count();
sw2.Stop();
Logger.LogTrace($"PerformMetaDataProvidersReleaseSearch: Release Media Processing Complete [{ sw2.ElapsedMilliseconds }]");
}
if (!string.IsNullOrEmpty(artistFolder))
{
// If any file exist for cover that over-rides whatever if found in metadata providers.
var releaseFolder = new DirectoryInfo(result.ReleaseFileFolder(artistFolder));
if (releaseFolder.Exists)
{
string coverFileName = null;
var cover = ImageHelper.FindImageTypeInDirectory(releaseFolder, ImageType.Release);
if (!cover.Any())
{
// See if cover exist by filename
var imageFilesInFolder =
ImageHelper.ImageFilesInFolder(releaseFolder.FullName, SearchOption.AllDirectories);
if (imageFilesInFolder != null && imageFilesInFolder.Any())
{
var imageCoverByReleaseName = imageFilesInFolder.FirstOrDefault(x =>
x == result.Title || x == result.Title.ToFileNameFriendly());
if (imageCoverByReleaseName != null) coverFileName = imageCoverByReleaseName;
}
}
else if (cover.Any())
{
coverFileName = cover.First().FullName;
}
}
}
sw.Stop();
return new OperationResult<Release>
{
Data = result,
IsSuccess = result != null,
Errors = resultsExceptions,
OperationTime = sw.ElapsedMilliseconds
};
}
}
}