Jelajahi Sumber

Merge pull request #1929 from Narfinger/parser-fix4

[Draft][Help wanted] Fix parsing of certain names and adds a default season if no season was found

(cherry picked from commit 61b9b4046af1890d4a2a68495f212def3bca6915)
Signed-off-by: Joshua Boniface <joshua@boniface.me>
dkanada 5 tahun lalu
induk
melakukan
46ab046c34

+ 12 - 8
Emby.Naming/Common/NamingOptions.cs

@@ -328,28 +328,32 @@ namespace Emby.Naming.Common
 
                 // *** End Kodi Standard Naming
 
-                new EpisodeExpression(@".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})[^\\\/]*$")
+                // [bar] Foo - 1 [baz]
+                new EpisodeExpression(@".*?(\[.*?\])+.*?(?<seriesname>(\w+\s)+?)[-\s_]+(?<epnumber>\d+).*$"){
+                    IsNamed=false,
+                },
+                new EpisodeExpression(@".*(\\|\/)[sS]?(?<seasonnumber>\d+)[xX](?<epnumber>\d+)[^\\\/]*$")
                 {
                     IsNamed = true
                 },
 
-                new EpisodeExpression(@".*(\\|\/)[sS](?<seasonnumber>\d{1,4})[x,X]?[eE](?<epnumber>\d{1,3})[^\\\/]*$")
+                new EpisodeExpression(@".*(\\|\/)[sS](?<seasonnumber>\d+)[x,X]?[eE](?<epnumber>\d+)[^\\\/]*$")
                 {
                     IsNamed = true
                 },
 
-                new EpisodeExpression(@".*(\\|\/)(?<seriesname>((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3}))[^\\\/]*$")
+                new EpisodeExpression(@".*(\\|\/)(?<seriesname>((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d+))[^\\\/]*$")
                 {
                     IsNamed = true
                 },
 
-                new EpisodeExpression(@".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>\d{1,4})[xX\.]?[eE](?<epnumber>\d{1,3})[^\\\/]*$")
+                new EpisodeExpression(@".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>\d{1,4})[xX\.]?[eE](?<epnumber>\d+)[^\\\/]*$")
                 {
                     IsNamed = true
                 },
 
                 // "01.avi"
-                new EpisodeExpression(@".*[\\\/](?<epnumber>\d{1,3})(-(?<endingepnumber>\d{2,3}))*\.\w+$")
+                new EpisodeExpression(@".*[\\\/](?<epnumber>\d+)(-(?<endingepnumber>\d+))*\.\w+$")
                 {
                     IsOptimistic = true,
                     IsNamed = true
@@ -654,9 +658,9 @@ namespace Emby.Naming.Common
                 @".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>\d{1,4})[xX\.]?[eE](?<epnumber>\d{1,3})((-| - )?[xXeE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
                 @".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>\d{1,4})[xX\.]?[eE](?<epnumber>\d{1,3})(-[xX]?[eE]?(?<endingepnumber>\d{1,3}))+[^\\\/]*$"
             }.Select(i => new EpisodeExpression(i)
-                {
-                    IsNamed = true
-                }).ToArray();
+            {
+                IsNamed = true
+            }).ToArray();
 
             VideoFileExtensions = extensions
                 .Distinct(StringComparer.OrdinalIgnoreCase)

+ 10 - 1
Emby.Server.Implementations/Library/LibraryManager.cs

@@ -1899,7 +1899,7 @@ namespace Emby.Server.Implementations.Library
         /// <param name="cancellationToken">The cancellation token.</param>
         public void UpdateItem(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
         {
-            UpdateItems(new [] { item }, parent, updateReason, cancellationToken);
+            UpdateItems(new[] { item }, parent, updateReason, cancellationToken);
         }
 
         /// <summary>
@@ -2487,6 +2487,15 @@ namespace Emby.Server.Implementations.Library
                 {
                     episode.ParentIndexNumber = season.IndexNumber;
                 }
+                else
+                {
+                    /*
+                    Anime series don't generally have a season in their file name, however,
+                    tvdb needs a season to correctly get the metadata.
+                    Hence, a null season needs to be filled with something. */
+                    //FIXME perhaps this would be better for tvdb parser to ask for season 1 if no season is specified
+                    episode.ParentIndexNumber = 1;
+                }
 
                 if (episode.ParentIndexNumber.HasValue)
                 {

+ 7 - 0
MediaBrowser.sln

@@ -59,6 +59,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Common.Tests", "te
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.MediaEncoding.Tests", "tests\Jellyfin.MediaEncoding.Tests\Jellyfin.MediaEncoding.Tests.csproj", "{28464062-0939-4AA7-9F7B-24DDDA61A7C0}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Naming.Tests", "tests\Jellyfin.Naming.Tests\Jellyfin.Naming.Tests.csproj", "{3998657B-1CCC-49DD-A19F-275DC8495F57}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -165,6 +167,10 @@ Global
 		{28464062-0939-4AA7-9F7B-24DDDA61A7C0}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{28464062-0939-4AA7-9F7B-24DDDA61A7C0}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{28464062-0939-4AA7-9F7B-24DDDA61A7C0}.Release|Any CPU.Build.0 = Release|Any CPU
+		{3998657B-1CCC-49DD-A19F-275DC8495F57}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{3998657B-1CCC-49DD-A19F-275DC8495F57}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{3998657B-1CCC-49DD-A19F-275DC8495F57}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{3998657B-1CCC-49DD-A19F-275DC8495F57}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -193,5 +199,6 @@ Global
 	GlobalSection(NestedProjects) = preSolution
 		{DF194677-DFD3-42AF-9F75-D44D5A416478} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6}
 		{28464062-0939-4AA7-9F7B-24DDDA61A7C0} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6}
+		{3998657B-1CCC-49DD-A19F-275DC8495F57} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6}
 	EndGlobalSection
 EndGlobal

+ 54 - 0
tests/Jellyfin.Naming.Tests/EpisodePathParserTest.cs

@@ -0,0 +1,54 @@
+namespace Emby.Naming.TV
+{
+    using Emby.Naming.Common;
+    using Xunit;
+
+    public class EpisodePathParserTest
+    {
+        [Theory]
+        [InlineData("/media/Foo/Foo-S01E01", "Foo", 1, 1)]
+        [InlineData("/media/Foo - S04E011", "Foo", 4, 11)]
+        [InlineData("/media/Foo/Foo s01x01", "Foo", 1, 1)]
+        [InlineData("/media/Foo (2019)/Season 4/Foo (2019).S04E03", "Foo (2019)", 4, 3)]
+        public void ParseEpisodesCorrectly(string path, string name, int season, int episode)
+        {
+            NamingOptions o = new NamingOptions();
+            EpisodePathParser p = new EpisodePathParser(o);
+            var res = p.Parse(path, false);
+
+            Assert.True(res.Success);
+            Assert.Equal(name, res.SeriesName);
+            Assert.Equal(season, res.SeasonNumber);
+            Assert.Equal(episode, res.EpisodeNumber);
+
+            //testing other paths delimeter
+            var res2 = p.Parse(path.Replace("/", "\\"), false);
+            Assert.True(res2.Success);
+            Assert.Equal(name, res2.SeriesName);
+            Assert.Equal(season, res2.SeasonNumber);
+            Assert.Equal(episode, res2.EpisodeNumber);
+        }
+
+        [Theory]
+        [InlineData("/media/Foo/Foo 889", "Foo", 889)]
+        [InlineData("/media/Foo/[Bar] Foo Baz - 11 [1080p]", "Foo Baz", 11)]
+        public void ParseEpisodeWithoutSeason(string path, string name, int episode)
+        {
+            NamingOptions o = new NamingOptions();
+            EpisodePathParser p = new EpisodePathParser(o);
+            var res = p.Parse(path, true, null, null, true);
+
+            Assert.True(res.Success);
+            Assert.Equal(name, res.SeriesName);
+            Assert.True(res.SeasonNumber == null);
+            Assert.Equal(episode, res.EpisodeNumber);
+
+            //testing other paths delimeter
+            var res2 = p.Parse(path.Replace("/", "\\"), false, null, null, true);
+            Assert.True(res2.Success);
+            Assert.Equal(name, res2.SeriesName);
+            Assert.True(res2.SeasonNumber == null);
+            Assert.Equal(episode, res2.EpisodeNumber);
+        }
+    }
+}

+ 20 - 0
tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj

@@ -0,0 +1,20 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>netcoreapp3.0</TargetFramework>
+
+    <IsPackable>false</IsPackable>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
+    <PackageReference Include="xunit" Version="2.4.0" />
+    <PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
+    <PackageReference Include="coverlet.collector" Version="1.0.1" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\..\Emby.Naming\Emby.Naming.csproj" />
+  </ItemGroup>
+
+</Project>