add fallback metadata for music videos (#134)

This commit is contained in:
Jason Dove
2021-04-04 15:57:25 -05:00
committed by GitHub
parent 6b44873474
commit 8fea24a3a5
7 changed files with 129 additions and 146 deletions
@@ -8,6 +8,7 @@ namespace ErsatzTV.Core.Interfaces.Metadata
ShowMetadata GetFallbackMetadataForShow(string showFolder);
Tuple<EpisodeMetadata, int> GetFallbackMetadata(Episode episode);
MovieMetadata GetFallbackMetadata(Movie movie);
MusicVideoMetadata GetFallbackMetadata(MusicVideo musicVideo);
string GetSortTitle(string title);
}
}
@@ -1,19 +1,18 @@
using System.Threading.Tasks;
using ErsatzTV.Core.Domain;
using LanguageExt;
namespace ErsatzTV.Core.Interfaces.Metadata
{
public interface ILocalMetadataProvider
{
Task<ShowMetadata> GetMetadataForShow(string showFolder);
Task<Option<MusicVideoMetadata>> GetMetadataForMusicVideo(string filePath);
Task<bool> RefreshSidecarMetadata(Movie movie, string nfoFileName);
Task<bool> RefreshSidecarMetadata(Show televisionShow, string nfoFileName);
Task<bool> RefreshSidecarMetadata(Episode episode, string nfoFileName);
Task<bool> RefreshSidecarMetadata(MusicVideo musicVideo, string nfoFileName);
Task<bool> RefreshFallbackMetadata(Movie movie);
Task<bool> RefreshFallbackMetadata(Episode episode);
Task<bool> RefreshFallbackMetadata(MusicVideo musicVideo);
Task<bool> RefreshFallbackMetadata(Show televisionShow, string showFolder);
}
}
@@ -8,13 +8,7 @@ namespace ErsatzTV.Core.Interfaces.Repositories
{
public interface IMusicVideoRepository
{
Task<Option<MusicVideo>> GetByMetadata(LibraryPath libraryPath, MusicVideoMetadata metadata);
Task<Either<BaseError, MediaItemScanResult<MusicVideo>>> Add(
LibraryPath libraryPath,
string filePath,
MusicVideoMetadata metadata);
Task<Either<BaseError, MediaItemScanResult<MusicVideo>>> GetOrAdd(LibraryPath libraryPath, string path);
Task<IEnumerable<string>> FindMusicVideoPaths(LibraryPath libraryPath);
Task<List<int>> DeleteByPath(LibraryPath libraryPath, string path);
Task<bool> AddGenre(MusicVideoMetadata metadata, Genre genre);
@@ -36,6 +36,19 @@ namespace ErsatzTV.Core.Metadata
return fileName != null ? GetMovieMetadata(fileName, metadata) : metadata;
}
public MusicVideoMetadata GetFallbackMetadata(MusicVideo musicVideo)
{
string path = musicVideo.MediaVersions.Head().MediaFiles.Head().Path;
string fileName = Path.GetFileName(path);
var metadata = new MusicVideoMetadata
{
MetadataKind = MetadataKind.Fallback,
Title = fileName ?? path
};
return fileName != null ? GetMusicVideoMetadata(fileName, metadata) : metadata;
}
public string GetSortTitle(string title)
{
if (string.IsNullOrWhiteSpace(title))
@@ -112,6 +125,30 @@ namespace ErsatzTV.Core.Metadata
return metadata;
}
private MusicVideoMetadata GetMusicVideoMetadata(string fileName, MusicVideoMetadata metadata)
{
try
{
const string PATTERN = @"^(.*?) - (.*?).\w+$";
Match match = Regex.Match(fileName, PATTERN);
if (match.Success)
{
metadata.Artist = match.Groups[1].Value;
metadata.Title = match.Groups[2].Value;
metadata.Genres = new List<Genre>();
metadata.Tags = new List<Tag>();
metadata.Studios = new List<Studio>();
metadata.DateUpdated = DateTime.UtcNow;
}
}
catch (Exception)
{
// ignored
}
return metadata;
}
private ShowMetadata GetTelevisionShowMetadata(string fileName, ShowMetadata metadata)
{
try
@@ -70,23 +70,6 @@ namespace ErsatzTV.Core.Metadata
});
}
public async Task<Option<MusicVideoMetadata>> GetMetadataForMusicVideo(string filePath)
{
string nfoFileName = Path.ChangeExtension(filePath, "nfo");
Option<MusicVideoMetadata> maybeMetadata = None;
if (_localFileSystem.FileExists(nfoFileName))
{
maybeMetadata = await LoadMusicVideoMetadata(nfoFileName);
}
return maybeMetadata.Map(
metadata =>
{
metadata.SortTitle = _fallbackMetadataProvider.GetSortTitle(metadata.Title);
return metadata;
});
}
public Task<bool> RefreshSidecarMetadata(Movie movie, string nfoFileName) =>
LoadMovieMetadata(movie, nfoFileName).Bind(
maybeMetadata => maybeMetadata.Match(
@@ -109,7 +92,7 @@ namespace ErsatzTV.Core.Metadata
LoadMusicVideoMetadata(nfoFileName).Bind(
maybeMetadata => maybeMetadata.Match(
metadata => ApplyMetadataUpdate(musicVideo, metadata),
() => Task.FromResult(false)));
() => RefreshFallbackMetadata(musicVideo)));
public Task<bool> RefreshFallbackMetadata(Movie movie) =>
ApplyMetadataUpdate(movie, _fallbackMetadataProvider.GetFallbackMetadata(movie));
@@ -117,6 +100,9 @@ namespace ErsatzTV.Core.Metadata
public Task<bool> RefreshFallbackMetadata(Episode episode) =>
ApplyMetadataUpdate(episode, _fallbackMetadataProvider.GetFallbackMetadata(episode));
public Task<bool> RefreshFallbackMetadata(MusicVideo musicVideo) =>
ApplyMetadataUpdate(musicVideo, _fallbackMetadataProvider.GetFallbackMetadata(musicVideo));
public Task<bool> RefreshFallbackMetadata(Show televisionShow, string showFolder) =>
ApplyMetadataUpdate(televisionShow, _fallbackMetadataProvider.GetFallbackMetadataForShow(showFolder));
@@ -67,10 +67,7 @@ namespace ErsatzTV.Core.Metadata
var foldersCompleted = 0;
var folderQueue = new Queue<string>();
foreach (string folder in _localFileSystem.ListSubdirectories(libraryPath.Path).OrderBy(identity))
{
folderQueue.Enqueue(folder);
}
folderQueue.Enqueue(libraryPath.Path);
while (folderQueue.Count > 0)
{
@@ -83,20 +80,13 @@ namespace ErsatzTV.Core.Metadata
var allFiles = _localFileSystem.ListFiles(musicVideoFolder)
.Filter(f => VideoFileExtensions.Contains(Path.GetExtension(f)))
.Filter(
f => !ExtraFiles.Any(
e => Path.GetFileNameWithoutExtension(f).EndsWith(e, StringComparison.OrdinalIgnoreCase)))
.Filter(f => f.Contains(" - "))
.ToList();
if (allFiles.Count == 0)
foreach (string subdirectory in _localFileSystem.ListSubdirectories(musicVideoFolder)
.OrderBy(identity))
{
foreach (string subdirectory in _localFileSystem.ListSubdirectories(musicVideoFolder)
.OrderBy(identity))
{
folderQueue.Enqueue(subdirectory);
}
continue;
folderQueue.Enqueue(subdirectory);
}
if (_localFileSystem.GetLastWriteTime(musicVideoFolder) < lastScan)
@@ -107,11 +97,11 @@ namespace ErsatzTV.Core.Metadata
foreach (string file in allFiles.OrderBy(identity))
{
// TODO: figure out how to rebuild playouts
Either<BaseError, MediaItemScanResult<MusicVideo>> maybeMusicVideo =
await FindOrCreateMusicVideo(libraryPath, file)
.BindT(musicVideo => UpdateStatistics(musicVideo, ffprobePath))
.BindT(UpdateMetadata)
.BindT(UpdateThumbnail);
Either<BaseError, MediaItemScanResult<MusicVideo>> maybeMusicVideo = await _musicVideoRepository
.GetOrAdd(libraryPath, file)
.BindT(musicVideo => UpdateStatistics(musicVideo, ffprobePath))
.BindT(UpdateMetadata)
.BindT(UpdateThumbnail);
await maybeMusicVideo.Match(
async result =>
@@ -146,34 +136,13 @@ namespace ErsatzTV.Core.Metadata
return Unit.Default;
}
private async Task<Either<BaseError, MediaItemScanResult<MusicVideo>>> FindOrCreateMusicVideo(
LibraryPath libraryPath,
string filePath)
{
Option<MusicVideoMetadata> maybeMetadata = await _localMetadataProvider.GetMetadataForMusicVideo(filePath);
return await maybeMetadata.Match(
async metadata =>
{
Option<MusicVideo> maybeMusicVideo =
await _musicVideoRepository.GetByMetadata(libraryPath, metadata);
return await maybeMusicVideo.Match(
musicVideo =>
Right<BaseError, MediaItemScanResult<MusicVideo>>(
new MediaItemScanResult<MusicVideo>(musicVideo))
.AsTask(),
async () => await _musicVideoRepository.Add(libraryPath, filePath, metadata));
},
() => Left<BaseError, MediaItemScanResult<MusicVideo>>(
BaseError.New("Unable to locate metadata for music video")).AsTask());
}
private async Task<Either<BaseError, MediaItemScanResult<MusicVideo>>> UpdateMetadata(
MediaItemScanResult<MusicVideo> result)
{
try
{
MusicVideo musicVideo = result.Item;
return await LocateNfoFile(musicVideo).Match<Task<Either<BaseError, MediaItemScanResult<MusicVideo>>>>(
await LocateNfoFile(musicVideo).Match(
async nfoFile =>
{
bool shouldUpdate = Optional(musicVideo.MusicVideoMetadata).Flatten().HeadOrNone().Match(
@@ -189,11 +158,21 @@ namespace ErsatzTV.Core.Metadata
result.IsUpdated = true;
}
}
return result;
},
() => Left<BaseError, MediaItemScanResult<MusicVideo>>(
BaseError.New("Unable to locate metadata for music video")).AsTask());
async () =>
{
if (!Optional(musicVideo.MusicVideoMetadata).Flatten().Any())
{
string path = musicVideo.MediaVersions.Head().MediaFiles.Head().Path;
_logger.LogDebug("Refreshing {Attribute} for {Path}", "Fallback Metadata", path);
if (await _localMetadataProvider.RefreshFallbackMetadata(musicVideo))
{
result.IsUpdated = true;
}
}
});
return result;
}
catch (Exception ex)
{