more local metadata parsing improvements (#776)

* extract remaining nfo xml serializers

* add artist nfo tests

* add music video nfo tests

* add tv show nfo reader tests

* custom artist nfo reader

* custom music video nfo reader

* custom tv show nfo reader

* local metadata parsing cleanup

* update changelog
This commit is contained in:
Jason Dove
2022-05-01 14:00:10 -05:00
committed by GitHub
parent 2a8bf57930
commit ecb6ed37f0
20 changed files with 1259 additions and 169 deletions
@@ -0,0 +1,83 @@
using System.Xml;
using Bugsnag;
using ErsatzTV.Core.Errors;
using ErsatzTV.Core.Interfaces.Metadata.Nfo;
namespace ErsatzTV.Core.Metadata.Nfo;
public class ArtistNfoReader : NfoReader<ArtistNfo>, IArtistNfoReader
{
private readonly IClient _client;
public ArtistNfoReader(IClient client) => _client = client;
public async Task<Either<BaseError, ArtistNfo>> Read(Stream input)
{
try
{
var settings = new XmlReaderSettings { Async = true, ConformanceLevel = ConformanceLevel.Fragment };
using var reader = XmlReader.Create(input, settings);
ArtistNfo nfo = null;
var done = false;
while (!done && await reader.ReadAsync())
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
switch (reader.Name.ToLowerInvariant())
{
case "artist":
nfo = new ArtistNfo
{
Genres = new List<string>(),
Styles = new List<string>(),
Moods = new List<string>()
};
break;
case "name":
await ReadStringContent(reader, nfo, (artist, name) => artist.Name = name);
break;
case "disambiguation":
await ReadStringContent(
reader,
nfo,
(artist, disambiguation) => artist.Disambiguation = disambiguation);
break;
case "genre":
await ReadStringContent(reader, nfo, (artist, genre) => artist.Genres.Add(genre));
break;
case "style":
await ReadStringContent(reader, nfo, (artist, style) => artist.Styles.Add(style));
break;
case "mood":
await ReadStringContent(reader, nfo, (artist, mood) => artist.Moods.Add(mood));
break;
case "biography":
await ReadStringContent(
reader,
nfo,
(artist, biography) => artist.Biography = biography);
break;
}
break;
case XmlNodeType.EndElement:
if (reader.Name == "artist")
{
done = true;
}
break;
}
}
return Optional(nfo).ToEither((BaseError)new FailedToReadNfo());
}
catch (Exception ex)
{
_client.Notify(ex);
return new FailedToReadNfo(ex.ToString());
}
}
}
+96 -85
View File
@@ -1,104 +1,115 @@
using System.Xml;
using Bugsnag;
using ErsatzTV.Core.Errors;
using ErsatzTV.Core.Interfaces.Metadata.Nfo;
namespace ErsatzTV.Core.Metadata.Nfo;
public class EpisodeNfoReader : NfoReader<TvShowEpisodeNfo>, IEpisodeNfoReader
{
public async Task<List<TvShowEpisodeNfo>> Read(Stream input)
private readonly IClient _client;
public EpisodeNfoReader(IClient client) => _client = client;
public async Task<Either<BaseError, List<TvShowEpisodeNfo>>> Read(Stream input)
{
var result = new List<TvShowEpisodeNfo>();
var settings = new XmlReaderSettings { Async = true, ConformanceLevel = ConformanceLevel.Fragment };
using var reader = XmlReader.Create(input, settings);
TvShowEpisodeNfo nfo = null;
while (await reader.ReadAsync())
try
{
switch (reader.NodeType)
var result = new List<TvShowEpisodeNfo>();
var settings = new XmlReaderSettings { Async = true, ConformanceLevel = ConformanceLevel.Fragment };
using var reader = XmlReader.Create(input, settings);
TvShowEpisodeNfo nfo = null;
while (await reader.ReadAsync())
{
case XmlNodeType.Element:
switch (reader.Name.ToLowerInvariant())
{
case "episodedetails":
nfo = new TvShowEpisodeNfo
{
UniqueIds = new List<UniqueIdNfo>(),
Actors = new List<ActorNfo>(),
Writers = new List<string>(),
Directors = new List<string>()
};
break;
case "title":
await ReadStringContent(reader, nfo, (episode, title) => episode.Title = title);
break;
case "showtitle":
await ReadStringContent(reader, nfo, (episode, showTitle) => episode.ShowTitle = showTitle);
break;
case "episode":
await ReadIntContent(
reader,
nfo,
(episode, episodeNumber) => episode.Episode = episodeNumber);
break;
case "season":
await ReadIntContent(reader, nfo, (episode, seasonNumber) => episode.Season = seasonNumber);
break;
case "uniqueid":
await ReadUniqueId(reader, nfo, (episode, uniqueId) => episode.UniqueIds.Add(uniqueId));
break;
case "mpaa":
await ReadStringContent(
reader,
nfo,
(episode, contentRating) => episode.ContentRating = contentRating);
break;
case "aired":
// TODO: parse the date here
await ReadAired(reader, nfo);
break;
case "plot":
await ReadStringContent(reader, nfo, (episode, plot) => episode.Plot = plot);
break;
case "actor":
ReadActor(reader, nfo, (episode, actor) => episode.Actors.Add(actor));
break;
case "credits":
await ReadStringContent(reader, nfo, (episode, writer) => episode.Writers.Add(writer));
break;
case "director":
await ReadStringContent(
reader,
nfo,
(episode, director) => episode.Directors.Add(director));
break;
}
switch (reader.NodeType)
{
case XmlNodeType.Element:
switch (reader.Name.ToLowerInvariant())
{
case "episodedetails":
nfo = new TvShowEpisodeNfo
{
UniqueIds = new List<UniqueIdNfo>(),
Actors = new List<ActorNfo>(),
Writers = new List<string>(),
Directors = new List<string>()
};
break;
case "title":
await ReadStringContent(reader, nfo, (episode, title) => episode.Title = title);
break;
case "showtitle":
await ReadStringContent(
reader,
nfo,
(episode, showTitle) => episode.ShowTitle = showTitle);
break;
case "episode":
await ReadIntContent(
reader,
nfo,
(episode, episodeNumber) => episode.Episode = episodeNumber);
break;
case "season":
await ReadIntContent(
reader,
nfo,
(episode, seasonNumber) => episode.Season = seasonNumber);
break;
case "uniqueid":
await ReadUniqueId(reader, nfo, (episode, uniqueId) => episode.UniqueIds.Add(uniqueId));
break;
case "mpaa":
await ReadStringContent(
reader,
nfo,
(episode, contentRating) => episode.ContentRating = contentRating);
break;
case "aired":
await ReadDateTimeContent(reader, nfo, (episode, aired) => episode.Aired = aired);
break;
case "plot":
await ReadStringContent(reader, nfo, (episode, plot) => episode.Plot = plot);
break;
case "actor":
ReadActor(reader, nfo, (episode, actor) => episode.Actors.Add(actor));
break;
case "credits":
await ReadStringContent(reader, nfo, (episode, writer) => episode.Writers.Add(writer));
break;
case "director":
await ReadStringContent(
reader,
nfo,
(episode, director) => episode.Directors.Add(director));
break;
}
break;
case XmlNodeType.EndElement:
switch (reader.Name.ToLowerInvariant())
{
case "episodedetails":
if (nfo != null)
{
result.Add(nfo);
}
break;
case XmlNodeType.EndElement:
switch (reader.Name.ToLowerInvariant())
{
case "episodedetails":
if (nfo != null)
{
result.Add(nfo);
}
break;
}
break;
}
break;
break;
}
}
return result;
}
return result;
}
private static async Task ReadAired(XmlReader reader, TvShowEpisodeNfo nfo)
{
if (nfo != null)
catch (Exception ex)
{
nfo.Aired = await reader.ReadElementContentAsStringAsync();
_client.Notify(ex);
return new FailedToReadNfo(ex.ToString());
}
}
}
+1 -1
View File
@@ -21,7 +21,7 @@ public class MovieNfo
public string ContentRating { get; set; }
[XmlElement("premiered")]
public DateTime Premiered { get; set; }
public Option<DateTime> Premiered { get; set; }
[XmlElement("plot")]
public string Plot { get; set; }
@@ -17,9 +17,6 @@ public class MusicVideoNfo
[XmlElement("plot")]
public string Plot { get; set; }
[XmlElement("premiered")]
public string Premiered { get; set; }
[XmlElement("year")]
public int Year { get; set; }
@@ -0,0 +1,92 @@
using System.Xml;
using Bugsnag;
using ErsatzTV.Core.Errors;
using ErsatzTV.Core.Interfaces.Metadata.Nfo;
namespace ErsatzTV.Core.Metadata.Nfo;
public class MusicVideoNfoReader : NfoReader<MusicVideoNfo>, IMusicVideoNfoReader
{
private readonly IClient _client;
public MusicVideoNfoReader(IClient client) => _client = client;
public async Task<Either<BaseError, MusicVideoNfo>> Read(Stream input)
{
try
{
var settings = new XmlReaderSettings { Async = true, ConformanceLevel = ConformanceLevel.Fragment };
using var reader = XmlReader.Create(input, settings);
MusicVideoNfo nfo = null;
var done = false;
while (!done && await reader.ReadAsync())
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
switch (reader.Name.ToLowerInvariant())
{
case "musicvideo":
nfo = new MusicVideoNfo
{
Genres = new List<string>(),
Tags = new List<string>(),
Studios = new List<string>()
};
break;
case "artist":
await ReadStringContent(
reader,
nfo,
(musicVideo, artist) => musicVideo.Artist = artist);
break;
case "title":
await ReadStringContent(reader, nfo, (musicVideo, title) => musicVideo.Title = title);
break;
case "album":
await ReadStringContent(reader, nfo, (musicVideo, album) => musicVideo.Album = album);
break;
case "plot":
await ReadStringContent(reader, nfo, (musicVideo, plot) => musicVideo.Plot = plot);
break;
case "year":
await ReadIntContent(reader, nfo, (musicVideo, year) => musicVideo.Year = year);
break;
case "genre":
await ReadStringContent(
reader,
nfo,
(musicVideo, genre) => musicVideo.Genres.Add(genre));
break;
case "tag":
await ReadStringContent(reader, nfo, (musicVideo, tag) => musicVideo.Tags.Add(tag));
break;
case "studio":
await ReadStringContent(
reader,
nfo,
(musicVideo, studio) => musicVideo.Studios.Add(studio));
break;
}
break;
case XmlNodeType.EndElement:
if (reader.Name == "musicvideo")
{
done = true;
}
break;
}
}
return Optional(nfo).ToEither((BaseError)new FailedToReadNfo());
}
catch (Exception ex)
{
_client.Notify(ex);
return new FailedToReadNfo(ex.ToString());
}
}
}
@@ -21,7 +21,7 @@ public class TvShowEpisodeNfo
public string ContentRating { get; set; }
[XmlElement("aired")]
public string Aired { get; set; }
public Option<DateTime> Aired { get; set; }
[XmlElement("plot")]
public string Plot { get; set; }
+1 -1
View File
@@ -31,7 +31,7 @@ public class TvShowNfo
public string ContentRating { get; set; }
[XmlElement("premiered")]
public string Premiered { get; set; }
public Option<DateTime> Premiered { get; set; }
[XmlElement("genre")]
public List<string> Genres { get; set; }
@@ -0,0 +1,100 @@
using System.Xml;
using Bugsnag;
using ErsatzTV.Core.Errors;
using ErsatzTV.Core.Interfaces.Metadata.Nfo;
namespace ErsatzTV.Core.Metadata.Nfo;
public class TvShowNfoReader : NfoReader<TvShowNfo>, ITvShowNfoReader
{
private readonly IClient _client;
public TvShowNfoReader(IClient client) => _client = client;
public async Task<Either<BaseError, TvShowNfo>> Read(Stream input)
{
try
{
var settings = new XmlReaderSettings { Async = true, ConformanceLevel = ConformanceLevel.Fragment };
using var reader = XmlReader.Create(input, settings);
TvShowNfo nfo = null;
var done = false;
while (!done && await reader.ReadAsync())
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
switch (reader.Name.ToLowerInvariant())
{
case "tvshow":
nfo = new TvShowNfo
{
Genres = new List<string>(),
Tags = new List<string>(),
Studios = new List<string>(),
Actors = new List<ActorNfo>(),
UniqueIds = new List<UniqueIdNfo>()
};
break;
case "title":
await ReadStringContent(reader, nfo, (show, title) => show.Title = title);
break;
case "year":
await ReadIntContent(reader, nfo, (show, year) => show.Year = year);
break;
case "plot":
await ReadStringContent(reader, nfo, (show, plot) => show.Plot = plot);
break;
case "outline":
await ReadStringContent(reader, nfo, (show, outline) => show.Outline = outline);
break;
case "tagline":
await ReadStringContent(reader, nfo, (show, tagline) => show.Tagline = tagline);
break;
case "mpaa":
await ReadStringContent(
reader,
nfo,
(show, contentRating) => show.ContentRating = contentRating);
break;
case "premiered":
await ReadDateTimeContent(reader, nfo, (show, premiered) => show.Premiered = premiered);
break;
case "genre":
await ReadStringContent(reader, nfo, (show, genre) => show.Genres.Add(genre));
break;
case "tag":
await ReadStringContent(reader, nfo, (show, tag) => show.Tags.Add(tag));
break;
case "studio":
await ReadStringContent(reader, nfo, (show, studio) => show.Studios.Add(studio));
break;
case "actor":
ReadActor(reader, nfo, (episode, actor) => episode.Actors.Add(actor));
break;
case "uniqueid":
await ReadUniqueId(reader, nfo, (episode, uniqueid) => episode.UniqueIds.Add(uniqueid));
break;
}
break;
case XmlNodeType.EndElement:
if (reader.Name == "tvshow")
{
done = true;
}
break;
}
}
return Optional(nfo).ToEither((BaseError)new FailedToReadNfo());
}
catch (Exception ex)
{
_client.Notify(ex);
return new FailedToReadNfo(ex.ToString());
}
}
}