mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-06-09 08:34:16 -04:00
Cleaning up and adding tests for the regex identifier
This commit is contained in:
parent
48e81dfd92
commit
e55f60166e
@ -12,32 +12,26 @@ namespace Kyoo.Controllers
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Identify a path and return the parsed metadata.
|
/// Identify a path and return the parsed metadata.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="path">
|
/// <param name="path">The path of the episode file to parse.</param>
|
||||||
/// The path of the episode file to parse.
|
/// <exception cref="IdentificationFailedException">
|
||||||
/// </param>
|
/// The identifier could not work for the given path.
|
||||||
/// <param name="relativePath">
|
/// </exception>
|
||||||
/// The path of the episode file relative to the library root. It starts with a <c>/</c>.
|
|
||||||
/// </param>
|
|
||||||
/// <exception cref="IdentificationFailedException">The identifier could not work for the given path.</exception>
|
|
||||||
/// <returns>
|
/// <returns>
|
||||||
/// A tuple of models representing parsed metadata.
|
/// A tuple of models representing parsed metadata.
|
||||||
/// If no metadata could be parsed for a type, null can be returned.
|
/// If no metadata could be parsed for a type, null can be returned.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
Task<(Collection, Show, Season, Episode)> Identify(string path, string relativePath);
|
Task<(Collection, Show, Season, Episode)> Identify(string path);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Identify an external subtitle or track file from it's path and return the parsed metadata.
|
/// Identify an external subtitle or track file from it's path and return the parsed metadata.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="path">
|
/// <param name="path">The path of the external track file to parse.</param>
|
||||||
/// The path of the external track file to parse.
|
/// <exception cref="IdentificationFailedException">
|
||||||
/// </param>
|
/// The identifier could not work for the given path.
|
||||||
/// <param name="relativePath">
|
/// </exception>
|
||||||
/// The path of the episode file relative to the library root. It starts with a <c>/</c>.
|
|
||||||
/// </param>
|
|
||||||
/// <exception cref="IdentificationFailedException">The identifier could not work for the given path.</exception>
|
|
||||||
/// <returns>
|
/// <returns>
|
||||||
/// The metadata of the track identified.
|
/// The metadata of the track identified.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
Task<Track> IdentifyTrack(string path, string relativePath);
|
Task<Track> IdentifyTrack(string path);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,6 +4,7 @@ using System.Linq;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
|
using Kyoo.Models;
|
||||||
using Kyoo.Models.Attributes;
|
using Kyoo.Models.Attributes;
|
||||||
using Kyoo.Models.Exceptions;
|
using Kyoo.Models.Exceptions;
|
||||||
|
|
||||||
@ -116,6 +117,15 @@ namespace Kyoo.Controllers
|
|||||||
{
|
{
|
||||||
if (typeof(T) == typeof(object))
|
if (typeof(T) == typeof(object))
|
||||||
return (T)Value;
|
return (T)Value;
|
||||||
|
|
||||||
|
if (Value is IResource resource)
|
||||||
|
{
|
||||||
|
if (typeof(T) == typeof(string))
|
||||||
|
return (T)(object)resource.Slug;
|
||||||
|
if (typeof(T) == typeof(int))
|
||||||
|
return (T)(object)resource.ID;
|
||||||
|
}
|
||||||
|
|
||||||
return (T)Convert.ChangeType(Value, typeof(T));
|
return (T)Convert.ChangeType(Value, typeof(T));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ namespace Kyoo.Tests
|
|||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
Repositories.Dispose();
|
Repositories.Dispose();
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ValueTask DisposeAsync()
|
public ValueTask DisposeAsync()
|
||||||
|
135
Kyoo.Tests/Identifier/IdentifierTests.cs
Normal file
135
Kyoo.Tests/Identifier/IdentifierTests.cs
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using Kyoo.Controllers;
|
||||||
|
using Kyoo.Models;
|
||||||
|
using Kyoo.Models.Options;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Moq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Kyoo.Tests.Identifier
|
||||||
|
{
|
||||||
|
public class Identifier
|
||||||
|
{
|
||||||
|
private readonly Mock<ILibraryManager> _manager;
|
||||||
|
private readonly IIdentifier _identifier;
|
||||||
|
|
||||||
|
public Identifier()
|
||||||
|
{
|
||||||
|
Mock<IOptionsMonitor<MediaOptions>> options = new();
|
||||||
|
options.Setup(x => x.CurrentValue).Returns(new MediaOptions
|
||||||
|
{
|
||||||
|
Regex = new []
|
||||||
|
{
|
||||||
|
"^\\/?(?<Collection>.+)?\\/(?<Show>.+?)(?: \\((?<StartYear>\\d+)\\))?\\/\\k<Show>(?: \\(\\d+\\))? S(?<Season>\\d+)E(?<Episode>\\d+)\\..*$",
|
||||||
|
"^\\/?(?<Collection>.+)?\\/(?<Show>.+?)(?: \\((?<StartYear>\\d+)\\))?\\/\\k<Show>(?: \\(\\d+\\))? (?<Absolute>\\d+)\\..*$",
|
||||||
|
"^\\/?(?<Collection>.+)?\\/(?<Show>.+?)(?: \\((?<StartYear>\\d+)\\))?\\/\\k<Show>(?: \\(\\d+\\))?\\..*$"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_manager = new Mock<ILibraryManager>();
|
||||||
|
_identifier = new RegexIdentifier(options.Object, _manager.Object);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task EpisodeIdentification()
|
||||||
|
{
|
||||||
|
_manager.Setup(x => x.GetAll(null, new Sort<Library>(), default)).ReturnsAsync(new[]
|
||||||
|
{
|
||||||
|
new Library {Paths = new [] {"/kyoo/Library/"}}
|
||||||
|
});
|
||||||
|
(Collection collection, Show show, Season season, Episode episode) = await _identifier.Identify(
|
||||||
|
"/kyoo/Library/Collection/Show (2000)/Show S01E01.extension");
|
||||||
|
Assert.Equal("Collection", collection.Name);
|
||||||
|
Assert.Equal("collection", collection.Slug);
|
||||||
|
Assert.Equal("Show", show.Title);
|
||||||
|
Assert.Equal("show", show.Slug);
|
||||||
|
Assert.Equal(2000, show.StartAir!.Value.Year);
|
||||||
|
Assert.Equal(1, season.SeasonNumber);
|
||||||
|
Assert.Equal(1, episode.SeasonNumber);
|
||||||
|
Assert.Equal(1, episode.EpisodeNumber);
|
||||||
|
Assert.Null(episode.AbsoluteNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task EpisodeIdentificationWithoutLibraryTrailingSlash()
|
||||||
|
{
|
||||||
|
_manager.Setup(x => x.GetAll(null, new Sort<Library>(), default)).ReturnsAsync(new[]
|
||||||
|
{
|
||||||
|
new Library {Paths = new [] {"/kyoo/Library"}}
|
||||||
|
});
|
||||||
|
(Collection collection, Show show, Season season, Episode episode) = await _identifier.Identify(
|
||||||
|
"/kyoo/Library/Collection/Show (2000)/Show S01E01.extension");
|
||||||
|
Assert.Equal("Collection", collection.Name);
|
||||||
|
Assert.Equal("collection", collection.Slug);
|
||||||
|
Assert.Equal("Show", show.Title);
|
||||||
|
Assert.Equal("show", show.Slug);
|
||||||
|
Assert.Equal(2000, show.StartAir!.Value.Year);
|
||||||
|
Assert.Equal(1, season.SeasonNumber);
|
||||||
|
Assert.Equal(1, episode.SeasonNumber);
|
||||||
|
Assert.Equal(1, episode.EpisodeNumber);
|
||||||
|
Assert.Null(episode.AbsoluteNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task EpisodeIdentificationMultiplePaths()
|
||||||
|
{
|
||||||
|
_manager.Setup(x => x.GetAll(null, new Sort<Library>(), default)).ReturnsAsync(new[]
|
||||||
|
{
|
||||||
|
new Library {Paths = new [] {"/kyoo", "/kyoo/Library/"}}
|
||||||
|
});
|
||||||
|
(Collection collection, Show show, Season season, Episode episode) = await _identifier.Identify(
|
||||||
|
"/kyoo/Library/Collection/Show (2000)/Show S01E01.extension");
|
||||||
|
Assert.Equal("Collection", collection.Name);
|
||||||
|
Assert.Equal("collection", collection.Slug);
|
||||||
|
Assert.Equal("Show", show.Title);
|
||||||
|
Assert.Equal("show", show.Slug);
|
||||||
|
Assert.Equal(2000, show.StartAir!.Value.Year);
|
||||||
|
Assert.Equal(1, season.SeasonNumber);
|
||||||
|
Assert.Equal(1, episode.SeasonNumber);
|
||||||
|
Assert.Equal(1, episode.EpisodeNumber);
|
||||||
|
Assert.Null(episode.AbsoluteNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task AbsoluteEpisodeIdentification()
|
||||||
|
{
|
||||||
|
_manager.Setup(x => x.GetAll(null, new Sort<Library>(), default)).ReturnsAsync(new[]
|
||||||
|
{
|
||||||
|
new Library {Paths = new [] {"/kyoo", "/kyoo/Library/"}}
|
||||||
|
});
|
||||||
|
(Collection collection, Show show, Season season, Episode episode) = await _identifier.Identify(
|
||||||
|
"/kyoo/Library/Collection/Show (2000)/Show 100.extension");
|
||||||
|
Assert.Equal("Collection", collection.Name);
|
||||||
|
Assert.Equal("collection", collection.Slug);
|
||||||
|
Assert.Equal("Show", show.Title);
|
||||||
|
Assert.Equal("show", show.Slug);
|
||||||
|
Assert.Equal(2000, show.StartAir!.Value.Year);
|
||||||
|
Assert.Null(season);
|
||||||
|
Assert.Null(episode.SeasonNumber);
|
||||||
|
Assert.Null(episode.EpisodeNumber);
|
||||||
|
Assert.Equal(100, episode.AbsoluteNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task MovieEpisodeIdentification()
|
||||||
|
{
|
||||||
|
_manager.Setup(x => x.GetAll(null, new Sort<Library>(), default)).ReturnsAsync(new[]
|
||||||
|
{
|
||||||
|
new Library {Paths = new [] {"/kyoo", "/kyoo/Library/"}}
|
||||||
|
});
|
||||||
|
(Collection collection, Show show, Season season, Episode episode) = await _identifier.Identify(
|
||||||
|
"/kyoo/Library/Collection/Show (2000)/Show.extension");
|
||||||
|
Assert.Equal("Collection", collection.Name);
|
||||||
|
Assert.Equal("collection", collection.Slug);
|
||||||
|
Assert.Equal("Show", show.Title);
|
||||||
|
Assert.Equal("show", show.Slug);
|
||||||
|
Assert.Equal(2000, show.StartAir!.Value.Year);
|
||||||
|
Assert.Null(season);
|
||||||
|
Assert.True(show.IsMovie);
|
||||||
|
Assert.Null(episode.SeasonNumber);
|
||||||
|
Assert.Null(episode.EpisodeNumber);
|
||||||
|
Assert.Null(episode.AbsoluteNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -17,6 +17,7 @@
|
|||||||
<PackageReference Include="Divergic.Logging.Xunit" Version="3.6.0" />
|
<PackageReference Include="Divergic.Logging.Xunit" Version="3.6.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="5.0.0" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="5.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||||
|
<PackageReference Include="Moq" Version="4.16.1" />
|
||||||
<PackageReference Include="xunit" Version="2.4.1" />
|
<PackageReference Include="xunit" Version="2.4.1" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Kyoo.Models;
|
using Kyoo.Models;
|
||||||
@ -18,24 +19,48 @@ namespace Kyoo.Controllers
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The configuration of kyoo to retrieve the identifier regex.
|
/// The configuration of kyoo to retrieve the identifier regex.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly IOptions<MediaOptions> _configuration;
|
private readonly IOptionsMonitor<MediaOptions> _configuration;
|
||||||
|
/// <summary>
|
||||||
|
/// The library manager used to retrieve libraries paths.
|
||||||
|
/// </summary>
|
||||||
|
private readonly ILibraryManager _libraryManager;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a new <see cref="RegexIdentifier"/>.
|
/// Create a new <see cref="RegexIdentifier"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="configuration">The regex patterns to use.</param>
|
/// <param name="configuration">The regex patterns to use.</param>
|
||||||
public RegexIdentifier(IOptions<MediaOptions> configuration)
|
/// <param name="libraryManager">The library manager used to retrieve libraries paths.</param>
|
||||||
|
public RegexIdentifier(IOptionsMonitor<MediaOptions> configuration, ILibraryManager libraryManager)
|
||||||
{
|
{
|
||||||
_configuration = configuration;
|
_configuration = configuration;
|
||||||
|
_libraryManager = libraryManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieve the relative path of an episode or subtitle.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">The full path of the episode</param>
|
||||||
|
/// <returns>The path relative to the library root.</returns>
|
||||||
|
private async Task<string> _GetRelativePath(string path)
|
||||||
|
{
|
||||||
|
string libraryPath = (await _libraryManager.GetAll<Library>())
|
||||||
|
.SelectMany(x => x.Paths)
|
||||||
|
.Where(path.StartsWith)
|
||||||
|
.OrderByDescending(x => x.Length)
|
||||||
|
.FirstOrDefault();
|
||||||
|
return path[(libraryPath?.Length ?? 0)..];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Task<(Collection, Show, Season, Episode)> Identify(string path, string relativePath)
|
public async Task<(Collection, Show, Season, Episode)> Identify(string path)
|
||||||
{
|
{
|
||||||
Regex regex = new(_configuration.Value.Regex, RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
string relativePath = await _GetRelativePath(path);
|
||||||
Match match = regex.Match(relativePath);
|
Match match = _configuration.CurrentValue.Regex
|
||||||
|
.Select(x => new Regex(x, RegexOptions.IgnoreCase | RegexOptions.Compiled))
|
||||||
|
.Select(x => x.Match(relativePath))
|
||||||
|
.FirstOrDefault(x => x.Success);
|
||||||
|
|
||||||
if (!match.Success)
|
if (match == null)
|
||||||
throw new IdentificationFailedException($"The episode at {path} does not match the episode's regex.");
|
throw new IdentificationFailedException($"The episode at {path} does not match the episode's regex.");
|
||||||
|
|
||||||
(Collection collection, Show show, Season season, Episode episode) ret = (
|
(Collection collection, Show show, Season season, Episode episode) ret = (
|
||||||
@ -80,20 +105,23 @@ namespace Kyoo.Controllers
|
|||||||
ret.episode.Title = ret.show.Title;
|
ret.episode.Title = ret.show.Title;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Task.FromResult(ret);
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Task<Track> IdentifyTrack(string path, string relativePath)
|
public async Task<Track> IdentifyTrack(string path)
|
||||||
{
|
{
|
||||||
Regex regex = new(_configuration.Value.SubtitleRegex, RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
string relativePath = await _GetRelativePath(path);
|
||||||
Match match = regex.Match(path);
|
Match match = _configuration.CurrentValue.SubtitleRegex
|
||||||
|
.Select(x => new Regex(x, RegexOptions.IgnoreCase | RegexOptions.Compiled))
|
||||||
|
.Select(x => x.Match(relativePath))
|
||||||
|
.FirstOrDefault(x => x.Success);
|
||||||
|
|
||||||
if (!match.Success)
|
if (match == null)
|
||||||
throw new IdentificationFailedException($"The subtitle at {path} does not match the subtitle's regex.");
|
throw new IdentificationFailedException($"The subtitle at {path} does not match the subtitle's regex.");
|
||||||
|
|
||||||
string episodePath = match.Groups["Episode"].Value;
|
string episodePath = match.Groups["Episode"].Value;
|
||||||
return Task.FromResult(new Track
|
return new Track
|
||||||
{
|
{
|
||||||
Type = StreamType.Subtitle,
|
Type = StreamType.Subtitle,
|
||||||
Language = match.Groups["Language"].Value,
|
Language = match.Groups["Language"].Value,
|
||||||
@ -106,7 +134,7 @@ namespace Kyoo.Controllers
|
|||||||
{
|
{
|
||||||
Path = episodePath
|
Path = episodePath
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -13,11 +13,11 @@ namespace Kyoo.Models.Options
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A regex for episodes
|
/// A regex for episodes
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Regex { get; set; }
|
public string[] Regex { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A regex for subtitles
|
/// A regex for subtitles
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string SubtitleRegex { get; set; }
|
public string[] SubtitleRegex { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -73,8 +73,6 @@ namespace Kyoo.Tasks
|
|||||||
return new()
|
return new()
|
||||||
{
|
{
|
||||||
TaskParameter.CreateRequired<string>("path", "The path of the episode file"),
|
TaskParameter.CreateRequired<string>("path", "The path of the episode file"),
|
||||||
TaskParameter.CreateRequired<string>("relativePath",
|
|
||||||
"The path of the episode file relative to the library root. It starts with a /."),
|
|
||||||
TaskParameter.CreateRequired<Library>("library", "The library in witch the episode is")
|
TaskParameter.CreateRequired<Library>("library", "The library in witch the episode is")
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -83,17 +81,19 @@ namespace Kyoo.Tasks
|
|||||||
public async Task Run(TaskParameters arguments, IProgress<float> progress, CancellationToken cancellationToken)
|
public async Task Run(TaskParameters arguments, IProgress<float> progress, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
string path = arguments["path"].As<string>();
|
string path = arguments["path"].As<string>();
|
||||||
string relativePath = arguments["relativePath"].As<string>();
|
|
||||||
Library library = arguments["library"].As<Library>();
|
Library library = arguments["library"].As<Library>();
|
||||||
progress.Report(0);
|
progress.Report(0);
|
||||||
|
|
||||||
|
if (library != null)
|
||||||
|
{
|
||||||
if (library.Providers == null)
|
if (library.Providers == null)
|
||||||
await _libraryManager.Load(library, x => x.Providers);
|
await _libraryManager.Load(library, x => x.Providers);
|
||||||
_metadataProvider.UseProviders(library.Providers);
|
_metadataProvider.UseProviders(library.Providers);
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
(Collection collection, Show show, Season season, Episode episode) = await _identifier.Identify(path,
|
(Collection collection, Show show, Season season, Episode episode) = await _identifier.Identify(path);
|
||||||
relativePath);
|
|
||||||
progress.Report(15);
|
progress.Report(15);
|
||||||
|
|
||||||
collection = await _RegisterAndFill(collection);
|
collection = await _RegisterAndFill(collection);
|
||||||
|
@ -39,9 +39,7 @@ namespace Kyoo.Tasks
|
|||||||
{
|
{
|
||||||
return new()
|
return new()
|
||||||
{
|
{
|
||||||
TaskParameter.CreateRequired<string>("path", "The path of the subtitle file"),
|
TaskParameter.CreateRequired<string>("path", "The path of the subtitle file")
|
||||||
TaskParameter.CreateRequired<string>("relativePath",
|
|
||||||
"The path of the subtitle file relative to the library root. It starts with a /.")
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,12 +47,11 @@ namespace Kyoo.Tasks
|
|||||||
public async Task Run(TaskParameters arguments, IProgress<float> progress, CancellationToken cancellationToken)
|
public async Task Run(TaskParameters arguments, IProgress<float> progress, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
string path = arguments["path"].As<string>();
|
string path = arguments["path"].As<string>();
|
||||||
string relativePath = arguments["relativePath"].As<string>();
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
progress.Report(0);
|
progress.Report(0);
|
||||||
Track track = await _identifier.IdentifyTrack(path, relativePath);
|
Track track = await _identifier.IdentifyTrack(path);
|
||||||
progress.Report(25);
|
progress.Report(25);
|
||||||
|
|
||||||
if (track.Episode == null)
|
if (track.Episode == null)
|
||||||
|
@ -44,8 +44,14 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
"media": {
|
"media": {
|
||||||
"regex": "(?:\\/(?<Collection>.*?))?\\/(?<Show>.*?)(?: \\(?<StartYear>\\d+\\))?\\/\\k<Show>(?: \\(\\d+\\))?(?:(?: S(?<Season>\\d+)E(?<Episode>\\d+))| (?<Absolute>\\d+))?.*$",
|
"regex": [
|
||||||
"subtitleRegex": "^(?<Episode>.*)\\.(?<Language>\\w{1,3})\\.(?<Default>default\\.)?(?<Forced>forced\\.)?.*$"
|
"^\\/?(?<Collection>.+)?\\/(?<Show>.+?)(?: \\((?<StartYear>\\d+)\\))?\\/\\k<Show>(?: \\(\\d+\\))? S(?<Season>\\d+)E(?<Episode>\\d+)\\..*$",
|
||||||
|
"^\\/?(?<Collection>.+)?\\/(?<Show>.+?)(?: \\((?<StartYear>\\d+)\\))?\\/\\k<Show>(?: \\(\\d+\\))? (?<Absolute>\\d+)\\..*$",
|
||||||
|
"^\\/?(?<Collection>.+)?\\/(?<Show>.+?)(?: \\((?<StartYear>\\d+)\\))?\\/\\k<Show>(?: \\(\\d+\\))?\\..*$"
|
||||||
|
],
|
||||||
|
"subtitleRegex": [
|
||||||
|
"^(?<Episode>.+)\\.(?<Language>\\w{1,3})\\.(?<Default>default\\.)?(?<Forced>forced\\.)?.*$"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
"authentication": {
|
"authentication": {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user