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:
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user