aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStepan <ste.martinek+git@gmail.com>2020-11-02 20:03:12 +0100
committerStepan <ste.martinek+git@gmail.com>2020-11-02 20:03:12 +0100
commit1e7177568887d0f808660454e5eb7ca7ebcd6998 (patch)
tree0359804e2c4d691a7cf476c7364cefded0ab2947
parent50a2ef9d8aaf92e8b69ced5ea2fcc8fa185fe675 (diff)
Add Name and Year parsing for audiobooks
-rw-r--r--Emby.Naming/AudioBook/AudioBookInfo.cs4
-rw-r--r--Emby.Naming/AudioBook/AudioBookListResolver.cs6
-rw-r--r--Emby.Naming/AudioBook/AudioBookNameParser.cs59
-rw-r--r--Emby.Naming/AudioBook/AudioBookNameParserResult.cs12
-rw-r--r--Emby.Naming/Common/NamingOptions.cs9
-rw-r--r--Emby.Naming/Video/StackResolver.cs20
-rw-r--r--tests/Jellyfin.Naming.Tests/AudioBook/AudioBookListResolverTests.cs78
-rw-r--r--tests/Jellyfin.Naming.Tests/AudioBook/AudioBookResolverTests.cs1
8 files changed, 163 insertions, 26 deletions
diff --git a/Emby.Naming/AudioBook/AudioBookInfo.cs b/Emby.Naming/AudioBook/AudioBookInfo.cs
index fba11ea72..353a0f4a0 100644
--- a/Emby.Naming/AudioBook/AudioBookInfo.cs
+++ b/Emby.Naming/AudioBook/AudioBookInfo.cs
@@ -11,12 +11,14 @@ namespace Emby.Naming.AudioBook
/// Initializes a new instance of the <see cref="AudioBookInfo" /> class.
/// </summary>
/// <param name="name">Name of audiobook.</param>
- public AudioBookInfo(string name)
+ /// <param name="year">Year of audiobook release.</param>
+ public AudioBookInfo(string name, int? year)
{
Files = new List<AudioBookFileInfo>();
Extras = new List<AudioBookFileInfo>();
AlternateVersions = new List<AudioBookFileInfo>();
Name = name;
+ Year = year;
}
/// <summary>
diff --git a/Emby.Naming/AudioBook/AudioBookListResolver.cs b/Emby.Naming/AudioBook/AudioBookListResolver.cs
index 795065a6c..86ba2eeea 100644
--- a/Emby.Naming/AudioBook/AudioBookListResolver.cs
+++ b/Emby.Naming/AudioBook/AudioBookListResolver.cs
@@ -41,9 +41,9 @@ namespace Emby.Naming.AudioBook
stackFiles.Sort();
- // stack.Name can be empty when we have file without folder, but always have some files
- var name = string.IsNullOrEmpty(stack.Name) ? stack.Files[0] : stack.Name;
- var info = new AudioBookInfo(name) { Files = stackFiles };
+ var result = new AudioBookNameParser(_options).Parse(stack.Name);
+
+ var info = new AudioBookInfo(result.Name, result.Year) { Files = stackFiles };
yield return info;
}
diff --git a/Emby.Naming/AudioBook/AudioBookNameParser.cs b/Emby.Naming/AudioBook/AudioBookNameParser.cs
new file mode 100644
index 000000000..c48db93b3
--- /dev/null
+++ b/Emby.Naming/AudioBook/AudioBookNameParser.cs
@@ -0,0 +1,59 @@
+#nullable enable
+#pragma warning disable CS1591
+
+using System.Globalization;
+using System.IO;
+using System.Text.RegularExpressions;
+using Emby.Naming.Common;
+
+namespace Emby.Naming.AudioBook
+{
+ public class AudioBookNameParser
+ {
+ private readonly NamingOptions _options;
+
+ public AudioBookNameParser(NamingOptions options)
+ {
+ _options = options;
+ }
+
+ public AudioBookNameParserResult Parse(string name)
+ {
+ AudioBookNameParserResult result = default;
+ foreach (var expression in _options.AudioBookNamesExpressions)
+ {
+ var match = new Regex(expression, RegexOptions.IgnoreCase).Match(name);
+ if (match.Success)
+ {
+ if (result.Name == null)
+ {
+ var value = match.Groups["name"];
+ if (value.Success)
+ {
+ result.Name = value.Value;
+ }
+ }
+
+ if (!result.Year.HasValue)
+ {
+ var value = match.Groups["year"];
+ if (value.Success)
+ {
+ if (int.TryParse(value.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var intValue))
+ {
+ result.Year = intValue;
+ }
+ }
+ }
+ }
+ }
+
+ if (string.IsNullOrEmpty(result.Name))
+ {
+ result.Name = name;
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/Emby.Naming/AudioBook/AudioBookNameParserResult.cs b/Emby.Naming/AudioBook/AudioBookNameParserResult.cs
new file mode 100644
index 000000000..b28e259dd
--- /dev/null
+++ b/Emby.Naming/AudioBook/AudioBookNameParserResult.cs
@@ -0,0 +1,12 @@
+#nullable enable
+#pragma warning disable CS1591
+
+namespace Emby.Naming.AudioBook
+{
+ public struct AudioBookNameParserResult
+ {
+ public string Name { get; set; }
+
+ public int? Year { get; set; }
+ }
+}
diff --git a/Emby.Naming/Common/NamingOptions.cs b/Emby.Naming/Common/NamingOptions.cs
index 537de63d5..5bf232451 100644
--- a/Emby.Naming/Common/NamingOptions.cs
+++ b/Emby.Naming/Common/NamingOptions.cs
@@ -575,6 +575,13 @@ namespace Emby.Naming.Common
@"dis(?:c|k)[\s_-]?(?<chapter>[0-9]+)"
};
+ AudioBookNamesExpressions = new[]
+ {
+ // Detect year usually in brackets after name Batman (2020)
+ @"^(?<name>.+?)\s*\(\s*(?<year>\d{4})\s*\)\s*$",
+ @"^\s*(?<name>.+?)\s*$"
+ };
+
var extensions = VideoFileExtensions.ToList();
extensions.AddRange(new[]
@@ -658,6 +665,8 @@ namespace Emby.Naming.Common
public string[] AudioBookPartsExpressions { get; set; }
+ public string[] AudioBookNamesExpressions { get; set; }
+
public StubTypeRule[] StubTypes { get; set; }
public char[] VideoFlagDelimiters { get; set; }
diff --git a/Emby.Naming/Video/StackResolver.cs b/Emby.Naming/Video/StackResolver.cs
index ce3152739..e11b4063c 100644
--- a/Emby.Naming/Video/StackResolver.cs
+++ b/Emby.Naming/Video/StackResolver.cs
@@ -36,13 +36,25 @@ namespace Emby.Naming.Video
foreach (var directory in groupedDirectoryFiles)
{
- var stack = new FileStack { Name = Path.GetFileName(directory.Key), IsDirectoryStack = false };
- foreach (var file in directory)
+ if (string.IsNullOrEmpty(directory.Key))
{
- stack.Files.Add(file.Path);
+ foreach (var file in directory)
+ {
+ var stack = new FileStack { Name = Path.GetFileNameWithoutExtension(file.Path), IsDirectoryStack = false };
+ stack.Files.Add(file.Path);
+ yield return stack;
+ }
}
+ else
+ {
+ var stack = new FileStack { Name = Path.GetFileName(directory.Key), IsDirectoryStack = false };
+ foreach (var file in directory)
+ {
+ stack.Files.Add(file.Path);
+ }
- yield return stack;
+ yield return stack;
+ }
}
}
diff --git a/tests/Jellyfin.Naming.Tests/AudioBook/AudioBookListResolverTests.cs b/tests/Jellyfin.Naming.Tests/AudioBook/AudioBookListResolverTests.cs
index c4b061b4e..91492d46c 100644
--- a/tests/Jellyfin.Naming.Tests/AudioBook/AudioBookListResolverTests.cs
+++ b/tests/Jellyfin.Naming.Tests/AudioBook/AudioBookListResolverTests.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Linq;
using Emby.Naming.AudioBook;
using Emby.Naming.Common;
@@ -72,33 +73,69 @@ namespace Jellyfin.Naming.Tests.AudioBook
}
[Fact]
- public void TestYearExtraction()
+ public void TestNameYearExtraction()
{
- var files = new[]
+ var data = new[]
{
- "Harry Potter and the Deathly Hallows (2007)/Chapter 1.ogg",
- "Harry Potter and the Deathly Hallows (2007)/Chapter 2.mp3",
-
- "Batman (2020).ogg",
-
- "Batman(2021).mp3",
-
- "Batman.mp3"
+ new NameYearPath
+ {
+ Name = "Harry Potter and the Deathly Hallows",
+ Path = "Harry Potter and the Deathly Hallows (2007)/Chapter 1.ogg",
+ Year = 2007
+ },
+ new NameYearPath
+ {
+ Name = "Batman",
+ Path = "Batman (2020).ogg",
+ Year = 2020
+ },
+ new NameYearPath
+ {
+ Name = "Batman",
+ Path = "Batman( 2021 ).mp3",
+ Year = 2021
+ },
+ new NameYearPath
+ {
+ Name = "Batman(*2021*)",
+ Path = "Batman(*2021*).mp3",
+ Year = null
+ },
+ new NameYearPath
+ {
+ Name = "Batman",
+ Path = "Batman.mp3",
+ Year = null
+ },
+ new NameYearPath
+ {
+ Name = "+ Batman .",
+ Path = " + Batman . .mp3",
+ Year = null
+ },
+ new NameYearPath
+ {
+ Name = " ",
+ Path = " .mp3",
+ Year = null
+ }
};
var resolver = GetResolver();
- var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ var result = resolver.Resolve(data.Select(i => new FileSystemMetadata
{
IsDirectory = false,
- FullName = i
+ FullName = i.Path
})).ToList();
- Assert.Equal(3, result[0].Files.Count);
- Assert.Equal(2007, result[0].Year);
- Assert.Equal(2020, result[1].Year);
- Assert.Equal(2021, result[2].Year);
- Assert.Null(result[2].Year);
+ Assert.Equal(data.Length, result.Count);
+
+ for (int i = 0; i < data.Length; i++)
+ {
+ Assert.Equal(data[i].Name, result[i].Name);
+ Assert.Equal(data[i].Year, result[i].Year);
+ }
}
[Fact]
@@ -180,5 +217,12 @@ namespace Jellyfin.Naming.Tests.AudioBook
{
return new AudioBookListResolver(_namingOptions);
}
+
+ internal struct NameYearPath
+ {
+ public string Name;
+ public string Path;
+ public int? Year;
+ }
}
}
diff --git a/tests/Jellyfin.Naming.Tests/AudioBook/AudioBookResolverTests.cs b/tests/Jellyfin.Naming.Tests/AudioBook/AudioBookResolverTests.cs
index 5e9d12970..b3257ace3 100644
--- a/tests/Jellyfin.Naming.Tests/AudioBook/AudioBookResolverTests.cs
+++ b/tests/Jellyfin.Naming.Tests/AudioBook/AudioBookResolverTests.cs
@@ -35,7 +35,6 @@ namespace Jellyfin.Naming.Tests.AudioBook
};
}
-
[Theory]
[MemberData(nameof(GetResolveFileTestData))]
public void Resolve_ValidFileName_Success(AudioBookFileInfo expectedResult)