support movie nfo metadata in other video libraries (#788)
* add other video nfo metadata * update docs
This commit is contained in:
@@ -24,6 +24,7 @@ public class LocalMetadataProvider : ILocalMetadataProvider
|
||||
private readonly IMovieRepository _movieRepository;
|
||||
private readonly IMusicVideoNfoReader _musicVideoNfoReader;
|
||||
private readonly IMusicVideoRepository _musicVideoRepository;
|
||||
private readonly IOtherVideoNfoReader _otherVideoNfoReader;
|
||||
private readonly IOtherVideoRepository _otherVideoRepository;
|
||||
private readonly ISongRepository _songRepository;
|
||||
private readonly ITelevisionRepository _televisionRepository;
|
||||
@@ -44,6 +45,7 @@ public class LocalMetadataProvider : ILocalMetadataProvider
|
||||
IArtistNfoReader artistNfoReader,
|
||||
IMusicVideoNfoReader musicVideoNfoReader,
|
||||
ITvShowNfoReader tvShowNfoReader,
|
||||
IOtherVideoNfoReader otherVideoNfoReader,
|
||||
ILocalStatisticsProvider localStatisticsProvider,
|
||||
IClient client,
|
||||
ILogger<LocalMetadataProvider> logger)
|
||||
@@ -62,6 +64,7 @@ public class LocalMetadataProvider : ILocalMetadataProvider
|
||||
_artistNfoReader = artistNfoReader;
|
||||
_musicVideoNfoReader = musicVideoNfoReader;
|
||||
_tvShowNfoReader = tvShowNfoReader;
|
||||
_otherVideoNfoReader = otherVideoNfoReader;
|
||||
_localStatisticsProvider = localStatisticsProvider;
|
||||
_client = client;
|
||||
_logger = logger;
|
||||
@@ -107,38 +110,77 @@ public class LocalMetadataProvider : ILocalMetadataProvider
|
||||
return fallbackMetadata;
|
||||
}
|
||||
|
||||
public Task<bool> RefreshSidecarMetadata(Movie movie, string nfoFileName) =>
|
||||
LoadMovieMetadata(movie, nfoFileName).Bind(
|
||||
maybeMetadata => maybeMetadata.Match(
|
||||
metadata => ApplyMetadataUpdate(movie, metadata),
|
||||
() => Task.FromResult(false)));
|
||||
public async Task<bool> RefreshSidecarMetadata(Movie movie, string nfoFileName)
|
||||
{
|
||||
Option<MovieMetadata> maybeMetadata = await LoadMovieMetadata(movie, nfoFileName);
|
||||
foreach (MovieMetadata metadata in maybeMetadata)
|
||||
{
|
||||
return await ApplyMetadataUpdate(movie, metadata);
|
||||
}
|
||||
|
||||
public Task<bool> RefreshSidecarMetadata(Show televisionShow, string nfoFileName) =>
|
||||
LoadTelevisionShowMetadata(nfoFileName).Bind(
|
||||
maybeMetadata => maybeMetadata.Match(
|
||||
metadata => ApplyMetadataUpdate(televisionShow, metadata),
|
||||
() => Task.FromResult(false)));
|
||||
return false;
|
||||
}
|
||||
|
||||
public Task<bool> RefreshSidecarMetadata(Episode episode, string nfoFileName) =>
|
||||
LoadEpisodeMetadata(episode, nfoFileName).Bind(metadata => ApplyMetadataUpdate(episode, metadata));
|
||||
public async Task<bool> RefreshSidecarMetadata(Show televisionShow, string nfoFileName)
|
||||
{
|
||||
Option<ShowMetadata> maybeMetadata = await LoadTelevisionShowMetadata(nfoFileName);
|
||||
foreach (ShowMetadata metadata in maybeMetadata)
|
||||
{
|
||||
return await ApplyMetadataUpdate(televisionShow, metadata);
|
||||
}
|
||||
|
||||
public Task<bool> RefreshSidecarMetadata(Artist artist, string nfoFileName) =>
|
||||
LoadArtistMetadata(nfoFileName).Bind(
|
||||
maybeMetadata => maybeMetadata.Match(
|
||||
metadata => ApplyMetadataUpdate(artist, metadata),
|
||||
() => Task.FromResult(false)));
|
||||
return false;
|
||||
}
|
||||
|
||||
public Task<bool> RefreshSidecarMetadata(MusicVideo musicVideo, string nfoFileName) =>
|
||||
LoadMusicVideoMetadata(nfoFileName).Bind(
|
||||
maybeMetadata => maybeMetadata.Match(
|
||||
metadata => ApplyMetadataUpdate(musicVideo, metadata),
|
||||
() => RefreshFallbackMetadata(musicVideo)));
|
||||
public async Task<bool> RefreshSidecarMetadata(Episode episode, string nfoFileName)
|
||||
{
|
||||
List<EpisodeMetadata> metadata = await LoadEpisodeMetadata(episode, nfoFileName);
|
||||
return await ApplyMetadataUpdate(episode, metadata);
|
||||
}
|
||||
|
||||
public Task<bool> RefreshTagMetadata(Song song, string ffprobePath) =>
|
||||
LoadSongMetadata(song, ffprobePath).Bind(
|
||||
maybeMetadata => maybeMetadata.Match(
|
||||
metadata => ApplyMetadataUpdate(song, metadata),
|
||||
() => RefreshFallbackMetadata(song)));
|
||||
public async Task<bool> RefreshSidecarMetadata(Artist artist, string nfoFileName)
|
||||
{
|
||||
Option<ArtistMetadata> maybeMetadata = await LoadArtistMetadata(nfoFileName);
|
||||
foreach (ArtistMetadata metadata in maybeMetadata)
|
||||
{
|
||||
return await ApplyMetadataUpdate(artist, metadata);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public async Task<bool> RefreshSidecarMetadata(MusicVideo musicVideo, string nfoFileName)
|
||||
{
|
||||
Option<MusicVideoMetadata> maybeMetadata = await LoadMusicVideoMetadata(nfoFileName);
|
||||
foreach (MusicVideoMetadata metadata in maybeMetadata)
|
||||
{
|
||||
return await ApplyMetadataUpdate(musicVideo, metadata);
|
||||
}
|
||||
|
||||
return await RefreshFallbackMetadata(musicVideo);
|
||||
}
|
||||
|
||||
public async Task<bool> RefreshSidecarMetadata(OtherVideo otherVideo, string nfoFileName)
|
||||
{
|
||||
Option<OtherVideoMetadata> maybeMetadata = await LoadOtherVideoMetadata(nfoFileName);
|
||||
foreach (OtherVideoMetadata metadata in maybeMetadata)
|
||||
{
|
||||
return await ApplyMetadataUpdate(otherVideo, metadata);
|
||||
}
|
||||
|
||||
return await RefreshFallbackMetadata(otherVideo);
|
||||
}
|
||||
|
||||
public async Task<bool> RefreshTagMetadata(Song song, string ffprobePath)
|
||||
{
|
||||
Option<SongMetadata> maybeMetadata = await LoadSongMetadata(song, ffprobePath);
|
||||
foreach (SongMetadata metadata in maybeMetadata)
|
||||
{
|
||||
return await ApplyMetadataUpdate(song, metadata);
|
||||
}
|
||||
|
||||
return await RefreshFallbackMetadata(song);
|
||||
}
|
||||
|
||||
public Task<bool> RefreshFallbackMetadata(Movie movie) =>
|
||||
ApplyMetadataUpdate(movie, _fallbackMetadataProvider.GetFallbackMetadata(movie));
|
||||
@@ -149,20 +191,38 @@ public class LocalMetadataProvider : ILocalMetadataProvider
|
||||
public Task<bool> RefreshFallbackMetadata(Artist artist, string artistFolder) =>
|
||||
ApplyMetadataUpdate(artist, _fallbackMetadataProvider.GetFallbackMetadataForArtist(artistFolder));
|
||||
|
||||
public Task<bool> RefreshFallbackMetadata(OtherVideo otherVideo) =>
|
||||
_fallbackMetadataProvider.GetFallbackMetadata(otherVideo).Match(
|
||||
metadata => ApplyMetadataUpdate(otherVideo, metadata),
|
||||
() => Task.FromResult(false));
|
||||
public async Task<bool> RefreshFallbackMetadata(OtherVideo otherVideo)
|
||||
{
|
||||
Option<OtherVideoMetadata> maybeMetadata = _fallbackMetadataProvider.GetFallbackMetadata(otherVideo);
|
||||
foreach (OtherVideoMetadata metadata in maybeMetadata)
|
||||
{
|
||||
return await ApplyMetadataUpdate(otherVideo, metadata);
|
||||
}
|
||||
|
||||
public Task<bool> RefreshFallbackMetadata(Song song) =>
|
||||
_fallbackMetadataProvider.GetFallbackMetadata(song).Match(
|
||||
metadata => ApplyMetadataUpdate(song, metadata),
|
||||
() => Task.FromResult(false));
|
||||
return false;
|
||||
}
|
||||
|
||||
public Task<bool> RefreshFallbackMetadata(MusicVideo musicVideo) =>
|
||||
_fallbackMetadataProvider.GetFallbackMetadata(musicVideo).Match(
|
||||
metadata => ApplyMetadataUpdate(musicVideo, metadata),
|
||||
() => Task.FromResult(false));
|
||||
public async Task<bool> RefreshFallbackMetadata(Song song)
|
||||
{
|
||||
Option<SongMetadata> maybeMetadata = _fallbackMetadataProvider.GetFallbackMetadata(song);
|
||||
foreach (SongMetadata metadata in maybeMetadata)
|
||||
{
|
||||
return await ApplyMetadataUpdate(song, metadata);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public async Task<bool> RefreshFallbackMetadata(MusicVideo musicVideo)
|
||||
{
|
||||
Option<MusicVideoMetadata> maybeMetadata = _fallbackMetadataProvider.GetFallbackMetadata(musicVideo);
|
||||
foreach (MusicVideoMetadata metadata in maybeMetadata)
|
||||
{
|
||||
return await ApplyMetadataUpdate(musicVideo, metadata);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public Task<bool> RefreshFallbackMetadata(Show televisionShow, string showFolder) =>
|
||||
ApplyMetadataUpdate(televisionShow, _fallbackMetadataProvider.GetFallbackMetadataForShow(showFolder));
|
||||
@@ -767,6 +827,10 @@ public class LocalMetadataProvider : ILocalMetadataProvider
|
||||
Option<OtherVideoMetadata> maybeMetadata = Optional(otherVideo.OtherVideoMetadata).Flatten().HeadOrNone();
|
||||
foreach (OtherVideoMetadata existing in maybeMetadata)
|
||||
{
|
||||
existing.ContentRating = metadata.ContentRating;
|
||||
existing.Outline = metadata.Outline;
|
||||
existing.Plot = metadata.Plot;
|
||||
existing.Tagline = metadata.Tagline;
|
||||
existing.Title = metadata.Title;
|
||||
|
||||
if (existing.DateAdded == SystemTime.MinValueUtc)
|
||||
@@ -776,6 +840,9 @@ public class LocalMetadataProvider : ILocalMetadataProvider
|
||||
|
||||
existing.DateUpdated = metadata.DateUpdated;
|
||||
existing.MetadataKind = metadata.MetadataKind;
|
||||
existing.OriginalTitle = metadata.OriginalTitle;
|
||||
existing.ReleaseDate = metadata.ReleaseDate;
|
||||
existing.Year = metadata.Year;
|
||||
existing.SortTitle = string.IsNullOrWhiteSpace(metadata.SortTitle)
|
||||
? _fallbackMetadataProvider.GetSortTitle(metadata.Title)
|
||||
: metadata.SortTitle;
|
||||
@@ -784,10 +851,70 @@ public class LocalMetadataProvider : ILocalMetadataProvider
|
||||
bool updated = await UpdateMetadataCollections(
|
||||
existing,
|
||||
metadata,
|
||||
(_, _) => Task.FromResult(false),
|
||||
_otherVideoRepository.AddGenre,
|
||||
_otherVideoRepository.AddTag,
|
||||
(_, _) => Task.FromResult(false),
|
||||
(_, _) => Task.FromResult(false));
|
||||
_otherVideoRepository.AddStudio,
|
||||
_otherVideoRepository.AddActor);
|
||||
|
||||
foreach (Director director in existing.Directors
|
||||
.Filter(d => metadata.Directors.All(d2 => d2.Name != d.Name)).ToList())
|
||||
{
|
||||
existing.Directors.Remove(director);
|
||||
if (await _metadataRepository.RemoveDirector(director))
|
||||
{
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Director director in metadata.Directors
|
||||
.Filter(d => existing.Directors.All(d2 => d2.Name != d.Name)).ToList())
|
||||
{
|
||||
existing.Directors.Add(director);
|
||||
if (await _otherVideoRepository.AddDirector(existing, director))
|
||||
{
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Writer writer in existing.Writers
|
||||
.Filter(w => metadata.Writers.All(w2 => w2.Name != w.Name)).ToList())
|
||||
{
|
||||
existing.Writers.Remove(writer);
|
||||
if (await _metadataRepository.RemoveWriter(writer))
|
||||
{
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Writer writer in metadata.Writers
|
||||
.Filter(w => existing.Writers.All(w2 => w2.Name != w.Name)).ToList())
|
||||
{
|
||||
existing.Writers.Add(writer);
|
||||
if (await _otherVideoRepository.AddWriter(existing, writer))
|
||||
{
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (MetadataGuid guid in existing.Guids
|
||||
.Filter(g => metadata.Guids.All(g2 => g2.Guid != g.Guid)).ToList())
|
||||
{
|
||||
existing.Guids.Remove(guid);
|
||||
if (await _metadataRepository.RemoveGuid(guid))
|
||||
{
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (MetadataGuid guid in metadata.Guids
|
||||
.Filter(g => existing.Guids.All(g2 => g2.Guid != g.Guid)).ToList())
|
||||
{
|
||||
existing.Guids.Add(guid);
|
||||
if (await _metadataRepository.AddGuid(existing, guid))
|
||||
{
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
return await _metadataRepository.Update(existing) || updated;
|
||||
}
|
||||
@@ -1068,6 +1195,81 @@ public class LocalMetadataProvider : ILocalMetadataProvider
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<Option<OtherVideoMetadata>> LoadOtherVideoMetadata(string nfoFileName)
|
||||
{
|
||||
try
|
||||
{
|
||||
await using FileStream fileStream = File.Open(nfoFileName, FileMode.Open, FileAccess.Read);
|
||||
Either<BaseError, OtherVideoNfo> maybeNfo = await _otherVideoNfoReader.Read(fileStream);
|
||||
foreach (BaseError error in maybeNfo.LeftToSeq())
|
||||
{
|
||||
_logger.LogInformation(
|
||||
"Failed to read OtherVideo nfo metadata from {Path}: {Error}",
|
||||
nfoFileName,
|
||||
error.ToString());
|
||||
|
||||
return None;
|
||||
}
|
||||
|
||||
foreach (OtherVideoNfo nfo in maybeNfo.RightToSeq())
|
||||
{
|
||||
DateTime dateAdded = DateTime.UtcNow;
|
||||
DateTime dateUpdated = File.GetLastWriteTimeUtc(nfoFileName);
|
||||
|
||||
var year = 0;
|
||||
if (nfo.Year > 1000)
|
||||
{
|
||||
year = nfo.Year;
|
||||
}
|
||||
|
||||
DateTime releaseDate = year > 0
|
||||
? new DateTimeOffset(year, 1, 1, 0, 0, 0, TimeSpan.Zero).UtcDateTime
|
||||
: SystemTime.MinValueUtc;
|
||||
|
||||
foreach (DateTime premiered in nfo.Premiered)
|
||||
{
|
||||
if (year == 0)
|
||||
{
|
||||
year = premiered.Year;
|
||||
}
|
||||
|
||||
releaseDate = premiered;
|
||||
}
|
||||
|
||||
return new OtherVideoMetadata
|
||||
{
|
||||
MetadataKind = MetadataKind.Sidecar,
|
||||
DateAdded = dateAdded,
|
||||
DateUpdated = dateUpdated,
|
||||
Title = nfo.Title,
|
||||
SortTitle = nfo.SortTitle,
|
||||
Year = year,
|
||||
ContentRating = nfo.ContentRating,
|
||||
ReleaseDate = releaseDate,
|
||||
Plot = nfo.Plot,
|
||||
Outline = nfo.Outline,
|
||||
Tagline = nfo.Tagline,
|
||||
Genres = nfo.Genres.Map(g => new Genre { Name = g }).ToList(),
|
||||
Tags = nfo.Tags.Map(t => new Tag { Name = t }).ToList(),
|
||||
Studios = nfo.Studios.Map(s => new Studio { Name = s }).ToList(),
|
||||
Actors = Actors(nfo.Actors, dateAdded, dateUpdated),
|
||||
Directors = nfo.Directors.Map(d => new Director { Name = d }).ToList(),
|
||||
Writers = nfo.Writers.Map(w => new Writer { Name = w }).ToList(),
|
||||
Guids = nfo.UniqueIds
|
||||
.Map(id => new MetadataGuid { Guid = $"{id.Type}://{id.Guid}" })
|
||||
.ToList()
|
||||
};
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogInformation(ex, "Failed to read OtherVideo nfo metadata from {Path}", nfoFileName);
|
||||
_client.Notify(ex);
|
||||
}
|
||||
|
||||
return None;
|
||||
}
|
||||
|
||||
private static int? GetYear(int? year, Option<DateTime> premiered)
|
||||
{
|
||||
if (year is > 1000)
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace ErsatzTV.Core.Metadata.Nfo;
|
||||
|
||||
[XmlRoot("movie")]
|
||||
public class OtherVideoNfo
|
||||
{
|
||||
[XmlElement("title")]
|
||||
public string Title { get; set; }
|
||||
|
||||
[XmlElement("sorttitle")]
|
||||
public string SortTitle { get; set; }
|
||||
|
||||
[XmlElement("outline")]
|
||||
public string Outline { get; set; }
|
||||
|
||||
[XmlElement("year")]
|
||||
public int Year { get; set; }
|
||||
|
||||
[XmlElement("mpaa")]
|
||||
public string ContentRating { get; set; }
|
||||
|
||||
[XmlElement("premiered")]
|
||||
public Option<DateTime> Premiered { get; set; }
|
||||
|
||||
[XmlElement("plot")]
|
||||
public string Plot { get; set; }
|
||||
|
||||
[XmlElement("tagline")]
|
||||
public string Tagline { get; set; }
|
||||
|
||||
[XmlElement("genre")]
|
||||
public List<string> Genres { get; set; }
|
||||
|
||||
[XmlElement("tag")]
|
||||
public List<string> Tags { get; set; }
|
||||
|
||||
[XmlElement("studio")]
|
||||
public List<string> Studios { get; set; }
|
||||
|
||||
[XmlElement("actor")]
|
||||
public List<ActorNfo> Actors { get; set; }
|
||||
|
||||
[XmlElement("credits")]
|
||||
public List<string> Writers { get; set; }
|
||||
|
||||
[XmlElement("director")]
|
||||
public List<string> Directors { get; set; }
|
||||
|
||||
[XmlElement("uniqueid")]
|
||||
public List<UniqueIdNfo> UniqueIds { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
using System.Xml;
|
||||
using Bugsnag;
|
||||
using ErsatzTV.Core.Errors;
|
||||
using ErsatzTV.Core.Interfaces.Metadata.Nfo;
|
||||
|
||||
namespace ErsatzTV.Core.Metadata.Nfo;
|
||||
|
||||
public class OtherVideoNfoReader : NfoReader<OtherVideoNfo>, IOtherVideoNfoReader
|
||||
{
|
||||
private readonly IClient _client;
|
||||
|
||||
public OtherVideoNfoReader(IClient client) => _client = client;
|
||||
|
||||
public async Task<Either<BaseError, OtherVideoNfo>> Read(Stream input)
|
||||
{
|
||||
try
|
||||
{
|
||||
var settings = new XmlReaderSettings { Async = true, ConformanceLevel = ConformanceLevel.Fragment };
|
||||
using var reader = XmlReader.Create(input, settings);
|
||||
OtherVideoNfo nfo = null;
|
||||
var done = false;
|
||||
|
||||
while (!done && await reader.ReadAsync())
|
||||
{
|
||||
switch (reader.NodeType)
|
||||
{
|
||||
case XmlNodeType.Element:
|
||||
switch (reader.Name.ToLowerInvariant())
|
||||
{
|
||||
case "movie":
|
||||
nfo = new OtherVideoNfo
|
||||
{
|
||||
Genres = new List<string>(),
|
||||
Tags = new List<string>(),
|
||||
Studios = new List<string>(),
|
||||
Actors = new List<ActorNfo>(),
|
||||
Writers = new List<string>(),
|
||||
Directors = new List<string>(),
|
||||
UniqueIds = new List<UniqueIdNfo>()
|
||||
};
|
||||
break;
|
||||
case "title":
|
||||
await ReadStringContent(reader, nfo, (movie, title) => movie.Title = title);
|
||||
break;
|
||||
case "sorttitle":
|
||||
await ReadStringContent(reader, nfo, (movie, sortTitle) => movie.SortTitle = sortTitle);
|
||||
break;
|
||||
case "outline":
|
||||
await ReadStringContent(reader, nfo, (movie, outline) => movie.Outline = outline);
|
||||
break;
|
||||
case "year":
|
||||
await ReadIntContent(reader, nfo, (movie, year) => movie.Year = year);
|
||||
break;
|
||||
case "mpaa":
|
||||
await ReadStringContent(
|
||||
reader,
|
||||
nfo,
|
||||
(movie, contentRating) => movie.ContentRating = contentRating);
|
||||
break;
|
||||
case "premiered":
|
||||
await ReadDateTimeContent(
|
||||
reader,
|
||||
nfo,
|
||||
(movie, premiered) => movie.Premiered = premiered);
|
||||
break;
|
||||
case "plot":
|
||||
await ReadStringContent(reader, nfo, (movie, plot) => movie.Plot = plot);
|
||||
break;
|
||||
case "genre":
|
||||
await ReadStringContent(reader, nfo, (movie, genre) => movie.Genres.Add(genre));
|
||||
break;
|
||||
case "tag":
|
||||
await ReadStringContent(reader, nfo, (movie, tag) => movie.Tags.Add(tag));
|
||||
break;
|
||||
case "studio":
|
||||
await ReadStringContent(reader, nfo, (movie, studio) => movie.Studios.Add(studio));
|
||||
break;
|
||||
case "actor":
|
||||
ReadActor(reader, nfo, (movie, actor) => movie.Actors.Add(actor));
|
||||
break;
|
||||
case "credits":
|
||||
await ReadStringContent(reader, nfo, (movie, writer) => movie.Writers.Add(writer));
|
||||
break;
|
||||
case "director":
|
||||
await ReadStringContent(
|
||||
reader,
|
||||
nfo,
|
||||
(movie, director) => movie.Directors.Add(director));
|
||||
break;
|
||||
case "uniqueid":
|
||||
await ReadUniqueId(reader, nfo, (movie, uniqueid) => movie.UniqueIds.Add(uniqueid));
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
case XmlNodeType.EndElement:
|
||||
if (reader.Name == "movie")
|
||||
{
|
||||
done = true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return Optional(nfo).ToEither((BaseError)new FailedToReadNfo());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_client.Notify(ex);
|
||||
return new FailedToReadNfo(ex.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,15 +77,17 @@ public class OtherVideoFolderScanner : LocalFolderScanner, IOtherVideoFolderScan
|
||||
|
||||
var foldersCompleted = 0;
|
||||
|
||||
var allFolders = new System.Collections.Generic.HashSet<string>();
|
||||
var folderQueue = new Queue<string>();
|
||||
|
||||
if (ShouldIncludeFolder(libraryPath.Path))
|
||||
if (ShouldIncludeFolder(libraryPath.Path) && allFolders.Add(libraryPath.Path))
|
||||
{
|
||||
folderQueue.Enqueue(libraryPath.Path);
|
||||
}
|
||||
|
||||
foreach (string folder in _localFileSystem.ListSubdirectories(libraryPath.Path)
|
||||
.Filter(ShouldIncludeFolder)
|
||||
.Filter(allFolders.Add)
|
||||
.OrderBy(identity))
|
||||
{
|
||||
folderQueue.Enqueue(folder);
|
||||
@@ -115,6 +117,7 @@ public class OtherVideoFolderScanner : LocalFolderScanner, IOtherVideoFolderScan
|
||||
|
||||
foreach (string subdirectory in _localFileSystem.ListSubdirectories(otherVideoFolder)
|
||||
.Filter(ShouldIncludeFolder)
|
||||
.Filter(allFolders.Add)
|
||||
.OrderBy(identity))
|
||||
{
|
||||
folderQueue.Enqueue(subdirectory);
|
||||
@@ -205,15 +208,38 @@ public class OtherVideoFolderScanner : LocalFolderScanner, IOtherVideoFolderScan
|
||||
try
|
||||
{
|
||||
OtherVideo otherVideo = result.Item;
|
||||
if (!Optional(otherVideo.OtherVideoMetadata).Flatten().Any())
|
||||
{
|
||||
otherVideo.OtherVideoMetadata ??= new List<OtherVideoMetadata>();
|
||||
string path = otherVideo.MediaVersions.Head().MediaFiles.Head().Path;
|
||||
|
||||
string path = otherVideo.MediaVersions.Head().MediaFiles.Head().Path;
|
||||
_logger.LogDebug("Refreshing {Attribute} for {Path}", "Fallback Metadata", path);
|
||||
if (await _localMetadataProvider.RefreshFallbackMetadata(otherVideo))
|
||||
Option<string> maybeNfoFile = new List<string> { Path.ChangeExtension(path, "nfo") }
|
||||
.Filter(_localFileSystem.FileExists)
|
||||
.HeadOrNone();
|
||||
|
||||
if (maybeNfoFile.IsNone)
|
||||
{
|
||||
if (!Optional(otherVideo.OtherVideoMetadata).Flatten().Any())
|
||||
{
|
||||
result.IsUpdated = true;
|
||||
_logger.LogDebug("Refreshing {Attribute} for {Path}", "Fallback Metadata", path);
|
||||
if (await _localMetadataProvider.RefreshFallbackMetadata(otherVideo))
|
||||
{
|
||||
result.IsUpdated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (string nfoFile in maybeNfoFile)
|
||||
{
|
||||
bool shouldUpdate = Optional(otherVideo.OtherVideoMetadata).Flatten().HeadOrNone().Match(
|
||||
m => m.MetadataKind == MetadataKind.Fallback ||
|
||||
m.DateUpdated != _localFileSystem.GetLastWriteTime(nfoFile),
|
||||
true);
|
||||
|
||||
if (shouldUpdate)
|
||||
{
|
||||
_logger.LogDebug("Refreshing {Attribute} from {Path}", "Sidecar Metadata", nfoFile);
|
||||
if (await _localMetadataProvider.RefreshSidecarMetadata(otherVideo, nfoFile))
|
||||
{
|
||||
result.IsUpdated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user