bug fixes (#774)
* add custom movie metadata parsing * refactor episode nfo reader * fix emby and jellyfin bugs
This commit is contained in:
@@ -1,10 +1,9 @@
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
using ErsatzTV.Core.Interfaces.Metadata.Nfo;
|
||||
|
||||
namespace ErsatzTV.Core.Metadata.Nfo;
|
||||
|
||||
public class EpisodeNfoReader : IEpisodeNfoReader
|
||||
public class EpisodeNfoReader : NfoReader<TvShowEpisodeNfo>, IEpisodeNfoReader
|
||||
{
|
||||
public async Task<List<TvShowEpisodeNfo>> Read(Stream input)
|
||||
{
|
||||
@@ -31,38 +30,47 @@ public class EpisodeNfoReader : IEpisodeNfoReader
|
||||
};
|
||||
break;
|
||||
case "title":
|
||||
await ReadTitle(reader, nfo);
|
||||
await ReadStringContent(reader, nfo, (episode, title) => episode.Title = title);
|
||||
break;
|
||||
case "showtitle":
|
||||
await ReadShowTitle(reader, nfo);
|
||||
await ReadStringContent(reader, nfo, (episode, showTitle) => episode.ShowTitle = showTitle);
|
||||
break;
|
||||
case "episode":
|
||||
await ReadEpisode(reader, nfo);
|
||||
await ReadIntContent(
|
||||
reader,
|
||||
nfo,
|
||||
(episode, episodeNumber) => episode.Episode = episodeNumber);
|
||||
break;
|
||||
case "season":
|
||||
await ReadSeason(reader, nfo);
|
||||
await ReadIntContent(reader, nfo, (episode, seasonNumber) => episode.Season = seasonNumber);
|
||||
break;
|
||||
case "uniqueid":
|
||||
await ReadUniqueId(reader, nfo);
|
||||
await ReadUniqueId(reader, nfo, (episode, uniqueId) => episode.UniqueIds.Add(uniqueId));
|
||||
break;
|
||||
case "mpaa":
|
||||
await ReadContentRating(reader, nfo);
|
||||
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 ReadPlot(reader, nfo);
|
||||
await ReadStringContent(reader, nfo, (episode, plot) => episode.Plot = plot);
|
||||
break;
|
||||
case "actor":
|
||||
ReadActor(reader, nfo);
|
||||
ReadActor(reader, nfo, (episode, actor) => episode.Actors.Add(actor));
|
||||
break;
|
||||
case "credits":
|
||||
await ReadWriter(reader, nfo);
|
||||
await ReadStringContent(reader, nfo, (episode, writer) => episode.Writers.Add(writer));
|
||||
break;
|
||||
case "director":
|
||||
await ReadDirector(reader, nfo);
|
||||
await ReadStringContent(
|
||||
reader,
|
||||
nfo,
|
||||
(episode, director) => episode.Directors.Add(director));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -86,64 +94,6 @@ public class EpisodeNfoReader : IEpisodeNfoReader
|
||||
return result;
|
||||
}
|
||||
|
||||
private static async Task ReadTitle(XmlReader reader, TvShowEpisodeNfo nfo)
|
||||
{
|
||||
if (nfo != null)
|
||||
{
|
||||
nfo.Title = await reader.ReadElementContentAsStringAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task ReadShowTitle(XmlReader reader, TvShowEpisodeNfo nfo)
|
||||
{
|
||||
if (nfo != null)
|
||||
{
|
||||
nfo.ShowTitle = await reader.ReadElementContentAsStringAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task ReadEpisode(XmlReader reader, TvShowEpisodeNfo nfo)
|
||||
{
|
||||
if (nfo != null)
|
||||
{
|
||||
bool _ = int.TryParse(await reader.ReadElementContentAsStringAsync(), out int episode);
|
||||
nfo.Episode = episode;
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task ReadSeason(XmlReader reader, TvShowEpisodeNfo nfo)
|
||||
{
|
||||
if (nfo != null)
|
||||
{
|
||||
bool _ = int.TryParse(await reader.ReadElementContentAsStringAsync(), out int season);
|
||||
nfo.Season = season;
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task ReadUniqueId(XmlReader reader, TvShowEpisodeNfo nfo)
|
||||
{
|
||||
if (nfo != null)
|
||||
{
|
||||
var uniqueId = new UniqueIdNfo();
|
||||
reader.MoveToAttribute("default");
|
||||
uniqueId.Default = bool.TryParse(reader.Value, out bool def) && def;
|
||||
reader.MoveToAttribute("type");
|
||||
uniqueId.Type = reader.Value;
|
||||
reader.MoveToElement();
|
||||
uniqueId.Guid = await reader.ReadElementContentAsStringAsync();
|
||||
|
||||
nfo.UniqueIds.Add(uniqueId);
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task ReadContentRating(XmlReader reader, TvShowEpisodeNfo nfo)
|
||||
{
|
||||
if (nfo != null)
|
||||
{
|
||||
nfo.ContentRating = await reader.ReadElementContentAsStringAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task ReadAired(XmlReader reader, TvShowEpisodeNfo nfo)
|
||||
{
|
||||
if (nfo != null)
|
||||
@@ -151,47 +101,4 @@ public class EpisodeNfoReader : IEpisodeNfoReader
|
||||
nfo.Aired = await reader.ReadElementContentAsStringAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task ReadPlot(XmlReader reader, TvShowEpisodeNfo nfo)
|
||||
{
|
||||
if (nfo != null)
|
||||
{
|
||||
nfo.Plot = await reader.ReadElementContentAsStringAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private static void ReadActor(XmlReader reader, TvShowEpisodeNfo nfo)
|
||||
{
|
||||
if (nfo != null)
|
||||
{
|
||||
var actor = new ActorNfo();
|
||||
var element = (XElement)XNode.ReadFrom(reader);
|
||||
|
||||
XElement name = element.Element("name");
|
||||
if (name != null)
|
||||
{
|
||||
actor.Name = name.Value;
|
||||
}
|
||||
|
||||
XElement role = element.Element("role");
|
||||
if (role != null)
|
||||
{
|
||||
actor.Role = role.Value;
|
||||
}
|
||||
|
||||
XElement thumb = element.Element("thumb");
|
||||
if (thumb != null)
|
||||
{
|
||||
actor.Thumb = thumb.Value;
|
||||
}
|
||||
|
||||
nfo.Actors.Add(actor);
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task ReadWriter(XmlReader reader, TvShowEpisodeNfo nfo) =>
|
||||
nfo?.Writers.Add(await reader.ReadElementContentAsStringAsync());
|
||||
|
||||
private static async Task ReadDirector(XmlReader reader, TvShowEpisodeNfo nfo) =>
|
||||
nfo?.Directors.Add(await reader.ReadElementContentAsStringAsync());
|
||||
}
|
||||
|
||||
@@ -8,6 +8,9 @@ public class MovieNfo
|
||||
[XmlElement("title")]
|
||||
public string Title { get; set; }
|
||||
|
||||
[XmlElement("sorttitle")]
|
||||
public string SortTitle { get; set; }
|
||||
|
||||
[XmlElement("outline")]
|
||||
public string Outline { 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 MovieNfoReader : NfoReader<MovieNfo>, IMovieNfoReader
|
||||
{
|
||||
private readonly IClient _client;
|
||||
|
||||
public MovieNfoReader(IClient client) => _client = client;
|
||||
|
||||
public async Task<Either<BaseError, MovieNfo>> Read(Stream input)
|
||||
{
|
||||
try
|
||||
{
|
||||
var settings = new XmlReaderSettings { Async = true, ConformanceLevel = ConformanceLevel.Fragment };
|
||||
using var reader = XmlReader.Create(input, settings);
|
||||
MovieNfo nfo = null;
|
||||
var done = false;
|
||||
|
||||
while (!done && await reader.ReadAsync())
|
||||
{
|
||||
switch (reader.NodeType)
|
||||
{
|
||||
case XmlNodeType.Element:
|
||||
switch (reader.Name.ToLowerInvariant())
|
||||
{
|
||||
case "movie":
|
||||
nfo = new MovieNfo
|
||||
{
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace ErsatzTV.Core.Metadata.Nfo;
|
||||
|
||||
public abstract class NfoReader<T>
|
||||
{
|
||||
protected static async Task ReadStringContent(XmlReader reader, T nfo, Action<T, string> action)
|
||||
{
|
||||
if (nfo != null)
|
||||
{
|
||||
string result = await reader.ReadElementContentAsStringAsync();
|
||||
action(nfo, result);
|
||||
}
|
||||
}
|
||||
|
||||
protected static async Task ReadIntContent(XmlReader reader, T nfo, Action<T, int> action)
|
||||
{
|
||||
if (nfo != null && int.TryParse(await reader.ReadElementContentAsStringAsync(), out int result))
|
||||
{
|
||||
action(nfo, result);
|
||||
}
|
||||
}
|
||||
|
||||
protected static async Task ReadDateTimeContent(XmlReader reader, T nfo, Action<T, DateTime> action)
|
||||
{
|
||||
if (nfo != null && DateTime.TryParse(await reader.ReadElementContentAsStringAsync(), out DateTime result))
|
||||
{
|
||||
action(nfo, result);
|
||||
}
|
||||
}
|
||||
|
||||
protected static void ReadActor(XmlReader reader, T nfo, Action<T, ActorNfo> action)
|
||||
{
|
||||
if (nfo != null)
|
||||
{
|
||||
var actor = new ActorNfo();
|
||||
var element = (XElement)XNode.ReadFrom(reader);
|
||||
|
||||
XElement name = element.Element("name");
|
||||
if (name != null)
|
||||
{
|
||||
actor.Name = name.Value;
|
||||
}
|
||||
|
||||
XElement role = element.Element("role");
|
||||
if (role != null)
|
||||
{
|
||||
actor.Role = role.Value;
|
||||
}
|
||||
|
||||
XElement order = element.Element("order");
|
||||
if (order != null && int.TryParse(order.Value, out int orderValue))
|
||||
{
|
||||
actor.Order = orderValue;
|
||||
}
|
||||
|
||||
XElement thumb = element.Element("thumb");
|
||||
if (thumb != null)
|
||||
{
|
||||
actor.Thumb = thumb.Value;
|
||||
}
|
||||
|
||||
action(nfo, actor);
|
||||
}
|
||||
}
|
||||
|
||||
protected static async Task ReadUniqueId(XmlReader reader, T nfo, Action<T, UniqueIdNfo> action)
|
||||
{
|
||||
if (nfo != null)
|
||||
{
|
||||
var uniqueId = new UniqueIdNfo();
|
||||
reader.MoveToAttribute("default");
|
||||
uniqueId.Default = bool.TryParse(reader.Value, out bool def) && def;
|
||||
reader.MoveToAttribute("type");
|
||||
uniqueId.Type = reader.Value;
|
||||
reader.MoveToElement();
|
||||
uniqueId.Guid = await reader.ReadElementContentAsStringAsync();
|
||||
|
||||
action(nfo, uniqueId);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user