mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-05-31 04:05:50 -04:00
Fix: parsing of xbmc style multi episode nfo files (#12268)
This commit is contained in:
parent
0a1a109b2e
commit
d4eeafe53f
@ -185,6 +185,7 @@
|
|||||||
- [Vedant](https://github.com/viktory36/)
|
- [Vedant](https://github.com/viktory36/)
|
||||||
- [NotSaifA](https://github.com/NotSaifA)
|
- [NotSaifA](https://github.com/NotSaifA)
|
||||||
- [HonestlyWhoKnows](https://github.com/honestlywhoknows)
|
- [HonestlyWhoKnows](https://github.com/honestlywhoknows)
|
||||||
|
- [TheMelmacian](https://github.com/TheMelmacian)
|
||||||
- [ItsAllAboutTheCode](https://github.com/ItsAllAboutTheCode)
|
- [ItsAllAboutTheCode](https://github.com/ItsAllAboutTheCode)
|
||||||
|
|
||||||
# Emby Contributors
|
# Emby Contributors
|
||||||
|
@ -59,80 +59,50 @@ namespace MediaBrowser.XbmcMetadata.Parsers
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Extract episode details from the first episodedetails block
|
// Extract episode details from the first episodedetails block
|
||||||
using (var stringReader = new StringReader(xml))
|
ReadEpisodeDetailsFromXml(item, xml, settings, cancellationToken);
|
||||||
using (var reader = XmlReader.Create(stringReader, settings))
|
|
||||||
{
|
|
||||||
reader.MoveToContent();
|
|
||||||
reader.Read();
|
|
||||||
|
|
||||||
// Loop through each element
|
|
||||||
while (!reader.EOF && reader.ReadState == ReadState.Interactive)
|
|
||||||
{
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
|
||||||
|
|
||||||
if (reader.NodeType == XmlNodeType.Element)
|
|
||||||
{
|
|
||||||
FetchDataFromXmlNode(reader, item);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
reader.Read();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract the last episode number from nfo
|
// Extract the last episode number from nfo
|
||||||
// Retrieves all title and plot tags from the rest of the nfo and concatenates them with the first episode
|
// Retrieves all additional episodedetails blocks from the rest of the nfo and concatenates the name, originalTitle and overview tags with the first episode
|
||||||
// This is needed because XBMC metadata uses multiple episodedetails blocks instead of episodenumberend tag
|
// This is needed because XBMC metadata uses multiple episodedetails blocks instead of episodenumberend tag
|
||||||
var name = new StringBuilder(item.Item.Name);
|
var name = new StringBuilder(item.Item.Name);
|
||||||
|
var originalTitle = new StringBuilder(item.Item.OriginalTitle);
|
||||||
var overview = new StringBuilder(item.Item.Overview);
|
var overview = new StringBuilder(item.Item.Overview);
|
||||||
while ((index = xmlFile.IndexOf(srch, StringComparison.OrdinalIgnoreCase)) != -1)
|
while ((index = xmlFile.IndexOf(srch, StringComparison.OrdinalIgnoreCase)) != -1)
|
||||||
{
|
{
|
||||||
xml = xmlFile.Substring(0, index + srch.Length);
|
xml = xmlFile.Substring(0, index + srch.Length);
|
||||||
xmlFile = xmlFile.Substring(index + srch.Length);
|
xmlFile = xmlFile.Substring(index + srch.Length);
|
||||||
|
|
||||||
using (var stringReader = new StringReader(xml))
|
var additionalEpisode = new MetadataResult<Episode>()
|
||||||
using (var reader = XmlReader.Create(stringReader, settings))
|
|
||||||
{
|
{
|
||||||
reader.MoveToContent();
|
Item = new Episode()
|
||||||
|
};
|
||||||
|
|
||||||
while (!reader.EOF && reader.ReadState == ReadState.Interactive)
|
// Extract episode details from additional episodedetails block
|
||||||
{
|
ReadEpisodeDetailsFromXml(additionalEpisode, xml, settings, cancellationToken);
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
|
||||||
|
|
||||||
if (reader.NodeType == XmlNodeType.Element)
|
if (!string.IsNullOrEmpty(additionalEpisode.Item.Name))
|
||||||
{
|
{
|
||||||
switch (reader.Name)
|
name.Append(" / ").Append(additionalEpisode.Item.Name);
|
||||||
{
|
}
|
||||||
case "name":
|
|
||||||
case "title":
|
|
||||||
case "localtitle":
|
|
||||||
name.Append(" / ").Append(reader.ReadElementContentAsString());
|
|
||||||
break;
|
|
||||||
case "episode":
|
|
||||||
{
|
|
||||||
if (int.TryParse(reader.ReadElementContentAsString(), out var num))
|
|
||||||
{
|
|
||||||
item.Item.IndexNumberEnd = Math.Max(num, item.Item.IndexNumberEnd ?? num);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
if (!string.IsNullOrEmpty(additionalEpisode.Item.Overview))
|
||||||
}
|
{
|
||||||
|
overview.Append(" / ").Append(additionalEpisode.Item.Overview);
|
||||||
|
}
|
||||||
|
|
||||||
case "biography":
|
if (!string.IsNullOrEmpty(additionalEpisode.Item.OriginalTitle))
|
||||||
case "plot":
|
{
|
||||||
case "review":
|
originalTitle.Append(" / ").Append(additionalEpisode.Item.OriginalTitle);
|
||||||
overview.Append(" / ").Append(reader.ReadElementContentAsString());
|
}
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
reader.Read();
|
if (additionalEpisode.Item.IndexNumber != null)
|
||||||
}
|
{
|
||||||
|
item.Item.IndexNumberEnd = Math.Max((int)additionalEpisode.Item.IndexNumber, item.Item.IndexNumberEnd ?? (int)additionalEpisode.Item.IndexNumber);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
item.Item.Name = name.ToString();
|
item.Item.Name = name.ToString();
|
||||||
|
item.Item.OriginalTitle = originalTitle.ToString();
|
||||||
item.Item.Overview = overview.ToString();
|
item.Item.Overview = overview.ToString();
|
||||||
}
|
}
|
||||||
catch (XmlException)
|
catch (XmlException)
|
||||||
@ -200,5 +170,33 @@ namespace MediaBrowser.XbmcMetadata.Parsers
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads the episode details from the given xml and saves the result in the provided result item.
|
||||||
|
/// </summary>
|
||||||
|
private void ReadEpisodeDetailsFromXml(MetadataResult<Episode> item, string xml, XmlReaderSettings settings, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
using (var stringReader = new StringReader(xml))
|
||||||
|
using (var reader = XmlReader.Create(stringReader, settings))
|
||||||
|
{
|
||||||
|
reader.MoveToContent();
|
||||||
|
reader.Read();
|
||||||
|
|
||||||
|
// Loop through each element
|
||||||
|
while (!reader.EOF && reader.ReadState == ReadState.Interactive)
|
||||||
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
if (reader.NodeType == XmlNodeType.Element)
|
||||||
|
{
|
||||||
|
FetchDataFromXmlNode(reader, item);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
reader.Read();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,6 +123,30 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers
|
|||||||
Assert.Equal(2004, item.ProductionYear);
|
Assert.Equal(2004, item.ProductionYear);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Fetch_Valid_MultiEpisode_With_Missing_Tags_Success()
|
||||||
|
{
|
||||||
|
var result = new MetadataResult<Episode>()
|
||||||
|
{
|
||||||
|
Item = new Episode()
|
||||||
|
};
|
||||||
|
|
||||||
|
_parser.Fetch(result, "Test Data/Stargate Atlantis S01E01-E04.nfo", CancellationToken.None);
|
||||||
|
|
||||||
|
var item = result.Item;
|
||||||
|
// <title> provided for episode 1, 3 and 4
|
||||||
|
Assert.Equal("Rising / Hide and Seek / Thirty-Eight Minutes", item.Name);
|
||||||
|
// <originaltitle> provided for all episodes
|
||||||
|
Assert.Equal("Rising (1) / Rising (2) / Hide and Seek / Thirty-Eight Minutes", item.OriginalTitle);
|
||||||
|
Assert.Equal(1, item.IndexNumber);
|
||||||
|
Assert.Equal(4, item.IndexNumberEnd);
|
||||||
|
Assert.Equal(1, item.ParentIndexNumber);
|
||||||
|
// <plot> only provided for episode 1
|
||||||
|
Assert.Equal("A new Stargate team embarks on a dangerous mission to a distant galaxy, where they discover a mythical lost city -- and a deadly new enemy.", item.Overview);
|
||||||
|
Assert.Equal(new DateTime(2004, 7, 16), item.PremiereDate);
|
||||||
|
Assert.Equal(2004, item.ProductionYear);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Parse_GivenFileWithThumbWithoutAspect_Success()
|
public void Parse_GivenFileWithThumbWithoutAspect_Success()
|
||||||
{
|
{
|
||||||
|
@ -7,6 +7,18 @@
|
|||||||
<thumb>https://artworks.thetvdb.com/banners/episodes/70851/25333.jpg</thumb>
|
<thumb>https://artworks.thetvdb.com/banners/episodes/70851/25333.jpg</thumb>
|
||||||
<watched>false</watched>
|
<watched>false</watched>
|
||||||
<rating>8.0</rating>
|
<rating>8.0</rating>
|
||||||
|
<actor>
|
||||||
|
<name>Joe Flanigan</name>
|
||||||
|
<role>John Sheppard</role>
|
||||||
|
<order>0</order>
|
||||||
|
<thumb>https://image.tmdb.org/t/p/w300_and_h450_bestv2/5AA1ORKIsnMakT6fCVy3JKlzMs6.jpg</thumb>
|
||||||
|
</actor>
|
||||||
|
<actor>
|
||||||
|
<name>David Hewlett</name>
|
||||||
|
<role>Rodney McKay</role>
|
||||||
|
<order>1</order>
|
||||||
|
<thumb>https://image.tmdb.org/t/p/w300_and_h450_bestv2/hUcYyssAPCqnZ4GjolhOWXHTWSa.jpg</thumb>
|
||||||
|
</actor>
|
||||||
</episodedetails>
|
</episodedetails>
|
||||||
<episodedetails>
|
<episodedetails>
|
||||||
<title>Rising (2)</title>
|
<title>Rising (2)</title>
|
||||||
@ -17,4 +29,16 @@
|
|||||||
<thumb>https://artworks.thetvdb.com/banners/episodes/70851/25334.jpg</thumb>
|
<thumb>https://artworks.thetvdb.com/banners/episodes/70851/25334.jpg</thumb>
|
||||||
<watched>false</watched>
|
<watched>false</watched>
|
||||||
<rating>7.9</rating>
|
<rating>7.9</rating>
|
||||||
|
<actor>
|
||||||
|
<name>Joe Flanigan</name>
|
||||||
|
<role>John Sheppard</role>
|
||||||
|
<order>0</order>
|
||||||
|
<thumb>https://image.tmdb.org/t/p/w300_and_h450_bestv2/5AA1ORKIsnMakT6fCVy3JKlzMs6.jpg</thumb>
|
||||||
|
</actor>
|
||||||
|
<actor>
|
||||||
|
<name>David Hewlett</name>
|
||||||
|
<role>Rodney McKay</role>
|
||||||
|
<order>1</order>
|
||||||
|
<thumb>https://image.tmdb.org/t/p/w300_and_h450_bestv2/hUcYyssAPCqnZ4GjolhOWXHTWSa.jpg</thumb>
|
||||||
|
</actor>
|
||||||
</episodedetails>
|
</episodedetails>
|
||||||
|
@ -0,0 +1,89 @@
|
|||||||
|
<episodedetails>
|
||||||
|
<title>Rising</title>
|
||||||
|
<originaltitle>Rising (1)</originaltitle>
|
||||||
|
<season>1</season>
|
||||||
|
<episode>1</episode>
|
||||||
|
<aired>2004-07-16</aired>
|
||||||
|
<plot>A new Stargate team embarks on a dangerous mission to a distant galaxy, where they discover a mythical lost city -- and a deadly new enemy.</plot>
|
||||||
|
<thumb>https://artworks.thetvdb.com/banners/episodes/70851/25333.jpg</thumb>
|
||||||
|
<watched>false</watched>
|
||||||
|
<rating>8.0</rating>
|
||||||
|
<actor>
|
||||||
|
<name>Joe Flanigan</name>
|
||||||
|
<role>John Sheppard</role>
|
||||||
|
<order>0</order>
|
||||||
|
<thumb>https://image.tmdb.org/t/p/w300_and_h450_bestv2/5AA1ORKIsnMakT6fCVy3JKlzMs6.jpg</thumb>
|
||||||
|
</actor>
|
||||||
|
<actor>
|
||||||
|
<name>David Hewlett</name>
|
||||||
|
<role>Rodney McKay</role>
|
||||||
|
<order>1</order>
|
||||||
|
<thumb>https://image.tmdb.org/t/p/w300_and_h450_bestv2/hUcYyssAPCqnZ4GjolhOWXHTWSa.jpg</thumb>
|
||||||
|
</actor>
|
||||||
|
</episodedetails>
|
||||||
|
<episodedetails>
|
||||||
|
<originaltitle>Rising (2)</originaltitle>
|
||||||
|
<season>1</season>
|
||||||
|
<episode>2</episode>
|
||||||
|
<aired>2004-07-16</aired>
|
||||||
|
<thumb>https://artworks.thetvdb.com/banners/episodes/70851/25334.jpg</thumb>
|
||||||
|
<watched>false</watched>
|
||||||
|
<rating>7.9</rating>
|
||||||
|
<actor>
|
||||||
|
<name>Joe Flanigan</name>
|
||||||
|
<role>John Sheppard</role>
|
||||||
|
<order>0</order>
|
||||||
|
<thumb>https://image.tmdb.org/t/p/w300_and_h450_bestv2/5AA1ORKIsnMakT6fCVy3JKlzMs6.jpg</thumb>
|
||||||
|
</actor>
|
||||||
|
<actor>
|
||||||
|
<name>David Hewlett</name>
|
||||||
|
<role>Rodney McKay</role>
|
||||||
|
<order>1</order>
|
||||||
|
<thumb>https://image.tmdb.org/t/p/w300_and_h450_bestv2/hUcYyssAPCqnZ4GjolhOWXHTWSa.jpg</thumb>
|
||||||
|
</actor>
|
||||||
|
</episodedetails>
|
||||||
|
<episodedetails>
|
||||||
|
<title>Hide and Seek</title>
|
||||||
|
<originaltitle>Hide and Seek</originaltitle>
|
||||||
|
<season>1</season>
|
||||||
|
<episode>3</episode>
|
||||||
|
<aired>2004-07-23</aired>
|
||||||
|
<thumb>https://artworks.thetvdb.com/banners/episodes/70851/25335.jpg</thumb>
|
||||||
|
<watched>false</watched>
|
||||||
|
<rating>7.5</rating>
|
||||||
|
<actor>
|
||||||
|
<name>Joe Flanigan</name>
|
||||||
|
<role>John Sheppard</role>
|
||||||
|
<order>0</order>
|
||||||
|
<thumb>https://image.tmdb.org/t/p/w300_and_h450_bestv2/5AA1ORKIsnMakT6fCVy3JKlzMs6.jpg</thumb>
|
||||||
|
</actor>
|
||||||
|
<actor>
|
||||||
|
<name>David Hewlett</name>
|
||||||
|
<role>Rodney McKay</role>
|
||||||
|
<order>1</order>
|
||||||
|
<thumb>https://image.tmdb.org/t/p/w300_and_h450_bestv2/hUcYyssAPCqnZ4GjolhOWXHTWSa.jpg</thumb>
|
||||||
|
</actor>
|
||||||
|
</episodedetails>
|
||||||
|
<episodedetails>
|
||||||
|
<title>Thirty-Eight Minutes</title>
|
||||||
|
<originaltitle>Thirty-Eight Minutes</originaltitle>
|
||||||
|
<season>1</season>
|
||||||
|
<episode>4</episode>
|
||||||
|
<aired>2004-07-23</aired>
|
||||||
|
<thumb>https://artworks.thetvdb.com/banners/episodes/70851/25336.jpg</thumb>
|
||||||
|
<watched>false</watched>
|
||||||
|
<rating>7.5</rating>
|
||||||
|
<actor>
|
||||||
|
<name>Joe Flanigan</name>
|
||||||
|
<role>John Sheppard</role>
|
||||||
|
<order>0</order>
|
||||||
|
<thumb>https://image.tmdb.org/t/p/w300_and_h450_bestv2/5AA1ORKIsnMakT6fCVy3JKlzMs6.jpg</thumb>
|
||||||
|
</actor>
|
||||||
|
<actor>
|
||||||
|
<name>David Hewlett</name>
|
||||||
|
<role>Rodney McKay</role>
|
||||||
|
<order>1</order>
|
||||||
|
<thumb>https://image.tmdb.org/t/p/w300_and_h450_bestv2/hUcYyssAPCqnZ4GjolhOWXHTWSa.jpg</thumb>
|
||||||
|
</actor>
|
||||||
|
</episodedetails>
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user