From e2ffa705292b5dba22dca4a545990c3202d953d6 Mon Sep 17 00:00:00 2001
From: Jason Dove <1695733+jasongdove@users.noreply.github.com>
Date: Tue, 1 Jul 2025 02:59:18 +0000
Subject: [PATCH] support episodedetails and musicvideo as top-level other
video nfo tags (#2098)
---
CHANGELOG.md | 2 +
.../Metadata/Nfo/OtherVideoNfoReaderTests.cs | 39 +++++++++++++------
.../Core/Metadata/Nfo/OtherVideoNfoReader.cs | 8 +++-
3 files changed, 36 insertions(+), 13 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f46cd2f25..2129d1b8c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -58,6 +58,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Changed
- Allow `Other Video` libraries and `Image` libraries to use the same folders
- Try to mitigate inotify limit error by disabling automatic reloading of `appsettings.json` config files
+- Support `movie`, `musicvideo` and `episodedetails` top-level tags in other video NFO files
+ - Note that no change has been made to the metadata tags that are actually parsed, but this should help with various types of content
### Fixed
- Fix QSV acceleration in docker with older Intel devices
diff --git a/ErsatzTV.Scanner.Tests/Core/Metadata/Nfo/OtherVideoNfoReaderTests.cs b/ErsatzTV.Scanner.Tests/Core/Metadata/Nfo/OtherVideoNfoReaderTests.cs
index 815b4bdc8..a1086b855 100644
--- a/ErsatzTV.Scanner.Tests/Core/Metadata/Nfo/OtherVideoNfoReaderTests.cs
+++ b/ErsatzTV.Scanner.Tests/Core/Metadata/Nfo/OtherVideoNfoReaderTests.cs
@@ -33,9 +33,12 @@ public class OtherVideoNfoReaderTests
}
[Test]
- public async Task MetadataNfo_Should_Return_Nfo()
+ [TestCase("movie")]
+ [TestCase("episodedetails")]
+ [TestCase("musicvideo")]
+ public async Task MetadataNfo_Should_Return_Nfo(string topLevel)
{
- await using var stream = new MemoryStream(Encoding.UTF8.GetBytes(@""));
+ await using var stream = new MemoryStream(Encoding.UTF8.GetBytes(@$"<{topLevel}>{topLevel}>"));
Either result = await _otherVideoNfoReader.Read(stream);
@@ -43,11 +46,14 @@ public class OtherVideoNfoReaderTests
}
[Test]
- public async Task CombinationNfo_Should_Return_Nfo()
+ [TestCase("movie")]
+ [TestCase("episodedetails")]
+ [TestCase("musicvideo")]
+ public async Task CombinationNfo_Should_Return_Nfo(string topLevel)
{
await using var stream = new MemoryStream(
Encoding.UTF8.GetBytes(
- @"
+ @$"<{topLevel}>{topLevel}>
https://www.themoviedb.org/movie/11-star-wars"));
Either result = await _otherVideoNfoReader.Read(stream);
@@ -56,12 +62,15 @@ https://www.themoviedb.org/movie/11-star-wars"));
}
[Test]
- public async Task FullSample_Should_Return_Nfo()
+ [TestCase("movie")]
+ [TestCase("episodedetails")]
+ [TestCase("musicvideo")]
+ public async Task FullSample_Should_Return_Nfo(string topLevel)
{
await using var stream = new MemoryStream(
Encoding.UTF8.GetBytes(
- @"
-
+ @$"
+<{topLevel}>
Zack Snyder's Justice League
Zack Snyder's Justice League
Justice League 2
@@ -166,7 +175,7 @@ https://www.themoviedb.org/movie/11-star-wars"));
0.000000
2021-03-26 11:35:50
-"));
+{topLevel}>"));
Either result = await _otherVideoNfoReader.Read(stream);
@@ -223,9 +232,12 @@ https://www.themoviedb.org/movie/11-star-wars"));
}
[Test]
- public async Task MetadataNfo_With_Tag_Should_Return_Nfo()
+ [TestCase("movie")]
+ [TestCase("episodedetails")]
+ [TestCase("musicvideo")]
+ public async Task MetadataNfo_With_Tag_Should_Return_Nfo(string topLevel)
{
- await using var stream = new MemoryStream(Encoding.UTF8.GetBytes(@"Test Tag"));
+ await using var stream = new MemoryStream(Encoding.UTF8.GetBytes(@$"<{topLevel}>Test Tag{topLevel}>"));
Either result = await _otherVideoNfoReader.Read(stream);
@@ -237,10 +249,13 @@ https://www.themoviedb.org/movie/11-star-wars"));
}
[Test]
- public async Task MetadataNfo_With_Outline_Should_Return_Nfo()
+ [TestCase("movie")]
+ [TestCase("episodedetails")]
+ [TestCase("musicvideo")]
+ public async Task MetadataNfo_With_Outline_Should_Return_Nfo(string topLevel)
{
await using var stream =
- new MemoryStream(Encoding.UTF8.GetBytes(@"Test Outline"));
+ new MemoryStream(Encoding.UTF8.GetBytes(@$"<{topLevel}>Test Outline{topLevel}>"));
Either result = await _otherVideoNfoReader.Read(stream);
diff --git a/ErsatzTV.Scanner/Core/Metadata/Nfo/OtherVideoNfoReader.cs b/ErsatzTV.Scanner/Core/Metadata/Nfo/OtherVideoNfoReader.cs
index 7c0adb2cc..9c24164f8 100644
--- a/ErsatzTV.Scanner/Core/Metadata/Nfo/OtherVideoNfoReader.cs
+++ b/ErsatzTV.Scanner/Core/Metadata/Nfo/OtherVideoNfoReader.cs
@@ -49,7 +49,13 @@ public class OtherVideoNfoReader : NfoReader, IOtherVideoNfoReade
case XmlNodeType.Element:
switch (reader.Name.ToLowerInvariant())
{
+ case "episodedetails":
case "movie":
+ case "musicvideo":
+ if (nfo is not null)
+ {
+ throw new InvalidOperationException("Cannot have multiple opening tags");
+ }
nfo = new OtherVideoNfo();
break;
case "title":
@@ -107,7 +113,7 @@ public class OtherVideoNfoReader : NfoReader, IOtherVideoNfoReade
break;
case XmlNodeType.EndElement:
- if (reader.Name == "movie")
+ if (reader.Name is "episodedetails" or "movie" or "musicvideo")
{
done = true;
}