aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.ci/azure-pipelines.yml8
-rw-r--r--Emby.Naming/Common/NamingOptions.cs39
-rw-r--r--Emby.Naming/Emby.Naming.csproj2
-rw-r--r--Emby.Naming/Video/CleanStringParser.cs2
-rw-r--r--Emby.Naming/Video/ExtraResolver.cs2
-rw-r--r--Emby.Naming/Video/ExtraResult.cs4
-rw-r--r--Emby.Naming/Video/ExtraRule.cs5
-rw-r--r--Emby.Naming/Video/StubResolver.cs4
-rw-r--r--Emby.Naming/Video/VideoFileInfo.cs6
-rw-r--r--Emby.Naming/Video/VideoListResolver.cs9
-rw-r--r--Emby.Naming/Video/VideoResolver.cs2
-rw-r--r--Emby.Photos/Emby.Photos.csproj7
-rw-r--r--Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs1
-rw-r--r--Emby.Server.Implementations/Activity/ActivityManager.cs1
-rw-r--r--Emby.Server.Implementations/Activity/ActivityRepository.cs1
-rw-r--r--Emby.Server.Implementations/ApplicationHost.cs13
-rw-r--r--Emby.Server.Implementations/Branding/BrandingConfigurationFactory.cs1
-rw-r--r--Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs1
-rw-r--r--Emby.Server.Implementations/Channels/ChannelImageProvider.cs1
-rw-r--r--Emby.Server.Implementations/Channels/ChannelManager.cs1
-rw-r--r--Emby.Server.Implementations/Channels/ChannelPostScanTask.cs1
-rw-r--r--Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs1
-rw-r--r--Emby.Server.Implementations/Collections/CollectionImageProvider.cs1
-rw-r--r--Emby.Server.Implementations/Collections/CollectionManager.cs1
-rw-r--r--Emby.Server.Implementations/Cryptography/CryptographyProvider.cs2
-rw-r--r--Emby.Server.Implementations/Data/BaseSqliteRepository.cs1
-rw-r--r--Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs1
-rw-r--r--Emby.Server.Implementations/Data/ManagedConnection.cs1
-rw-r--r--Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs1
-rw-r--r--Emby.Server.Implementations/Data/SqliteExtensions.cs1
-rw-r--r--Emby.Server.Implementations/Data/SqliteUserDataRepository.cs1
-rw-r--r--Emby.Server.Implementations/Data/SqliteUserRepository.cs1
-rw-r--r--Emby.Server.Implementations/Devices/DeviceId.cs1
-rw-r--r--Emby.Server.Implementations/Devices/DeviceManager.cs1
-rw-r--r--Emby.Server.Implementations/Diagnostics/CommonProcess.cs1
-rw-r--r--Emby.Server.Implementations/Diagnostics/ProcessFactory.cs1
-rw-r--r--Emby.Server.Implementations/Dto/DtoService.cs1
-rw-r--r--Emby.Server.Implementations/Emby.Server.Implementations.csproj14
-rw-r--r--Emby.Server.Implementations/EntryPoints/AutomaticRestartEntryPoint.cs1
-rw-r--r--Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs1
-rw-r--r--Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs1
-rw-r--r--Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs1
-rw-r--r--Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs1
-rw-r--r--Emby.Server.Implementations/HttpServer/FileWriter.cs1
-rw-r--r--Emby.Server.Implementations/HttpServer/HttpListenerHost.cs2
-rw-r--r--Emby.Server.Implementations/HttpServer/HttpResultFactory.cs1
-rw-r--r--Emby.Server.Implementations/HttpServer/IHttpListener.cs1
-rw-r--r--Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs1
-rw-r--r--Emby.Server.Implementations/HttpServer/Security/AuthService.cs1
-rw-r--r--Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs1
-rw-r--r--Emby.Server.Implementations/HttpServer/Security/SessionContext.cs1
-rw-r--r--Emby.Server.Implementations/IO/ExtendedFileSystemInfo.cs1
-rw-r--r--Emby.Server.Implementations/IO/FileRefresher.cs1
-rw-r--r--Emby.Server.Implementations/IO/LibraryMonitor.cs1
-rw-r--r--Emby.Server.Implementations/IO/ManagedFileSystem.cs1
-rw-r--r--Emby.Server.Implementations/IO/MbLinkShortcutHandler.cs1
-rw-r--r--Emby.Server.Implementations/IO/StreamHelper.cs1
-rw-r--r--Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs1
-rw-r--r--Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs13
-rw-r--r--Emby.Server.Implementations/Library/ExclusiveLiveStream.cs1
-rw-r--r--Emby.Server.Implementations/Library/LibraryManager.cs32
-rw-r--r--Emby.Server.Implementations/Library/LiveStreamHelper.cs1
-rw-r--r--Emby.Server.Implementations/Library/MediaSourceManager.cs1
-rw-r--r--Emby.Server.Implementations/Library/MediaStreamSelector.cs1
-rw-r--r--Emby.Server.Implementations/Library/MusicManager.cs1
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs1
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs1
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs1
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs1
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs1
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs1
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs1
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs1
-rw-r--r--Emby.Server.Implementations/Library/SearchEngine.cs1
-rw-r--r--Emby.Server.Implementations/Library/UserDataManager.cs1
-rw-r--r--Emby.Server.Implementations/Library/UserManager.cs3
-rw-r--r--Emby.Server.Implementations/Library/UserViewManager.cs1
-rw-r--r--Emby.Server.Implementations/Localization/Core/fr.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/ms.json28
-rw-r--r--Emby.Server.Implementations/Localization/Core/sr.json1
-rw-r--r--Jellyfin.Api/Jellyfin.Api.csproj2
-rw-r--r--Jellyfin.Server/Jellyfin.Server.csproj12
-rw-r--r--MediaBrowser.Api/Playback/MediaInfoService.cs91
-rw-r--r--MediaBrowser.Api/Playback/UniversalAudioService.cs2
-rw-r--r--MediaBrowser.Common/Configuration/ConfigurationUpdateEventArgs.cs1
-rw-r--r--MediaBrowser.Common/Configuration/IConfigurationFactory.cs1
-rw-r--r--MediaBrowser.Common/Configuration/IConfigurationManager.cs1
-rw-r--r--MediaBrowser.Common/Cryptography/PasswordHash.cs25
-rw-r--r--MediaBrowser.Common/Events/EventHelper.cs8
-rw-r--r--MediaBrowser.Common/Extensions/BaseExtensions.cs7
-rw-r--r--MediaBrowser.Common/Extensions/CopyToExtensions.cs4
-rw-r--r--MediaBrowser.Common/Extensions/MethodNotAllowedException.cs26
-rw-r--r--MediaBrowser.Common/Extensions/RateLimitExceededException.cs26
-rw-r--r--MediaBrowser.Common/Extensions/ResourceNotFoundException.cs65
-rw-r--r--MediaBrowser.Common/Hex.cs12
-rw-r--r--MediaBrowser.Common/IApplicationHost.cs55
-rw-r--r--MediaBrowser.Common/MediaBrowser.Common.csproj17
-rw-r--r--MediaBrowser.Common/Net/CustomHeaderNames.cs1
-rw-r--r--MediaBrowser.Common/Net/HttpRequestOptions.cs28
-rw-r--r--MediaBrowser.Common/Net/HttpResponseInfo.cs32
-rw-r--r--MediaBrowser.Common/Net/IHttpClient.cs4
-rw-r--r--MediaBrowser.Common/Net/INetworkManager.cs11
-rw-r--r--MediaBrowser.Common/Plugins/BasePlugin.cs150
-rw-r--r--MediaBrowser.Common/Plugins/IPlugin.cs29
-rw-r--r--MediaBrowser.Common/Plugins/IPluginAssembly.cs14
-rw-r--r--MediaBrowser.Common/Progress/ActionableProgress.cs1
-rw-r--r--MediaBrowser.Common/Providers/SubtitleConfigurationFactory.cs1
-rw-r--r--MediaBrowser.Common/System/OperatingSystem.cs1
-rw-r--r--MediaBrowser.Common/Updates/IInstallationManager.cs10
-rw-r--r--MediaBrowser.Common/Updates/InstallationEventArgs.cs1
-rw-r--r--MediaBrowser.Common/Updates/InstallationFailedEventArgs.cs1
-rw-r--r--MediaBrowser.Controller/Authentication/AuthenticationResult.cs7
-rw-r--r--MediaBrowser.Controller/Entities/BaseItem.cs1
-rw-r--r--MediaBrowser.Model/Entities/MediaType.cs5
-rw-r--r--MediaBrowser.Model/MediaInfo/PlaybackInfoResponse.cs12
-rw-r--r--jellyfin.ruleset12
-rw-r--r--tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj2
-rw-r--r--tests/Jellyfin.Naming.Tests/Music/MultiDiscAlbumTests.cs57
-rw-r--r--tests/Jellyfin.Naming.Tests/Subtitles/SubtitleParserTests.cs40
-rw-r--r--tests/Jellyfin.Naming.Tests/TV/AbsoluteEpisodeNumberTests.cs61
-rw-r--r--tests/Jellyfin.Naming.Tests/TV/DailyEpisodeTests.cs69
-rw-r--r--tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs424
-rw-r--r--tests/Jellyfin.Naming.Tests/TV/EpisodeNumberWithoutSeasonTests.cs127
-rw-r--r--tests/Jellyfin.Naming.Tests/TV/EpisodePathParserTest.cs (renamed from tests/Jellyfin.Naming.Tests/EpisodePathParserTest.cs)22
-rw-r--r--tests/Jellyfin.Naming.Tests/TV/EpisodeWithoutSeasonTests.cs56
-rw-r--r--tests/Jellyfin.Naming.Tests/TV/MultiEpisodeTests.cs105
-rw-r--r--tests/Jellyfin.Naming.Tests/TV/SeasonFolderTests.cs112
-rw-r--r--tests/Jellyfin.Naming.Tests/TV/SeasonNumberTests.cs305
-rw-r--r--tests/Jellyfin.Naming.Tests/TV/SimpleEpisodeTests.cs95
-rw-r--r--tests/Jellyfin.Naming.Tests/Video/BaseVideoTest.cs15
-rw-r--r--tests/Jellyfin.Naming.Tests/Video/CleanDateTimeTests.cs143
-rw-r--r--tests/Jellyfin.Naming.Tests/Video/CleanStringTests.cs133
-rw-r--r--tests/Jellyfin.Naming.Tests/Video/ExtraTests.cs77
-rw-r--r--tests/Jellyfin.Naming.Tests/Video/Format3DTests.cs78
-rw-r--r--tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs438
-rw-r--r--tests/Jellyfin.Naming.Tests/Video/StackTests.cs478
-rw-r--r--tests/Jellyfin.Naming.Tests/Video/StubTests.cs55
-rw-r--r--tests/Jellyfin.Naming.Tests/Video/VideoListResolverTests.cs457
-rw-r--r--tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs275
139 files changed, 4134 insertions, 424 deletions
diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml
index 143873266..7bcaed70c 100644
--- a/.ci/azure-pipelines.yml
+++ b/.ci/azure-pipelines.yml
@@ -2,7 +2,7 @@ name: $(Date:yyyyMMdd)$(Rev:.r)
variables:
- name: TestProjects
- value: 'tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj'
+ value: 'tests/**/*Tests.csproj'
- name: RestoreBuildProjects
value: 'Jellyfin.Server/Jellyfin.Server.csproj'
@@ -132,12 +132,12 @@ jobs:
inputs:
packageFeedSelector: 'nugetOrg' # Options: nugetOrg, customFeed, netShare
versionSelector: 'latestPreRelease' # Required when packageFeedSelector == NugetOrg || PackageFeedSelector == CustomFeed# Options: latestPreRelease, latestStable, specificVersion
-
- task: VSTest@2
inputs:
testSelector: 'testAssemblies' # Options: testAssemblies, testPlan, testRun
testAssemblyVer2: | # Required when testSelector == TestAssemblies
- **\bin\$(BuildConfiguration)\**\*test*.dll
+ **\bin\$(BuildConfiguration)\**\*tests.dll
+ **\bin\$(BuildConfiguration)\**\*test.dll
!**\obj\**
!**\xunit.runner.visualstudio.testadapter.dll
!**\xunit.runner.visualstudio.dotnetcore.testadapter.dll
@@ -147,6 +147,8 @@ jobs:
codeCoverageEnabled: True # Optional
configuration: 'Debug' # Optional
publishRunAttachments: true # Optional
+ testRunTitle: $(Agent.JobName)
+ otherConsoleOptions: '/platform:x64 /Framework:.NETCoreApp,Version=v3.1 /logger:console;verbosity="normal"'
- job: main_build_win
displayName: Publish Windows
diff --git a/Emby.Naming/Common/NamingOptions.cs b/Emby.Naming/Common/NamingOptions.cs
index 69e68660d..a2105889b 100644
--- a/Emby.Naming/Common/NamingOptions.cs
+++ b/Emby.Naming/Common/NamingOptions.cs
@@ -5,6 +5,7 @@ using System;
using System.Linq;
using System.Text.RegularExpressions;
using Emby.Naming.Video;
+using MediaBrowser.Model.Entities;
namespace Emby.Naming.Common
{
@@ -176,7 +177,7 @@ namespace Emby.Naming.Common
CleanDateTimes = new[]
{
- @"(.+[^ _\,\.\(\)\[\]\-])[ _\.\(\)\[\]\-]+(19[0-9][0-9]|20[0-1][0-9])([ _\,\.\(\)\[\]\-][^0-9]|$)"
+ @"(.+[^_\,\.\(\)\[\]\-])[_\.\(\)\[\]\-](\d{4})([ _\,\.\(\)\[\]\-][^\d]|).*(\d{4})*"
};
CleanStrings = new[]
@@ -423,126 +424,126 @@ namespace Emby.Naming.Common
{
new ExtraRule
{
- ExtraType = "trailer",
+ ExtraType = ExtraType.Trailer,
RuleType = ExtraRuleType.Filename,
Token = "trailer",
MediaType = MediaType.Video
},
new ExtraRule
{
- ExtraType = "trailer",
+ ExtraType = ExtraType.Trailer,
RuleType = ExtraRuleType.Suffix,
Token = "-trailer",
MediaType = MediaType.Video
},
new ExtraRule
{
- ExtraType = "trailer",
+ ExtraType = ExtraType.Trailer,
RuleType = ExtraRuleType.Suffix,
Token = ".trailer",
MediaType = MediaType.Video
},
new ExtraRule
{
- ExtraType = "trailer",
+ ExtraType = ExtraType.Trailer,
RuleType = ExtraRuleType.Suffix,
Token = "_trailer",
MediaType = MediaType.Video
},
new ExtraRule
{
- ExtraType = "trailer",
+ ExtraType = ExtraType.Trailer,
RuleType = ExtraRuleType.Suffix,
Token = " trailer",
MediaType = MediaType.Video
},
new ExtraRule
{
- ExtraType = "sample",
+ ExtraType = ExtraType.Sample,
RuleType = ExtraRuleType.Filename,
Token = "sample",
MediaType = MediaType.Video
},
new ExtraRule
{
- ExtraType = "sample",
+ ExtraType = ExtraType.Sample,
RuleType = ExtraRuleType.Suffix,
Token = "-sample",
MediaType = MediaType.Video
},
new ExtraRule
{
- ExtraType = "sample",
+ ExtraType = ExtraType.Sample,
RuleType = ExtraRuleType.Suffix,
Token = ".sample",
MediaType = MediaType.Video
},
new ExtraRule
{
- ExtraType = "sample",
+ ExtraType = ExtraType.Sample,
RuleType = ExtraRuleType.Suffix,
Token = "_sample",
MediaType = MediaType.Video
},
new ExtraRule
{
- ExtraType = "sample",
+ ExtraType = ExtraType.Sample,
RuleType = ExtraRuleType.Suffix,
Token = " sample",
MediaType = MediaType.Video
},
new ExtraRule
{
- ExtraType = "themesong",
+ ExtraType = ExtraType.ThemeSong,
RuleType = ExtraRuleType.Filename,
Token = "theme",
MediaType = MediaType.Audio
},
new ExtraRule
{
- ExtraType = "scene",
+ ExtraType = ExtraType.Scene,
RuleType = ExtraRuleType.Suffix,
Token = "-scene",
MediaType = MediaType.Video
},
new ExtraRule
{
- ExtraType = "clip",
+ ExtraType = ExtraType.Clip,
RuleType = ExtraRuleType.Suffix,
Token = "-clip",
MediaType = MediaType.Video
},
new ExtraRule
{
- ExtraType = "interview",
+ ExtraType = ExtraType.Interview,
RuleType = ExtraRuleType.Suffix,
Token = "-interview",
MediaType = MediaType.Video
},
new ExtraRule
{
- ExtraType = "behindthescenes",
+ ExtraType = ExtraType.BehindTheScenes,
RuleType = ExtraRuleType.Suffix,
Token = "-behindthescenes",
MediaType = MediaType.Video
},
new ExtraRule
{
- ExtraType = "deletedscene",
+ ExtraType = ExtraType.DeletedScene,
RuleType = ExtraRuleType.Suffix,
Token = "-deleted",
MediaType = MediaType.Video
},
new ExtraRule
{
- ExtraType = "featurette",
+ ExtraType = ExtraType.Clip,
RuleType = ExtraRuleType.Suffix,
Token = "-featurette",
MediaType = MediaType.Video
},
new ExtraRule
{
- ExtraType = "short",
+ ExtraType = ExtraType.Clip,
RuleType = ExtraRuleType.Suffix,
Token = "-short",
MediaType = MediaType.Video
diff --git a/Emby.Naming/Emby.Naming.csproj b/Emby.Naming/Emby.Naming.csproj
index ed1670adf..900b9694c 100644
--- a/Emby.Naming/Emby.Naming.csproj
+++ b/Emby.Naming/Emby.Naming.csproj
@@ -27,7 +27,7 @@
<!-- Code Analyzers-->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
- <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.7" PrivateAssets="All" />
+ <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8" PrivateAssets="All" />
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" />
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
diff --git a/Emby.Naming/Video/CleanStringParser.cs b/Emby.Naming/Video/CleanStringParser.cs
index be028c662..fcd4b65c7 100644
--- a/Emby.Naming/Video/CleanStringParser.cs
+++ b/Emby.Naming/Video/CleanStringParser.cs
@@ -7,7 +7,7 @@ using System.Text.RegularExpressions;
namespace Emby.Naming.Video
{
/// <summary>
- /// http://kodi.wiki/view/Advancedsettings.xml#video
+ /// <see href="http://kodi.wiki/view/Advancedsettings.xml#video" />.
/// </summary>
public class CleanStringParser
{
diff --git a/Emby.Naming/Video/ExtraResolver.cs b/Emby.Naming/Video/ExtraResolver.cs
index 989ede206..ea9a6d6c2 100644
--- a/Emby.Naming/Video/ExtraResolver.cs
+++ b/Emby.Naming/Video/ExtraResolver.cs
@@ -23,7 +23,7 @@ namespace Emby.Naming.Video
{
return _options.VideoExtraRules
.Select(i => GetExtraInfo(path, i))
- .FirstOrDefault(i => !string.IsNullOrEmpty(i.ExtraType)) ?? new ExtraResult();
+ .FirstOrDefault(i => i.ExtraType != null) ?? new ExtraResult();
}
private ExtraResult GetExtraInfo(string path, ExtraRule rule)
diff --git a/Emby.Naming/Video/ExtraResult.cs b/Emby.Naming/Video/ExtraResult.cs
index 6081a4494..4e991d685 100644
--- a/Emby.Naming/Video/ExtraResult.cs
+++ b/Emby.Naming/Video/ExtraResult.cs
@@ -1,6 +1,8 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
+using MediaBrowser.Model.Entities;
+
namespace Emby.Naming.Video
{
public class ExtraResult
@@ -9,7 +11,7 @@ namespace Emby.Naming.Video
/// Gets or sets the type of the extra.
/// </summary>
/// <value>The type of the extra.</value>
- public string ExtraType { get; set; }
+ public ExtraType? ExtraType { get; set; }
/// <summary>
/// Gets or sets the rule.
diff --git a/Emby.Naming/Video/ExtraRule.cs b/Emby.Naming/Video/ExtraRule.cs
index cfce79fd0..cfaa84ed6 100644
--- a/Emby.Naming/Video/ExtraRule.cs
+++ b/Emby.Naming/Video/ExtraRule.cs
@@ -1,7 +1,8 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
-using Emby.Naming.Common;
+using MediaBrowser.Model.Entities;
+using MediaType = Emby.Naming.Common.MediaType;
namespace Emby.Naming.Video
{
@@ -17,7 +18,7 @@ namespace Emby.Naming.Video
/// Gets or sets the type of the extra.
/// </summary>
/// <value>The type of the extra.</value>
- public string ExtraType { get; set; }
+ public ExtraType ExtraType { get; set; }
/// <summary>
/// Gets or sets the type of the rule.
diff --git a/Emby.Naming/Video/StubResolver.cs b/Emby.Naming/Video/StubResolver.cs
index bbf399677..95868e89d 100644
--- a/Emby.Naming/Video/StubResolver.cs
+++ b/Emby.Naming/Video/StubResolver.cs
@@ -14,14 +14,14 @@ namespace Emby.Naming.Video
{
if (path == null)
{
- return default(StubResult);
+ return default;
}
var extension = Path.GetExtension(path);
if (!options.StubFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
{
- return default(StubResult);
+ return default;
}
var result = new StubResult()
diff --git a/Emby.Naming/Video/VideoFileInfo.cs b/Emby.Naming/Video/VideoFileInfo.cs
index 250a1ec45..90c798da1 100644
--- a/Emby.Naming/Video/VideoFileInfo.cs
+++ b/Emby.Naming/Video/VideoFileInfo.cs
@@ -1,3 +1,5 @@
+using MediaBrowser.Model.Entities;
+
namespace Emby.Naming.Video
{
/// <summary>
@@ -30,10 +32,10 @@ namespace Emby.Naming.Video
public int? Year { get; set; }
/// <summary>
- /// Gets or sets the type of the extra, e.g. trailer, theme song, behing the scenes, etc.
+ /// Gets or sets the type of the extra, e.g. trailer, theme song, behind the scenes, etc.
/// </summary>
/// <value>The type of the extra.</value>
- public string ExtraType { get; set; }
+ public ExtraType? ExtraType { get; set; }
/// <summary>
/// Gets or sets the extra rule.
diff --git a/Emby.Naming/Video/VideoListResolver.cs b/Emby.Naming/Video/VideoListResolver.cs
index 5a32846bf..87498000c 100644
--- a/Emby.Naming/Video/VideoListResolver.cs
+++ b/Emby.Naming/Video/VideoListResolver.cs
@@ -7,6 +7,7 @@ using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using Emby.Naming.Common;
+using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
namespace Emby.Naming.Video
@@ -32,7 +33,7 @@ namespace Emby.Naming.Video
// Filter out all extras, otherwise they could cause stacks to not be resolved
// See the unit test TestStackedWithTrailer
var nonExtras = videoInfos
- .Where(i => string.IsNullOrEmpty(i.ExtraType))
+ .Where(i => i.ExtraType == null)
.Select(i => new FileSystemMetadata
{
FullName = i.Path,
@@ -79,7 +80,7 @@ namespace Emby.Naming.Video
}
var standaloneMedia = remainingFiles
- .Where(i => string.IsNullOrEmpty(i.ExtraType))
+ .Where(i => i.ExtraType == null)
.ToList();
foreach (var media in standaloneMedia)
@@ -148,7 +149,7 @@ namespace Emby.Naming.Video
if (list.Count == 1)
{
var trailers = remainingFiles
- .Where(i => string.Equals(i.ExtraType, "trailer", StringComparison.OrdinalIgnoreCase))
+ .Where(i => i.ExtraType == ExtraType.Trailer)
.ToList();
list[0].Extras.AddRange(trailers);
@@ -229,7 +230,7 @@ namespace Emby.Naming.Video
}
return remainingFiles
- .Where(i => !string.IsNullOrEmpty(i.ExtraType))
+ .Where(i => i.ExtraType == null)
.Where(i => baseNames.Any(b => i.FileNameWithoutExtension.StartsWith(b, StringComparison.OrdinalIgnoreCase)))
.ToList();
}
diff --git a/Emby.Naming/Video/VideoResolver.cs b/Emby.Naming/Video/VideoResolver.cs
index 5a93e1eaf..41b79697c 100644
--- a/Emby.Naming/Video/VideoResolver.cs
+++ b/Emby.Naming/Video/VideoResolver.cs
@@ -94,7 +94,7 @@ namespace Emby.Naming.Video
{
var cleanDateTimeResult = CleanDateTime(name);
- if (string.IsNullOrEmpty(extraResult.ExtraType))
+ if (extraResult.ExtraType == null)
{
name = CleanString(cleanDateTimeResult.Name).Name;
}
diff --git a/Emby.Photos/Emby.Photos.csproj b/Emby.Photos/Emby.Photos.csproj
index 23b7a819b..29ed3c5f7 100644
--- a/Emby.Photos/Emby.Photos.csproj
+++ b/Emby.Photos/Emby.Photos.csproj
@@ -26,9 +26,10 @@
<!-- Code Analyzers-->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
- <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.7" />
- <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" />
- <PackageReference Include="SerilogAnalyzer" Version="0.15.0" />
+ <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8" PrivateAssets="All" />
+ <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" />
+ <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
+ <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
</ItemGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
diff --git a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
index b622a3167..ac8af66a2 100644
--- a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
+++ b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/Activity/ActivityManager.cs b/Emby.Server.Implementations/Activity/ActivityManager.cs
index a30e93912..b03c4d182 100644
--- a/Emby.Server.Implementations/Activity/ActivityManager.cs
+++ b/Emby.Server.Implementations/Activity/ActivityManager.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Linq;
diff --git a/Emby.Server.Implementations/Activity/ActivityRepository.cs b/Emby.Server.Implementations/Activity/ActivityRepository.cs
index 7be72319e..633343bb6 100644
--- a/Emby.Server.Implementations/Activity/ActivityRepository.cs
+++ b/Emby.Server.Implementations/Activity/ActivityRepository.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index a179c1b15..0bb1d832f 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Collections.Concurrent;
@@ -177,11 +178,7 @@ namespace Emby.Server.Implementations
/// Gets the plugins.
/// </summary>
/// <value>The plugins.</value>
- public IPlugin[] Plugins
- {
- get => _plugins;
- protected set => _plugins = value;
- }
+ public IReadOnlyList<IPlugin> Plugins => _plugins;
/// <summary>
/// Gets or sets the logger factory.
@@ -1056,7 +1053,7 @@ namespace Emby.Server.Implementations
}
ConfigurationManager.AddParts(GetExports<IConfigurationFactory>());
- Plugins = GetExports<IPlugin>()
+ _plugins = GetExports<IPlugin>()
.Select(LoadPlugin)
.Where(i => i != null)
.ToArray();
@@ -1705,9 +1702,9 @@ namespace Emby.Server.Implementations
/// <param name="plugin">The plugin.</param>
public void RemovePlugin(IPlugin plugin)
{
- var list = Plugins.ToList();
+ var list = _plugins.ToList();
list.Remove(plugin);
- Plugins = list.ToArray();
+ _plugins = list.ToArray();
}
/// <summary>
diff --git a/Emby.Server.Implementations/Branding/BrandingConfigurationFactory.cs b/Emby.Server.Implementations/Branding/BrandingConfigurationFactory.cs
index 93000ae12..15aee63a0 100644
--- a/Emby.Server.Implementations/Branding/BrandingConfigurationFactory.cs
+++ b/Emby.Server.Implementations/Branding/BrandingConfigurationFactory.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System.Collections.Generic;
using MediaBrowser.Common.Configuration;
diff --git a/Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs b/Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs
index 6016fed07..aae416b37 100644
--- a/Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs
+++ b/Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/Channels/ChannelImageProvider.cs b/Emby.Server.Implementations/Channels/ChannelImageProvider.cs
index 62aeb9bcb..fe64f1b15 100644
--- a/Emby.Server.Implementations/Channels/ChannelImageProvider.cs
+++ b/Emby.Server.Implementations/Channels/ChannelImageProvider.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System.Collections.Generic;
using System.Linq;
diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs
index 6e1baddfe..de2e123af 100644
--- a/Emby.Server.Implementations/Channels/ChannelManager.cs
+++ b/Emby.Server.Implementations/Channels/ChannelManager.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Collections.Concurrent;
diff --git a/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs b/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs
index 2712fc8c5..36e0e5e26 100644
--- a/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs
+++ b/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Linq;
diff --git a/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs b/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs
index 5774c0415..039e2c138 100644
--- a/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs
+++ b/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/Collections/CollectionImageProvider.cs b/Emby.Server.Implementations/Collections/CollectionImageProvider.cs
index 1fa556ec9..8006b8694 100644
--- a/Emby.Server.Implementations/Collections/CollectionImageProvider.cs
+++ b/Emby.Server.Implementations/Collections/CollectionImageProvider.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs
index 1d7c11989..efdef8481 100644
--- a/Emby.Server.Implementations/Collections/CollectionManager.cs
+++ b/Emby.Server.Implementations/Collections/CollectionManager.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs b/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs
index 776074b72..de83b023d 100644
--- a/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs
+++ b/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs
@@ -121,7 +121,7 @@ namespace Emby.Server.Implementations.Cryptography
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
- /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
+ /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected virtual void Dispose(bool disposing)
{
if (_disposed)
diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
index 0654132f4..b7f643819 100644
--- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
+++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs b/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs
index 2a8f2d6b3..8a5387e9b 100644
--- a/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs
+++ b/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Threading;
diff --git a/Emby.Server.Implementations/Data/ManagedConnection.cs b/Emby.Server.Implementations/Data/ManagedConnection.cs
index 5c094ddd2..2c2f19cd3 100644
--- a/Emby.Server.Implementations/Data/ManagedConnection.cs
+++ b/Emby.Server.Implementations/Data/ManagedConnection.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs b/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs
index d474f1c6b..8087419ce 100644
--- a/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/Data/SqliteExtensions.cs b/Emby.Server.Implementations/Data/SqliteExtensions.cs
index c87793072..55c24ccc0 100644
--- a/Emby.Server.Implementations/Data/SqliteExtensions.cs
+++ b/Emby.Server.Implementations/Data/SqliteExtensions.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs
index 22955850a..f6c37e4e5 100644
--- a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/Data/SqliteUserRepository.cs b/Emby.Server.Implementations/Data/SqliteUserRepository.cs
index a042320c9..c82c93ffc 100644
--- a/Emby.Server.Implementations/Data/SqliteUserRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteUserRepository.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/Devices/DeviceId.cs b/Emby.Server.Implementations/Devices/DeviceId.cs
index f0d43e665..ff75efa59 100644
--- a/Emby.Server.Implementations/Devices/DeviceId.cs
+++ b/Emby.Server.Implementations/Devices/DeviceId.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Globalization;
diff --git a/Emby.Server.Implementations/Devices/DeviceManager.cs b/Emby.Server.Implementations/Devices/DeviceManager.cs
index 2393f1f45..ef7317050 100644
--- a/Emby.Server.Implementations/Devices/DeviceManager.cs
+++ b/Emby.Server.Implementations/Devices/DeviceManager.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/Diagnostics/CommonProcess.cs b/Emby.Server.Implementations/Diagnostics/CommonProcess.cs
index bfa49ac5f..f8b754151 100644
--- a/Emby.Server.Implementations/Diagnostics/CommonProcess.cs
+++ b/Emby.Server.Implementations/Diagnostics/CommonProcess.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Diagnostics;
diff --git a/Emby.Server.Implementations/Diagnostics/ProcessFactory.cs b/Emby.Server.Implementations/Diagnostics/ProcessFactory.cs
index 02ad3c1a8..219f73c78 100644
--- a/Emby.Server.Implementations/Diagnostics/ProcessFactory.cs
+++ b/Emby.Server.Implementations/Diagnostics/ProcessFactory.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using MediaBrowser.Model.Diagnostics;
diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs
index 3d622b3fc..fcf0360c7 100644
--- a/Emby.Server.Implementations/Dto/DtoService.cs
+++ b/Emby.Server.Implementations/Dto/DtoService.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
index 7ae6f38a1..03cbe00b7 100644
--- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj
+++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
@@ -29,9 +29,9 @@
<PackageReference Include="Microsoft.AspNetCore.ResponseCompression" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.2.1" />
- <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.0.1" />
- <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.0.1" />
- <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.0.1" />
+ <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.0" />
+ <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.1.0" />
+ <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.0" />
<PackageReference Include="Mono.Nat" Version="2.0.0" />
<PackageReference Include="ServiceStack.Text.Core" Version="5.7.0" />
<PackageReference Include="sharpcompress" Version="0.24.0" />
@@ -51,10 +51,10 @@
<!-- Code Analyzers-->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
- <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.7" />
- <PackageReference Include="SerilogAnalyzer" Version="0.15.0" />
- <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" />
- <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" />
+ <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8" PrivateAssets="All" />
+ <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
+ <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" />
+ <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
</ItemGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
diff --git a/Emby.Server.Implementations/EntryPoints/AutomaticRestartEntryPoint.cs b/Emby.Server.Implementations/EntryPoints/AutomaticRestartEntryPoint.cs
index d69b0909d..a6eb1152f 100644
--- a/Emby.Server.Implementations/EntryPoints/AutomaticRestartEntryPoint.cs
+++ b/Emby.Server.Implementations/EntryPoints/AutomaticRestartEntryPoint.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Linq;
diff --git a/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs b/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
index e290c62e1..4e4ef3be0 100644
--- a/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
+++ b/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
index 5f938e59a..f85d52dbc 100644
--- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs b/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs
index dbb3503c4..e0aa18e89 100644
--- a/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Linq;
diff --git a/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs
index e431da148..3e22080fc 100644
--- a/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/HttpServer/FileWriter.cs b/Emby.Server.Implementations/HttpServer/FileWriter.cs
index c1c8c3eb3..1795651fd 100644
--- a/Emby.Server.Implementations/HttpServer/FileWriter.cs
+++ b/Emby.Server.Implementations/HttpServer/FileWriter.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
index 2aefc9fe5..b0126f7fa 100644
--- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
+++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Collections.Generic;
@@ -218,7 +219,6 @@ namespace Emby.Server.Implementations.HttpServer
case FileNotFoundException _:
case ResourceNotFoundException _: return 404;
case MethodNotAllowedException _: return 405;
- case RemoteServiceUnavailableException _: return 502;
default: return 500;
}
}
diff --git a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
index a62b4e7af..cefcaa835 100644
--- a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
+++ b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/HttpServer/IHttpListener.cs b/Emby.Server.Implementations/HttpServer/IHttpListener.cs
index 501593725..1c3496e5d 100644
--- a/Emby.Server.Implementations/HttpServer/IHttpListener.cs
+++ b/Emby.Server.Implementations/HttpServer/IHttpListener.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Threading;
diff --git a/Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs b/Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs
index 8b9028f6b..7cb113a58 100644
--- a/Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs
+++ b/Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/HttpServer/Security/AuthService.cs b/Emby.Server.Implementations/HttpServer/Security/AuthService.cs
index 58421aaf1..03b5b748d 100644
--- a/Emby.Server.Implementations/HttpServer/Security/AuthService.cs
+++ b/Emby.Server.Implementations/HttpServer/Security/AuthService.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Linq;
diff --git a/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs b/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs
index 129faeaab..e8884bca0 100644
--- a/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs
+++ b/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs b/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs
index 166952c64..a6a0f5b03 100644
--- a/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs
+++ b/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using MediaBrowser.Controller.Entities;
diff --git a/Emby.Server.Implementations/IO/ExtendedFileSystemInfo.cs b/Emby.Server.Implementations/IO/ExtendedFileSystemInfo.cs
index 3150f3367..5be144452 100644
--- a/Emby.Server.Implementations/IO/ExtendedFileSystemInfo.cs
+++ b/Emby.Server.Implementations/IO/ExtendedFileSystemInfo.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
namespace Emby.Server.Implementations.IO
{
diff --git a/Emby.Server.Implementations/IO/FileRefresher.cs b/Emby.Server.Implementations/IO/FileRefresher.cs
index 4b5b11f01..cf92ddbcd 100644
--- a/Emby.Server.Implementations/IO/FileRefresher.cs
+++ b/Emby.Server.Implementations/IO/FileRefresher.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/IO/LibraryMonitor.cs b/Emby.Server.Implementations/IO/LibraryMonitor.cs
index b1fb8cc63..7777efc3b 100644
--- a/Emby.Server.Implementations/IO/LibraryMonitor.cs
+++ b/Emby.Server.Implementations/IO/LibraryMonitor.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Collections.Concurrent;
diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs
index 9568f62df..e27081c45 100644
--- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs
+++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/IO/MbLinkShortcutHandler.cs b/Emby.Server.Implementations/IO/MbLinkShortcutHandler.cs
index e6696b8c4..574b63ae6 100644
--- a/Emby.Server.Implementations/IO/MbLinkShortcutHandler.cs
+++ b/Emby.Server.Implementations/IO/MbLinkShortcutHandler.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.IO;
diff --git a/Emby.Server.Implementations/IO/StreamHelper.cs b/Emby.Server.Implementations/IO/StreamHelper.cs
index 40b397edc..c99018e40 100644
--- a/Emby.Server.Implementations/IO/StreamHelper.cs
+++ b/Emby.Server.Implementations/IO/StreamHelper.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Buffers;
diff --git a/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs b/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs
index fd50f156a..acf3a3b23 100644
--- a/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs
+++ b/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs b/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs
index 94f60ea62..ab036eca7 100644
--- a/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs
+++ b/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs
@@ -70,9 +70,9 @@ namespace Emby.Server.Implementations.Library
byte[] calculatedHash = _cryptographyProvider.ComputeHash(
readyHash.Id,
passwordbytes,
- readyHash.Salt);
+ readyHash.Salt.ToArray());
- if (calculatedHash.SequenceEqual(readyHash.Hash))
+ if (readyHash.Hash.SequenceEqual(calculatedHash))
{
success = true;
}
@@ -148,17 +148,18 @@ namespace Emby.Server.Implementations.Library
// TODO: make use of iterations parameter?
PasswordHash passwordHash = PasswordHash.Parse(user.Password);
+ var salt = passwordHash.Salt.ToArray();
return new PasswordHash(
passwordHash.Id,
_cryptographyProvider.ComputeHash(
passwordHash.Id,
Encoding.UTF8.GetBytes(str),
- passwordHash.Salt),
- passwordHash.Salt,
+ salt),
+ salt,
passwordHash.Parameters.ToDictionary(x => x.Key, y => y.Value)).ToString();
}
- public byte[] GetHashed(User user, string str)
+ public ReadOnlySpan<byte> GetHashed(User user, string str)
{
if (string.IsNullOrEmpty(user.Password))
{
@@ -170,7 +171,7 @@ namespace Emby.Server.Implementations.Library
return _cryptographyProvider.ComputeHash(
passwordHash.Id,
Encoding.UTF8.GetBytes(str),
- passwordHash.Salt);
+ passwordHash.Salt.ToArray());
}
}
}
diff --git a/Emby.Server.Implementations/Library/ExclusiveLiveStream.cs b/Emby.Server.Implementations/Library/ExclusiveLiveStream.cs
index 9a7186898..3eb64c29c 100644
--- a/Emby.Server.Implementations/Library/ExclusiveLiveStream.cs
+++ b/Emby.Server.Implementations/Library/ExclusiveLiveStream.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Globalization;
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index cee51479e..ae3cdece9 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Collections.Concurrent;
@@ -2558,7 +2559,7 @@ namespace Emby.Server.Implementations.Library
if (currentVideo != null)
{
- files.AddRange(currentVideo.Extras.Where(i => string.Equals(i.ExtraType, "trailer", StringComparison.OrdinalIgnoreCase)).Select(i => _fileSystem.GetFileInfo(i.Path)));
+ files.AddRange(currentVideo.Extras.Where(i => i.ExtraType == ExtraType.Trailer).Select(i => _fileSystem.GetFileInfo(i.Path)));
}
var resolvers = new IItemResolver[]
@@ -2608,7 +2609,7 @@ namespace Emby.Server.Implementations.Library
if (currentVideo != null)
{
- files.AddRange(currentVideo.Extras.Where(i => !string.Equals(i.ExtraType, "trailer", StringComparison.OrdinalIgnoreCase)).Select(i => _fileSystem.GetFileInfo(i.Path)));
+ files.AddRange(currentVideo.Extras.Where(i => i.ExtraType != ExtraType.Trailer).Select(i => _fileSystem.GetFileInfo(i.Path)));
}
return ResolvePaths(files, directoryService, null, new LibraryOptions(), null)
@@ -2712,7 +2713,7 @@ namespace Emby.Server.Implementations.Library
if (!string.Equals(newPath, path, StringComparison.Ordinal))
{
- if (to.IndexOf('/') != -1)
+ if (to.IndexOf('/', StringComparison.Ordinal) != -1)
{
newPath = newPath.Replace('\\', '/');
}
@@ -2733,30 +2734,7 @@ namespace Emby.Server.Implementations.Library
var result = resolver.GetExtraInfo(item.Path);
- if (string.Equals(result.ExtraType, "deletedscene", StringComparison.OrdinalIgnoreCase))
- {
- item.ExtraType = ExtraType.DeletedScene;
- }
- else if (string.Equals(result.ExtraType, "behindthescenes", StringComparison.OrdinalIgnoreCase))
- {
- item.ExtraType = ExtraType.BehindTheScenes;
- }
- else if (string.Equals(result.ExtraType, "interview", StringComparison.OrdinalIgnoreCase))
- {
- item.ExtraType = ExtraType.Interview;
- }
- else if (string.Equals(result.ExtraType, "scene", StringComparison.OrdinalIgnoreCase))
- {
- item.ExtraType = ExtraType.Scene;
- }
- else if (string.Equals(result.ExtraType, "sample", StringComparison.OrdinalIgnoreCase))
- {
- item.ExtraType = ExtraType.Sample;
- }
- else
- {
- item.ExtraType = ExtraType.Clip;
- }
+ item.ExtraType = result.ExtraType;
}
public List<PersonInfo> GetPeople(InternalPeopleQuery query)
diff --git a/Emby.Server.Implementations/Library/LiveStreamHelper.cs b/Emby.Server.Implementations/Library/LiveStreamHelper.cs
index ed7d8aa40..f28f4a538 100644
--- a/Emby.Server.Implementations/Library/LiveStreamHelper.cs
+++ b/Emby.Server.Implementations/Library/LiveStreamHelper.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs
index 70d5bd9f4..e310065b2 100644
--- a/Emby.Server.Implementations/Library/MediaSourceManager.cs
+++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/Library/MediaStreamSelector.cs b/Emby.Server.Implementations/Library/MediaStreamSelector.cs
index 6b9f4d052..1652ad974 100644
--- a/Emby.Server.Implementations/Library/MediaStreamSelector.cs
+++ b/Emby.Server.Implementations/Library/MediaStreamSelector.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/Library/MusicManager.cs b/Emby.Server.Implementations/Library/MusicManager.cs
index 1ec578371..29af6670b 100644
--- a/Emby.Server.Implementations/Library/MusicManager.cs
+++ b/Emby.Server.Implementations/Library/MusicManager.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
index 9d4bd9e59..7e3b27a12 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
index c4bb861b8..43302bb3f 100644
--- a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.IO;
diff --git a/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs
index 0b93ebeb8..1e2e0704c 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.IO;
diff --git a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs
index a71ae8250..e1eb23652 100644
--- a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs
index a68562fc2..5e672f221 100644
--- a/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.IO;
diff --git a/Emby.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs b/Emby.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs
index 1030ed39d..eca60b133 100644
--- a/Emby.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.IO;
diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
index 7cc9eabc8..e39d85bc9 100644
--- a/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs
index 62268fce9..6404d6476 100644
--- a/Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
diff --git a/Emby.Server.Implementations/Library/SearchEngine.cs b/Emby.Server.Implementations/Library/SearchEngine.cs
index 11d6c737a..76ae14720 100644
--- a/Emby.Server.Implementations/Library/SearchEngine.cs
+++ b/Emby.Server.Implementations/Library/SearchEngine.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/Library/UserDataManager.cs b/Emby.Server.Implementations/Library/UserDataManager.cs
index 071681b08..f1fb35d9a 100644
--- a/Emby.Server.Implementations/Library/UserDataManager.cs
+++ b/Emby.Server.Implementations/Library/UserDataManager.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Collections.Concurrent;
diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs
index 1b9c317d8..656eeb145 100644
--- a/Emby.Server.Implementations/Library/UserManager.cs
+++ b/Emby.Server.Implementations/Library/UserManager.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Collections.Concurrent;
@@ -485,7 +486,7 @@ namespace Emby.Server.Implementations.Library
var hash = _cryptoProvider.ComputeHash(
passwordHash.Id,
Encoding.UTF8.GetBytes(password),
- passwordHash.Salt);
+ passwordHash.Salt.ToArray());
success = passwordHash.Hash.SequenceEqual(hash);
}
diff --git a/Emby.Server.Implementations/Library/UserViewManager.cs b/Emby.Server.Implementations/Library/UserViewManager.cs
index 322819b05..935deb71c 100644
--- a/Emby.Server.Implementations/Library/UserViewManager.cs
+++ b/Emby.Server.Implementations/Library/UserViewManager.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/Localization/Core/fr.json b/Emby.Server.Implementations/Localization/Core/fr.json
index e1dce82ff..6b25e3df0 100644
--- a/Emby.Server.Implementations/Localization/Core/fr.json
+++ b/Emby.Server.Implementations/Localization/Core/fr.json
@@ -5,7 +5,7 @@
"Artists": "Artistes",
"AuthenticationSucceededWithUserName": "{0} s'est authentifié avec succès",
"Books": "Livres",
- "CameraImageUploadedFrom": "Une nouvelle image de caméra a été chargée depuis {0}",
+ "CameraImageUploadedFrom": "Une nouvelle photo a été chargée depuis {0}",
"Channels": "Chaînes",
"ChapterNameValue": "Chapitre {0}",
"Collections": "Collections",
diff --git a/Emby.Server.Implementations/Localization/Core/ms.json b/Emby.Server.Implementations/Localization/Core/ms.json
index c10fbe58f..b91c98ca0 100644
--- a/Emby.Server.Implementations/Localization/Core/ms.json
+++ b/Emby.Server.Implementations/Localization/Core/ms.json
@@ -1,23 +1,23 @@
{
"Albums": "Album-album",
"AppDeviceValues": "App: {0}, Device: {1}",
- "Application": "Application",
+ "Application": "Aplikasi",
"Artists": "Artis-artis",
"AuthenticationSucceededWithUserName": "{0} successfully authenticated",
"Books": "Buku-buku",
"CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
- "Channels": "Channels",
+ "Channels": "Saluran",
"ChapterNameValue": "Chapter {0}",
- "Collections": "Collections",
+ "Collections": "Koleksi",
"DeviceOfflineWithName": "{0} has disconnected",
"DeviceOnlineWithName": "{0} is connected",
- "FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
+ "FailedLoginAttemptWithUserName": "Cubaan log masuk gagal dari {0}",
"Favorites": "Favorites",
"Folders": "Folders",
- "Genres": "Genres",
+ "Genres": "Genre-genre",
"HeaderAlbumArtists": "Album Artists",
- "HeaderCameraUploads": "Camera Uploads",
- "HeaderContinueWatching": "Continue Watching",
+ "HeaderCameraUploads": "Muatnaik Kamera",
+ "HeaderContinueWatching": "Terus Menonton",
"HeaderFavoriteAlbums": "Favorite Albums",
"HeaderFavoriteArtists": "Favorite Artists",
"HeaderFavoriteEpisodes": "Favorite Episodes",
@@ -39,8 +39,8 @@
"MessageServerConfigurationUpdated": "Server configuration has been updated",
"MixedContent": "Mixed content",
"Movies": "Movies",
- "Music": "Music",
- "MusicVideos": "Music videos",
+ "Music": "Muzik",
+ "MusicVideos": "Video muzik",
"NameInstallFailed": "{0} installation failed",
"NameSeasonNumber": "Season {0}",
"NameSeasonUnknown": "Season Unknown",
@@ -68,8 +68,8 @@
"PluginUninstalledWithName": "{0} was uninstalled",
"PluginUpdatedWithName": "{0} was updated",
"ProviderValue": "Provider: {0}",
- "ScheduledTaskFailedWithName": "{0} failed",
- "ScheduledTaskStartedWithName": "{0} started",
+ "ScheduledTaskFailedWithName": "{0} gagal",
+ "ScheduledTaskStartedWithName": "{0} bermula",
"ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
"Shows": "Series",
"Songs": "Songs",
@@ -78,7 +78,7 @@
"SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
"SubtitlesDownloadedForItem": "Subtitles downloaded for {0}",
"Sync": "Sync",
- "System": "System",
+ "System": "Sistem",
"TvShows": "TV Shows",
"User": "User",
"UserCreatedWithName": "User {0} has been created",
@@ -92,6 +92,6 @@
"UserStartedPlayingItemWithValues": "{0} is playing {1} on {2}",
"UserStoppedPlayingItemWithValues": "{0} has finished playing {1} on {2}",
"ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
- "ValueSpecialEpisodeName": "Special - {0}",
- "VersionNumber": "Version {0}"
+ "ValueSpecialEpisodeName": "Khas - {0}",
+ "VersionNumber": "Versi {0}"
}
diff --git a/Emby.Server.Implementations/Localization/Core/sr.json b/Emby.Server.Implementations/Localization/Core/sr.json
new file mode 100644
index 000000000..0967ef424
--- /dev/null
+++ b/Emby.Server.Implementations/Localization/Core/sr.json
@@ -0,0 +1 @@
+{}
diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj
index 73ffaa53d..38cdb0998 100644
--- a/Jellyfin.Api/Jellyfin.Api.csproj
+++ b/Jellyfin.Api/Jellyfin.Api.csproj
@@ -19,7 +19,7 @@
<!-- Code Analyzers-->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
- <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.7" PrivateAssets="All" />
+ <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8" PrivateAssets="All" />
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" />
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj
index a41112191..166e22909 100644
--- a/Jellyfin.Server/Jellyfin.Server.csproj
+++ b/Jellyfin.Server/Jellyfin.Server.csproj
@@ -27,10 +27,10 @@
<!-- Code Analyzers-->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
- <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.7" />
- <PackageReference Include="SerilogAnalyzer" Version="0.15.0" />
- <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" />
- <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" />
+ <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8" PrivateAssets="All" />
+ <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
+ <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" />
+ <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
</ItemGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
@@ -39,8 +39,8 @@
<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.6.0" />
- <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.0.1" />
- <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.0.1" />
+ <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.1.0" />
+ <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.0" />
<PackageReference Include="Serilog.AspNetCore" Version="3.2.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="3.1.0" />
<PackageReference Include="Serilog.Sinks.Async" Version="1.4.0" />
diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs
index 6d5a5b783..4b9bb8010 100644
--- a/MediaBrowser.Api/Playback/MediaInfoService.cs
+++ b/MediaBrowser.Api/Playback/MediaInfoService.cs
@@ -1,6 +1,13 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1402
+#pragma warning disable SA1600
+#pragma warning disable SA1649
+
using System;
+using System.Buffers;
using System.Collections.Generic;
using System.Globalization;
+using System.Text.Json;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -54,7 +61,7 @@ namespace MediaBrowser.Api.Playback
public class GetBitrateTestBytes
{
[ApiMember(Name = "Size", Description = "Size", IsRequired = true, DataType = "int", ParameterType = "query", Verb = "GET")]
- public long Size { get; set; }
+ public int Size { get; set; }
public GetBitrateTestBytes()
{
@@ -72,7 +79,6 @@ namespace MediaBrowser.Api.Playback
private readonly INetworkManager _networkManager;
private readonly IMediaEncoder _mediaEncoder;
private readonly IUserManager _userManager;
- private readonly IJsonSerializer _json;
private readonly IAuthorizationContext _authContext;
public MediaInfoService(
@@ -85,7 +91,6 @@ namespace MediaBrowser.Api.Playback
INetworkManager networkManager,
IMediaEncoder mediaEncoder,
IUserManager userManager,
- IJsonSerializer json,
IAuthorizationContext authContext)
: base(logger, serverConfigurationManager, httpResultFactory)
{
@@ -95,20 +100,35 @@ namespace MediaBrowser.Api.Playback
_networkManager = networkManager;
_mediaEncoder = mediaEncoder;
_userManager = userManager;
- _json = json;
_authContext = authContext;
}
public object Get(GetBitrateTestBytes request)
{
- var bytes = new byte[request.Size];
+ const int MaxSize = 10_000_000;
+
+ var size = request.Size;
+
+ if (size <= 0)
+ {
+ throw new ArgumentException($"The requested size ({size}) is equal to or smaller than 0.", nameof(request));
+ }
- for (var i = 0; i < bytes.Length; i++)
+ if (size > MaxSize)
{
- bytes[i] = 0;
+ throw new ArgumentException($"The requested size ({size}) is larger than the max allowed value ({MaxSize}).", nameof(request));
}
- return ResultFactory.GetResult(null, bytes, "application/octet-stream");
+ byte[] buffer = ArrayPool<byte>.Shared.Rent(size);
+ try
+ {
+ new Random().NextBytes(buffer);
+ return ResultFactory.GetResult(null, buffer, "application/octet-stream");
+ }
+ finally
+ {
+ ArrayPool<byte>.Shared.Return(buffer);
+ }
}
public async Task<object> Get(GetPlaybackInfo request)
@@ -166,8 +186,7 @@ namespace MediaBrowser.Api.Playback
public void Post(CloseMediaSource request)
{
- var task = _mediaSourceManager.CloseLiveStream(request.LiveStreamId);
- Task.WaitAll(task);
+ _mediaSourceManager.CloseLiveStream(request.LiveStreamId).GetAwaiter().GetResult();
}
public async Task<PlaybackInfoResponse> GetPlaybackInfo(GetPostedPlaybackInfo request)
@@ -176,7 +195,7 @@ namespace MediaBrowser.Api.Playback
var profile = request.DeviceProfile;
- //Logger.LogInformation("GetPostedPlaybackInfo profile: {profile}", _json.SerializeToString(profile));
+ Logger.LogInformation("GetPostedPlaybackInfo profile: {@Profile}", profile);
if (profile == null)
{
@@ -215,9 +234,7 @@ namespace MediaBrowser.Api.Playback
StartTimeTicks = request.StartTimeTicks,
SubtitleStreamIndex = request.SubtitleStreamIndex,
UserId = request.UserId,
- OpenToken = mediaSource.OpenToken,
- //EnableMediaProbe = request.EnableMediaProbe
-
+ OpenToken = mediaSource.OpenToken
}).ConfigureAwait(false);
info.MediaSources = new MediaSourceInfo[] { openStreamResult.MediaSource };
@@ -251,9 +268,8 @@ namespace MediaBrowser.Api.Playback
{
// Since we're going to be setting properties on MediaSourceInfos that come out of _mediaSourceManager, we should clone it
// Should we move this directly into MediaSourceManager?
-
- var json = _json.SerializeToString(obj);
- return _json.DeserializeFromString<T>(json);
+ var json = JsonSerializer.SerializeToUtf8Bytes(obj);
+ return JsonSerializer.Deserialize<T>(json);
}
private async Task<PlaybackInfoResponse> GetPlaybackInfo(Guid id, Guid userId, string[] supportedLiveMediaTypes, string mediaSourceId = null, string liveStreamId = null)
@@ -294,7 +310,7 @@ namespace MediaBrowser.Api.Playback
result.MediaSources = new MediaSourceInfo[] { mediaSource };
}
- if (result.MediaSources.Length == 0)
+ if (result.MediaSources.Count == 0)
{
if (!result.ErrorCode.HasValue)
{
@@ -311,7 +327,8 @@ namespace MediaBrowser.Api.Playback
return result;
}
- private void SetDeviceSpecificData(Guid itemId,
+ private void SetDeviceSpecificData(
+ Guid itemId,
PlaybackInfoResponse result,
DeviceProfile profile,
AuthorizationInfo auth,
@@ -339,7 +356,8 @@ namespace MediaBrowser.Api.Playback
SortMediaSources(result, maxBitrate);
}
- private void SetDeviceSpecificData(BaseItem item,
+ private void SetDeviceSpecificData(
+ BaseItem item,
MediaSourceInfo mediaSource,
DeviceProfile profile,
AuthorizationInfo auth,
@@ -383,10 +401,12 @@ namespace MediaBrowser.Api.Playback
{
mediaSource.SupportsDirectPlay = false;
}
+
if (!enableDirectStream)
{
mediaSource.SupportsDirectStream = false;
}
+
if (!enableTranscoding)
{
mediaSource.SupportsTranscoding = false;
@@ -434,9 +454,9 @@ namespace MediaBrowser.Api.Playback
}
// The MediaSource supports direct stream, now test to see if the client supports it
- var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ?
- streamBuilder.BuildAudioItem(options) :
- streamBuilder.BuildVideoItem(options);
+ var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase)
+ ? streamBuilder.BuildAudioItem(options)
+ : streamBuilder.BuildVideoItem(options);
if (streamInfo == null || !streamInfo.IsDirectStream)
{
@@ -473,9 +493,9 @@ namespace MediaBrowser.Api.Playback
}
// The MediaSource supports direct stream, now test to see if the client supports it
- var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ?
- streamBuilder.BuildAudioItem(options) :
- streamBuilder.BuildVideoItem(options);
+ var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase)
+ ? streamBuilder.BuildAudioItem(options)
+ : streamBuilder.BuildVideoItem(options);
if (streamInfo == null || !streamInfo.IsDirectStream)
{
@@ -493,9 +513,9 @@ namespace MediaBrowser.Api.Playback
options.MaxBitrate = GetMaxBitrate(maxBitrate, user);
// The MediaSource supports direct stream, now test to see if the client supports it
- var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ?
- streamBuilder.BuildAudioItem(options) :
- streamBuilder.BuildVideoItem(options);
+ var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase)
+ ? streamBuilder.BuildAudioItem(options)
+ : streamBuilder.BuildVideoItem(options);
if (streamInfo != null)
{
@@ -510,10 +530,12 @@ namespace MediaBrowser.Api.Playback
{
mediaSource.TranscodingUrl += "&allowVideoStreamCopy=false";
}
+
if (!allowAudioStreamCopy)
{
mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false";
}
+
mediaSource.TranscodingContainer = streamInfo.Container;
mediaSource.TranscodingSubProtocol = streamInfo.SubProtocol;
}
@@ -609,14 +631,11 @@ namespace MediaBrowser.Api.Playback
}).ThenBy(i =>
{
- switch (i.Protocol)
+ return i.Protocol switch
{
- case MediaProtocol.File:
- return 0;
- default:
- return 1;
- }
-
+ MediaProtocol.File => 0,
+ _ => 1,
+ };
}).ThenBy(i =>
{
if (maxBitrate.HasValue)
diff --git a/MediaBrowser.Api/Playback/UniversalAudioService.cs b/MediaBrowser.Api/Playback/UniversalAudioService.cs
index 9cba9df13..bcac5093e 100644
--- a/MediaBrowser.Api/Playback/UniversalAudioService.cs
+++ b/MediaBrowser.Api/Playback/UniversalAudioService.cs
@@ -74,7 +74,6 @@ namespace MediaBrowser.Api.Playback
[Authenticated]
public class UniversalAudioService : BaseApiService
{
- private readonly ILoggerFactory _loggerFactory;
private readonly EncodingHelper _encodingHelper;
public UniversalAudioService(
@@ -243,7 +242,6 @@ namespace MediaBrowser.Api.Playback
NetworkManager,
MediaEncoder,
UserManager,
- JsonSerializer,
AuthorizationContext)
{
Request = Request
diff --git a/MediaBrowser.Common/Configuration/ConfigurationUpdateEventArgs.cs b/MediaBrowser.Common/Configuration/ConfigurationUpdateEventArgs.cs
index 344aecf53..828415c18 100644
--- a/MediaBrowser.Common/Configuration/ConfigurationUpdateEventArgs.cs
+++ b/MediaBrowser.Common/Configuration/ConfigurationUpdateEventArgs.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
diff --git a/MediaBrowser.Common/Configuration/IConfigurationFactory.cs b/MediaBrowser.Common/Configuration/IConfigurationFactory.cs
index 4c4060096..9b4ed772d 100644
--- a/MediaBrowser.Common/Configuration/IConfigurationFactory.cs
+++ b/MediaBrowser.Common/Configuration/IConfigurationFactory.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/MediaBrowser.Common/Configuration/IConfigurationManager.cs b/MediaBrowser.Common/Configuration/IConfigurationManager.cs
index caf2edd83..7773596af 100644
--- a/MediaBrowser.Common/Configuration/IConfigurationManager.cs
+++ b/MediaBrowser.Common/Configuration/IConfigurationManager.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/MediaBrowser.Common/Cryptography/PasswordHash.cs b/MediaBrowser.Common/Cryptography/PasswordHash.cs
index 19b8be47a..3477c1c04 100644
--- a/MediaBrowser.Common/Cryptography/PasswordHash.cs
+++ b/MediaBrowser.Common/Cryptography/PasswordHash.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Collections.Generic;
@@ -15,24 +16,24 @@ namespace MediaBrowser.Common.Cryptography
public class PasswordHash
{
private readonly Dictionary<string, string> _parameters;
+ private readonly byte[] _salt;
+ private readonly byte[] _hash;
public PasswordHash(string id, byte[] hash)
: this(id, hash, Array.Empty<byte>())
{
-
}
public PasswordHash(string id, byte[] hash, byte[] salt)
: this(id, hash, salt, new Dictionary<string, string>())
{
-
}
public PasswordHash(string id, byte[] hash, byte[] salt, Dictionary<string, string> parameters)
{
Id = id;
- Hash = hash;
- Salt = salt;
+ _hash = hash;
+ _salt = salt;
_parameters = parameters;
}
@@ -45,25 +46,24 @@ namespace MediaBrowser.Common.Cryptography
/// <summary>
/// Gets the additional parameters used by the hash function.
/// </summary>
- /// <value></value>
public IReadOnlyDictionary<string, string> Parameters => _parameters;
/// <summary>
/// Gets the salt used for hashing the password.
/// </summary>
/// <value>Returns the salt used for hashing the password.</value>
- public byte[] Salt { get; }
+ public ReadOnlySpan<byte> Salt => _salt;
/// <summary>
/// Gets the hashed password.
/// </summary>
/// <value>Return the hashed password.</value>
- public byte[] Hash { get; }
+ public ReadOnlySpan<byte> Hash => _hash;
public static PasswordHash Parse(string hashString)
{
- string[] splitted = hashString.Split('$');
// The string should at least contain the hash function and the hash itself
+ string[] splitted = hashString.Split('$');
if (splitted.Length < 3)
{
throw new ArgumentException("String doesn't contain enough segments", nameof(hashString));
@@ -77,7 +77,7 @@ namespace MediaBrowser.Common.Cryptography
// Optional parameters
Dictionary<string, string> parameters = new Dictionary<string, string>();
- if (splitted[index].IndexOf('=') != -1)
+ if (splitted[index].IndexOf('=', StringComparison.Ordinal) != -1)
{
foreach (string paramset in splitted[index++].Split(','))
{
@@ -98,6 +98,7 @@ namespace MediaBrowser.Common.Cryptography
byte[] hash;
byte[] salt;
+
// Check if the string also contains a salt
if (splitted.Length - index == 2)
{
@@ -141,14 +142,14 @@ namespace MediaBrowser.Common.Cryptography
.Append(Id);
SerializeParameters(str);
- if (Salt.Length != 0)
+ if (_salt.Length != 0)
{
str.Append('$')
- .Append(Hex.Encode(Salt, false));
+ .Append(Hex.Encode(_salt, false));
}
return str.Append('$')
- .Append(Hex.Encode(Hash, false)).ToString();
+ .Append(Hex.Encode(_hash, false)).ToString();
}
}
}
diff --git a/MediaBrowser.Common/Events/EventHelper.cs b/MediaBrowser.Common/Events/EventHelper.cs
index b67315df6..c9d3226ac 100644
--- a/MediaBrowser.Common/Events/EventHelper.cs
+++ b/MediaBrowser.Common/Events/EventHelper.cs
@@ -1,15 +1,13 @@
-#pragma warning disable CS1591
-
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.Common.Events
{
- // TODO: @bond Remove
/// <summary>
- /// Class EventHelper
+ /// Class EventHelper.
/// </summary>
+ // TODO: @bond Remove
public static class EventHelper
{
/// <summary>
@@ -40,7 +38,7 @@ namespace MediaBrowser.Common.Events
/// <summary>
/// Queues the event.
/// </summary>
- /// <typeparam name="T"></typeparam>
+ /// <typeparam name="T">Argument type for the <c>handler</c>.</typeparam>
/// <param name="handler">The handler.</param>
/// <param name="sender">The sender.</param>
/// <param name="args">The args.</param>
diff --git a/MediaBrowser.Common/Extensions/BaseExtensions.cs b/MediaBrowser.Common/Extensions/BaseExtensions.cs
index 33473c2be..08964420e 100644
--- a/MediaBrowser.Common/Extensions/BaseExtensions.cs
+++ b/MediaBrowser.Common/Extensions/BaseExtensions.cs
@@ -1,12 +1,12 @@
using System;
+using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
-using System.Security.Cryptography;
namespace MediaBrowser.Common.Extensions
{
/// <summary>
- /// Class BaseExtensions
+ /// Class BaseExtensions.
/// </summary>
public static class BaseExtensions
{
@@ -30,10 +30,13 @@ namespace MediaBrowser.Common.Extensions
/// <returns><see cref="Guid" />.</returns>
public static Guid GetMD5(this string str)
{
+#pragma warning disable CA5351
using (var provider = MD5.Create())
{
return new Guid(provider.ComputeHash(Encoding.Unicode.GetBytes(str)));
}
+
+#pragma warning restore CA5351
}
}
}
diff --git a/MediaBrowser.Common/Extensions/CopyToExtensions.cs b/MediaBrowser.Common/Extensions/CopyToExtensions.cs
index 78a73f07e..2ecbc6539 100644
--- a/MediaBrowser.Common/Extensions/CopyToExtensions.cs
+++ b/MediaBrowser.Common/Extensions/CopyToExtensions.cs
@@ -5,7 +5,7 @@ namespace MediaBrowser.Common.Extensions
/// <summary>
/// Provides <c>CopyTo</c> extensions methods for <see cref="IReadOnlyList{T}" />.
/// </summary>
- public static class CollectionExtensions
+ public static class CopyToExtensions
{
/// <summary>
/// Copies all the elements of the current collection to the specified list
@@ -14,7 +14,7 @@ namespace MediaBrowser.Common.Extensions
/// <param name="source">The current collection that is the source of the elements.</param>
/// <param name="destination">The list that is the destination of the elements copied from the current collection.</param>
/// <param name="index">A 32-bit integer that represents the index in <c>destination</c> at which copying begins.</param>
- /// <typeparam name="T"></typeparam>
+ /// <typeparam name="T">The type of the array.</typeparam>
public static void CopyTo<T>(this IReadOnlyList<T> source, IList<T> destination, int index = 0)
{
for (int i = 0; i < source.Count; i++)
diff --git a/MediaBrowser.Common/Extensions/MethodNotAllowedException.cs b/MediaBrowser.Common/Extensions/MethodNotAllowedException.cs
new file mode 100644
index 000000000..48e758ee4
--- /dev/null
+++ b/MediaBrowser.Common/Extensions/MethodNotAllowedException.cs
@@ -0,0 +1,26 @@
+using System;
+
+namespace MediaBrowser.Common.Extensions
+{
+ /// <summary>
+ /// Class MethodNotAllowedException.
+ /// </summary>
+ public class MethodNotAllowedException : Exception
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MethodNotAllowedException" /> class.
+ /// </summary>
+ public MethodNotAllowedException()
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MethodNotAllowedException" /> class.
+ /// </summary>
+ /// <param name="message">The message.</param>
+ public MethodNotAllowedException(string message)
+ : base(message)
+ {
+ }
+ }
+}
diff --git a/MediaBrowser.Common/Extensions/RateLimitExceededException.cs b/MediaBrowser.Common/Extensions/RateLimitExceededException.cs
new file mode 100644
index 000000000..4e5d4e9ca
--- /dev/null
+++ b/MediaBrowser.Common/Extensions/RateLimitExceededException.cs
@@ -0,0 +1,26 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
+using System;
+
+namespace MediaBrowser.Common.Extensions
+{
+ public class RateLimitExceededException : Exception
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="RateLimitExceededException" /> class.
+ /// </summary>
+ public RateLimitExceededException()
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="RateLimitExceededException" /> class.
+ /// </summary>
+ /// <param name="message">The message.</param>
+ public RateLimitExceededException(string message)
+ : base(message)
+ {
+ }
+ }
+}
diff --git a/MediaBrowser.Common/Extensions/ResourceNotFoundException.cs b/MediaBrowser.Common/Extensions/ResourceNotFoundException.cs
index 9b064a40d..22130c5a1 100644
--- a/MediaBrowser.Common/Extensions/ResourceNotFoundException.cs
+++ b/MediaBrowser.Common/Extensions/ResourceNotFoundException.cs
@@ -1,11 +1,9 @@
-#pragma warning disable CS1591
-
using System;
namespace MediaBrowser.Common.Extensions
{
/// <summary>
- /// Class ResourceNotFoundException
+ /// Class ResourceNotFoundException.
/// </summary>
public class ResourceNotFoundException : Exception
{
@@ -14,7 +12,6 @@ namespace MediaBrowser.Common.Extensions
/// </summary>
public ResourceNotFoundException()
{
-
}
/// <summary>
@@ -24,66 +21,6 @@ namespace MediaBrowser.Common.Extensions
public ResourceNotFoundException(string message)
: base(message)
{
-
- }
- }
-
- /// <summary>
- /// Class MethodNotAllowedException
- /// </summary>
- public class MethodNotAllowedException : Exception
- {
- /// <summary>
- /// Initializes a new instance of the <see cref="MethodNotAllowedException" /> class.
- /// </summary>
- public MethodNotAllowedException()
- {
-
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="MethodNotAllowedException" /> class.
- /// </summary>
- /// <param name="message">The message.</param>
- public MethodNotAllowedException(string message)
- : base(message)
- {
-
- }
- }
-
- public class RemoteServiceUnavailableException : Exception
- {
- public RemoteServiceUnavailableException()
- {
-
- }
-
- public RemoteServiceUnavailableException(string message)
- : base(message)
- {
-
- }
- }
-
- public class RateLimitExceededException : Exception
- {
- /// <summary>
- /// Initializes a new instance of the <see cref="RateLimitExceededException" /> class.
- /// </summary>
- public RateLimitExceededException()
- {
-
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="RateLimitExceededException" /> class.
- /// </summary>
- /// <param name="message">The message.</param>
- public RateLimitExceededException(string message)
- : base(message)
- {
-
}
}
}
diff --git a/MediaBrowser.Common/Hex.cs b/MediaBrowser.Common/Hex.cs
index b2d9aea3a..559109f74 100644
--- a/MediaBrowser.Common/Hex.cs
+++ b/MediaBrowser.Common/Hex.cs
@@ -14,11 +14,11 @@ namespace MediaBrowser.Common
internal const int LastHexSymbol = 0x66; // 102: f
/// <summary>
- /// Map from an ASCII char to its hex value shifted,
+ /// Gets a map from an ASCII char to its hex value shifted,
/// e.g. <c>b</c> -> 11. 0xFF means it's not a hex symbol.
/// </summary>
- /// <value></value>
- internal static ReadOnlySpan<byte> HexLookup => new byte[] {
+ internal static ReadOnlySpan<byte> HexLookup => new byte[]
+ {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
@@ -29,10 +29,10 @@ namespace MediaBrowser.Common
};
/// <summary>
- /// Encodes <c>bytes</c> as a hex string.
+ /// Encodes each element of the specified bytes as its hexadecimal string representation.
/// </summary>
- /// <param name="bytes"></param>
- /// <param name="lowercase"></param>
+ /// <param name="bytes">An array of bytes.</param>
+ /// <param name="lowercase"><c>true</c> to use lowercase hexadecimal characters; otherwise <c>false</c>.</param>
/// <returns><c>bytes</c> as a hex string.</returns>
public static string Encode(ReadOnlySpan<byte> bytes, bool lowercase = true)
{
diff --git a/MediaBrowser.Common/IApplicationHost.cs b/MediaBrowser.Common/IApplicationHost.cs
index 6668e98aa..68a24aaba 100644
--- a/MediaBrowser.Common/IApplicationHost.cs
+++ b/MediaBrowser.Common/IApplicationHost.cs
@@ -8,11 +8,16 @@ using Microsoft.Extensions.DependencyInjection;
namespace MediaBrowser.Common
{
/// <summary>
- /// An interface to be implemented by the applications hosting a kernel
+ /// An interface to be implemented by the applications hosting a kernel.
/// </summary>
public interface IApplicationHost
{
/// <summary>
+ /// Occurs when [has pending restart changed].
+ /// </summary>
+ event EventHandler HasPendingRestartChanged;
+
+ /// <summary>
/// Gets the name.
/// </summary>
/// <value>The name.</value>
@@ -25,13 +30,13 @@ namespace MediaBrowser.Common
string SystemId { get; }
/// <summary>
- /// Gets or sets a value indicating whether this instance has pending kernel reload.
+ /// Gets a value indicating whether this instance has pending kernel reload.
/// </summary>
/// <value><c>true</c> if this instance has pending kernel reload; otherwise, <c>false</c>.</value>
bool HasPendingRestart { get; }
/// <summary>
- /// Gets or sets a value indicating whether this instance is currently shutting down.
+ /// Gets a value indicating whether this instance is currently shutting down.
/// </summary>
/// <value><c>true</c> if this instance is shutting down; otherwise, <c>false</c>.</value>
bool IsShuttingDown { get; }
@@ -43,27 +48,12 @@ namespace MediaBrowser.Common
bool CanSelfRestart { get; }
/// <summary>
- /// Get the version class of the system.
+ /// Gets the version class of the system.
/// </summary>
/// <value><see cref="PackageVersionClass.Release" /> or <see cref="PackageVersionClass.Beta" />.</value>
PackageVersionClass SystemUpdateLevel { get; }
/// <summary>
- /// Occurs when [has pending restart changed].
- /// </summary>
- event EventHandler HasPendingRestartChanged;
-
- /// <summary>
- /// Notifies the pending restart.
- /// </summary>
- void NotifyPendingRestart();
-
- /// <summary>
- /// Restarts this instance.
- /// </summary>
- void Restart();
-
- /// <summary>
/// Gets the application version.
/// </summary>
/// <value>The application version.</value>
@@ -88,6 +78,22 @@ namespace MediaBrowser.Common
string ApplicationUserAgentAddress { get; }
/// <summary>
+ /// Gets the plugins.
+ /// </summary>
+ /// <value>The plugins.</value>
+ IReadOnlyList<IPlugin> Plugins { get; }
+
+ /// <summary>
+ /// Notifies the pending restart.
+ /// </summary>
+ void NotifyPendingRestart();
+
+ /// <summary>
+ /// Restarts this instance.
+ /// </summary>
+ void Restart();
+
+ /// <summary>
/// Gets the exports.
/// </summary>
/// <typeparam name="T">The type.</typeparam>
@@ -98,22 +104,17 @@ namespace MediaBrowser.Common
/// <summary>
/// Resolves this instance.
/// </summary>
- /// <typeparam name="T"></typeparam>
+ /// <typeparam name="T">The <c>Type</c>.</typeparam>
/// <returns>``0.</returns>
T Resolve<T>();
/// <summary>
/// Shuts down.
/// </summary>
+ /// <returns>A task.</returns>
Task Shutdown();
/// <summary>
- /// Gets the plugins.
- /// </summary>
- /// <value>The plugins.</value>
- IPlugin[] Plugins { get; }
-
- /// <summary>
/// Removes the plugin.
/// </summary>
/// <param name="plugin">The plugin.</param>
@@ -122,6 +123,8 @@ namespace MediaBrowser.Common
/// <summary>
/// Inits this instance.
/// </summary>
+ /// <param name="serviceCollection">The service collection.</param>
+ /// <returns>A task.</returns>
Task InitAsync(IServiceCollection serviceCollection);
/// <summary>
diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj
index 889fbfa5a..567fcdda1 100644
--- a/MediaBrowser.Common/MediaBrowser.Common.csproj
+++ b/MediaBrowser.Common/MediaBrowser.Common.csproj
@@ -12,8 +12,8 @@
</ItemGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.0.0" />
- <PackageReference Include="Microsoft.Net.Http.Headers" Version="2.2.0" />
+ <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.1.0" />
+ <PackageReference Include="Microsoft.Net.Http.Headers" Version="2.2.8" />
</ItemGroup>
<ItemGroup>
@@ -27,9 +27,16 @@
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
- <PropertyGroup>
- <!-- We need at least C# 7.1 for the "default literal" feature-->
- <LangVersion>latest</LangVersion>
+ <!-- Code analyzers-->
+ <ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
+ <!-- <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8" PrivateAssets="All" /> -->
+ <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
+ <!-- <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" /> -->
+ <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
+ </ItemGroup>
+
+ <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
+ <CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
diff --git a/MediaBrowser.Common/Net/CustomHeaderNames.cs b/MediaBrowser.Common/Net/CustomHeaderNames.cs
index 5ca9897eb..8cc48c55f 100644
--- a/MediaBrowser.Common/Net/CustomHeaderNames.cs
+++ b/MediaBrowser.Common/Net/CustomHeaderNames.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
namespace MediaBrowser.Common.Net
{
diff --git a/MediaBrowser.Common/Net/HttpRequestOptions.cs b/MediaBrowser.Common/Net/HttpRequestOptions.cs
index 18c4b181f..8207a45f3 100644
--- a/MediaBrowser.Common/Net/HttpRequestOptions.cs
+++ b/MediaBrowser.Common/Net/HttpRequestOptions.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Collections.Generic;
@@ -8,11 +9,22 @@ using Microsoft.Net.Http.Headers;
namespace MediaBrowser.Common.Net
{
/// <summary>
- /// Class HttpRequestOptions
+ /// Class HttpRequestOptions.
/// </summary>
public class HttpRequestOptions
{
/// <summary>
+ /// Initializes a new instance of the <see cref="HttpRequestOptions"/> class.
+ /// </summary>
+ public HttpRequestOptions()
+ {
+ RequestHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+
+ CacheMode = CacheMode.None;
+ DecompressionMethod = CompressionMethod.Deflate;
+ }
+
+ /// <summary>
/// Gets or sets the URL.
/// </summary>
/// <value>The URL.</value>
@@ -71,14 +83,17 @@ namespace MediaBrowser.Common.Net
public string RequestContentType { get; set; }
public string RequestContent { get; set; }
+
public byte[] RequestContentBytes { get; set; }
public bool BufferContent { get; set; }
public bool LogErrorResponseBody { get; set; }
+
public bool EnableKeepAlive { get; set; }
public CacheMode CacheMode { get; set; }
+
public TimeSpan CacheLength { get; set; }
public bool EnableDefaultUserAgent { get; set; }
@@ -89,17 +104,6 @@ namespace MediaBrowser.Common.Net
return value;
}
-
- /// <summary>
- /// Initializes a new instance of the <see cref="HttpRequestOptions"/> class.
- /// </summary>
- public HttpRequestOptions()
- {
- RequestHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
-
- CacheMode = CacheMode.None;
- DecompressionMethod = CompressionMethod.Deflate;
- }
}
public enum CacheMode
diff --git a/MediaBrowser.Common/Net/HttpResponseInfo.cs b/MediaBrowser.Common/Net/HttpResponseInfo.cs
index 0de034b0e..d711ad64a 100644
--- a/MediaBrowser.Common/Net/HttpResponseInfo.cs
+++ b/MediaBrowser.Common/Net/HttpResponseInfo.cs
@@ -1,5 +1,3 @@
-#pragma warning disable CS1591
-
using System;
using System.IO;
using System.Net;
@@ -8,10 +6,25 @@ using System.Net.Http.Headers;
namespace MediaBrowser.Common.Net
{
/// <summary>
- /// Class HttpResponseInfo
+ /// Class HttpResponseInfo.
/// </summary>
public class HttpResponseInfo : IDisposable
{
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+ public HttpResponseInfo()
+ {
+ }
+
+ public HttpResponseInfo(HttpResponseHeaders headers, HttpContentHeaders contentHeader)
+ {
+ Headers = headers;
+ ContentHeaders = contentHeader;
+ }
+
+#pragma warning restore CS1591
+#pragma warning restore SA1600
+
/// <summary>
/// Gets or sets the type of the content.
/// </summary>
@@ -60,21 +73,10 @@ namespace MediaBrowser.Common.Net
/// <value>The content headers.</value>
public HttpContentHeaders ContentHeaders { get; set; }
- public HttpResponseInfo()
- {
-
- }
-
- public HttpResponseInfo(HttpResponseHeaders headers, HttpContentHeaders contentHeader)
- {
- Headers = headers;
- ContentHeaders = contentHeader;
- }
-
/// <inheritdoc />
public void Dispose()
{
- // Only IDisposable for backwards compatibility
+ // backwards compatibility
}
}
}
diff --git a/MediaBrowser.Common/Net/IHttpClient.cs b/MediaBrowser.Common/Net/IHttpClient.cs
index 23ba34173..534e22edd 100644
--- a/MediaBrowser.Common/Net/IHttpClient.cs
+++ b/MediaBrowser.Common/Net/IHttpClient.cs
@@ -1,12 +1,12 @@
using System;
using System.IO;
-using System.Threading.Tasks;
using System.Net.Http;
+using System.Threading.Tasks;
namespace MediaBrowser.Common.Net
{
/// <summary>
- /// Interface IHttpClient
+ /// Interface IHttpClient.
/// </summary>
public interface IHttpClient
{
diff --git a/MediaBrowser.Common/Net/INetworkManager.cs b/MediaBrowser.Common/Net/INetworkManager.cs
index 0b99dc910..6bd7dd1d6 100644
--- a/MediaBrowser.Common/Net/INetworkManager.cs
+++ b/MediaBrowser.Common/Net/INetworkManager.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Collections.Generic;
@@ -11,20 +12,20 @@ namespace MediaBrowser.Common.Net
{
event EventHandler NetworkChanged;
+ Func<string[]> LocalSubnetsFn { get; set; }
+
/// <summary>
- /// Gets a random port number that is currently available
+ /// Gets a random port number that is currently available.
/// </summary>
/// <returns>System.Int32.</returns>
int GetRandomUnusedTcpPort();
int GetRandomUnusedUdpPort();
- Func<string[]> LocalSubnetsFn { get; set; }
-
/// <summary>
- /// Returns MAC Address from first Network Card in Computer
+ /// Returns the MAC Address from first Network Card in Computer.
/// </summary>
- /// <returns>[string] MAC Address</returns>
+ /// <returns>The MAC Address.</returns>
List<PhysicalAddress> GetMacAddresses();
/// <summary>
diff --git a/MediaBrowser.Common/Plugins/BasePlugin.cs b/MediaBrowser.Common/Plugins/BasePlugin.cs
index 6ef891d66..b24d10ff1 100644
--- a/MediaBrowser.Common/Plugins/BasePlugin.cs
+++ b/MediaBrowser.Common/Plugins/BasePlugin.cs
@@ -1,4 +1,4 @@
-#pragma warning disable CS1591
+#pragma warning disable SA1402
using System;
using System.IO;
@@ -8,10 +8,13 @@ using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Common.Plugins
{
+ /// <summary>
+ /// Provides a common base class for all plugins.
+ /// </summary>
public abstract class BasePlugin : IPlugin, IPluginAssembly
{
/// <summary>
- /// Gets the name of the plugin
+ /// Gets the name of the plugin.
/// </summary>
/// <value>The name.</value>
public abstract string Name { get; }
@@ -29,18 +32,24 @@ namespace MediaBrowser.Common.Plugins
public virtual Guid Id { get; private set; }
/// <summary>
- /// Gets the plugin version
+ /// Gets the plugin version.
/// </summary>
/// <value>The version.</value>
public Version Version { get; private set; }
/// <summary>
- /// Gets the path to the assembly file
+ /// Gets the path to the assembly file.
/// </summary>
/// <value>The assembly file path.</value>
public string AssemblyFilePath { get; private set; }
/// <summary>
+ /// Gets the full path to the data folder, where the plugin can store any miscellaneous files needed.
+ /// </summary>
+ /// <value>The data folder path.</value>
+ public string DataFolderPath { get; private set; }
+
+ /// <summary>
/// Gets the plugin info.
/// </summary>
/// <returns>PluginInfo.</returns>
@@ -62,9 +71,9 @@ namespace MediaBrowser.Common.Plugins
/// </summary>
public virtual void OnUninstalling()
{
-
}
+ /// <inheritdoc />
public void SetAttributes(string assemblyFilePath, string dataFolderPath, Version assemblyVersion)
{
AssemblyFilePath = assemblyFilePath;
@@ -72,26 +81,49 @@ namespace MediaBrowser.Common.Plugins
Version = assemblyVersion;
}
+ /// <inheritdoc />
public void SetId(Guid assemblyId)
{
Id = assemblyId;
}
-
- /// <summary>
- /// Gets the full path to the data folder, where the plugin can store any miscellaneous files needed
- /// </summary>
- /// <value>The data folder path.</value>
- public string DataFolderPath { get; private set; }
}
/// <summary>
- /// Provides a common base class for all plugins
+ /// Provides a common base class for all plugins.
/// </summary>
/// <typeparam name="TConfigurationType">The type of the T configuration type.</typeparam>
public abstract class BasePlugin<TConfigurationType> : BasePlugin, IHasPluginConfiguration
where TConfigurationType : BasePluginConfiguration
{
/// <summary>
+ /// The configuration sync lock.
+ /// </summary>
+ private readonly object _configurationSyncLock = new object();
+
+ /// <summary>
+ /// The save lock.
+ /// </summary>
+ private readonly object _configurationSaveLock = new object();
+
+ private Action<string> _directoryCreateFn;
+
+ /// <summary>
+ /// The configuration.
+ /// </summary>
+ private TConfigurationType _configuration;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="BasePlugin{TConfigurationType}" /> class.
+ /// </summary>
+ /// <param name="applicationPaths">The application paths.</param>
+ /// <param name="xmlSerializer">The XML serializer.</param>
+ protected BasePlugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer)
+ {
+ ApplicationPaths = applicationPaths;
+ XmlSerializer = xmlSerializer;
+ }
+
+ /// <summary>
/// Gets the application paths.
/// </summary>
/// <value>The application paths.</value>
@@ -104,34 +136,19 @@ namespace MediaBrowser.Common.Plugins
protected IXmlSerializer XmlSerializer { get; private set; }
/// <summary>
- /// Gets the type of configuration this plugin uses
+ /// Gets the type of configuration this plugin uses.
/// </summary>
/// <value>The type of the configuration.</value>
public Type ConfigurationType => typeof(TConfigurationType);
- private Action<string> _directoryCreateFn;
- public void SetStartupInfo(Action<string> directoryCreateFn)
- {
- // hack alert, until the .net core transition is complete
- _directoryCreateFn = directoryCreateFn;
- }
-
/// <summary>
- /// Gets the name the assembly file
+ /// Gets the name the assembly file.
/// </summary>
/// <value>The name of the assembly file.</value>
protected string AssemblyFileName => Path.GetFileName(AssemblyFilePath);
/// <summary>
- /// The _configuration sync lock
- /// </summary>
- private readonly object _configurationSyncLock = new object();
- /// <summary>
- /// The _configuration
- /// </summary>
- private TConfigurationType _configuration;
- /// <summary>
- /// Gets the plugin's configuration
+ /// Gets or sets the plugin's configuration.
/// </summary>
/// <value>The configuration.</value>
public TConfigurationType Configuration
@@ -149,55 +166,54 @@ namespace MediaBrowser.Common.Plugins
}
}
}
+
return _configuration;
}
- protected set => _configuration = value;
- }
- private TConfigurationType LoadConfiguration()
- {
- var path = ConfigurationFilePath;
-
- try
- {
- return (TConfigurationType)XmlSerializer.DeserializeFromFile(typeof(TConfigurationType), path);
- }
- catch
- {
- return (TConfigurationType)Activator.CreateInstance(typeof(TConfigurationType));
- }
+ protected set => _configuration = value;
}
/// <summary>
- /// Gets the name of the configuration file. Subclasses should override
+ /// Gets the name of the configuration file. Subclasses should override.
/// </summary>
/// <value>The name of the configuration file.</value>
public virtual string ConfigurationFileName => Path.ChangeExtension(AssemblyFileName, ".xml");
/// <summary>
- /// Gets the full path to the configuration file
+ /// Gets the full path to the configuration file.
/// </summary>
/// <value>The configuration file path.</value>
public string ConfigurationFilePath => Path.Combine(ApplicationPaths.PluginConfigurationsPath, ConfigurationFileName);
/// <summary>
- /// Initializes a new instance of the <see cref="BasePlugin{TConfigurationType}" /> class.
+ /// Gets the plugin's configuration.
/// </summary>
- /// <param name="applicationPaths">The application paths.</param>
- /// <param name="xmlSerializer">The XML serializer.</param>
- protected BasePlugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer)
+ /// <value>The configuration.</value>
+ BasePluginConfiguration IHasPluginConfiguration.Configuration => Configuration;
+
+ /// <inheritdoc />
+ public void SetStartupInfo(Action<string> directoryCreateFn)
{
- ApplicationPaths = applicationPaths;
- XmlSerializer = xmlSerializer;
+ // hack alert, until the .net core transition is complete
+ _directoryCreateFn = directoryCreateFn;
}
- /// <summary>
- /// The _save lock
- /// </summary>
- private readonly object _configurationSaveLock = new object();
+ private TConfigurationType LoadConfiguration()
+ {
+ var path = ConfigurationFilePath;
+
+ try
+ {
+ return (TConfigurationType)XmlSerializer.DeserializeFromFile(typeof(TConfigurationType), path);
+ }
+ catch
+ {
+ return (TConfigurationType)Activator.CreateInstance(typeof(TConfigurationType));
+ }
+ }
/// <summary>
- /// Saves the current configuration to the file system
+ /// Saves the current configuration to the file system.
/// </summary>
public virtual void SaveConfiguration()
{
@@ -209,12 +225,7 @@ namespace MediaBrowser.Common.Plugins
}
}
- /// <summary>
- /// Completely overwrites the current configuration with a new copy
- /// Returns true or false indicating success or failure
- /// </summary>
- /// <param name="configuration">The configuration.</param>
- /// <exception cref="ArgumentNullException">configuration</exception>
+ /// <inheritdoc />
public virtual void UpdateConfiguration(BasePluginConfiguration configuration)
{
if (configuration == null)
@@ -227,12 +238,7 @@ namespace MediaBrowser.Common.Plugins
SaveConfiguration();
}
- /// <summary>
- /// Gets the plugin's configuration
- /// </summary>
- /// <value>The configuration.</value>
- BasePluginConfiguration IHasPluginConfiguration.Configuration => Configuration;
-
+ /// <inheritdoc />
public override PluginInfo GetPluginInfo()
{
var info = base.GetPluginInfo();
@@ -242,10 +248,4 @@ namespace MediaBrowser.Common.Plugins
return info;
}
}
-
- public interface IPluginAssembly
- {
- void SetAttributes(string assemblyFilePath, string dataFolderPath, Version assemblyVersion);
- void SetId(Guid assemblyId);
- }
}
diff --git a/MediaBrowser.Common/Plugins/IPlugin.cs b/MediaBrowser.Common/Plugins/IPlugin.cs
index 7bd90c964..001ca8be8 100644
--- a/MediaBrowser.Common/Plugins/IPlugin.cs
+++ b/MediaBrowser.Common/Plugins/IPlugin.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using MediaBrowser.Model.Plugins;
@@ -6,12 +7,12 @@ using MediaBrowser.Model.Plugins;
namespace MediaBrowser.Common.Plugins
{
/// <summary>
- /// Interface IPlugin
+ /// Interface IPlugin.
/// </summary>
public interface IPlugin
{
/// <summary>
- /// Gets the name of the plugin
+ /// Gets the name of the plugin.
/// </summary>
/// <value>The name.</value>
string Name { get; }
@@ -29,19 +30,19 @@ namespace MediaBrowser.Common.Plugins
Guid Id { get; }
/// <summary>
- /// Gets the plugin version
+ /// Gets the plugin version.
/// </summary>
/// <value>The version.</value>
Version Version { get; }
/// <summary>
- /// Gets the path to the assembly file
+ /// Gets the path to the assembly file.
/// </summary>
/// <value>The assembly file path.</value>
string AssemblyFilePath { get; }
/// <summary>
- /// Gets the full path to the data folder, where the plugin can store any miscellaneous files needed
+ /// Gets the full path to the data folder, where the plugin can store any miscellaneous files needed.
/// </summary>
/// <value>The data folder path.</value>
string DataFolderPath { get; }
@@ -61,24 +62,24 @@ namespace MediaBrowser.Common.Plugins
public interface IHasPluginConfiguration
{
/// <summary>
- /// Gets the type of configuration this plugin uses
+ /// Gets the type of configuration this plugin uses.
/// </summary>
/// <value>The type of the configuration.</value>
Type ConfigurationType { get; }
/// <summary>
- /// Completely overwrites the current configuration with a new copy
- /// Returns true or false indicating success or failure
+ /// Gets the plugin's configuration.
/// </summary>
- /// <param name="configuration">The configuration.</param>
- /// <exception cref="ArgumentNullException">configuration</exception>
- void UpdateConfiguration(BasePluginConfiguration configuration);
+ /// <value>The configuration.</value>
+ BasePluginConfiguration Configuration { get; }
/// <summary>
- /// Gets the plugin's configuration
+ /// Completely overwrites the current configuration with a new copy
+ /// Returns true or false indicating success or failure.
/// </summary>
- /// <value>The configuration.</value>
- BasePluginConfiguration Configuration { get; }
+ /// <param name="configuration">The configuration.</param>
+ /// <exception cref="ArgumentNullException"><c>configuration</c> is <c>null</c>.</exception>
+ void UpdateConfiguration(BasePluginConfiguration configuration);
void SetStartupInfo(Action<string> directoryCreateFn);
}
diff --git a/MediaBrowser.Common/Plugins/IPluginAssembly.cs b/MediaBrowser.Common/Plugins/IPluginAssembly.cs
new file mode 100644
index 000000000..388ac61ab
--- /dev/null
+++ b/MediaBrowser.Common/Plugins/IPluginAssembly.cs
@@ -0,0 +1,14 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
+using System;
+
+namespace MediaBrowser.Common.Plugins
+{
+ public interface IPluginAssembly
+ {
+ void SetAttributes(string assemblyFilePath, string dataFolderPath, Version assemblyVersion);
+
+ void SetId(Guid assemblyId);
+ }
+}
diff --git a/MediaBrowser.Common/Progress/ActionableProgress.cs b/MediaBrowser.Common/Progress/ActionableProgress.cs
index af69055aa..92141ba52 100644
--- a/MediaBrowser.Common/Progress/ActionableProgress.cs
+++ b/MediaBrowser.Common/Progress/ActionableProgress.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
diff --git a/MediaBrowser.Common/Providers/SubtitleConfigurationFactory.cs b/MediaBrowser.Common/Providers/SubtitleConfigurationFactory.cs
index 0445397ad..a6422e2c8 100644
--- a/MediaBrowser.Common/Providers/SubtitleConfigurationFactory.cs
+++ b/MediaBrowser.Common/Providers/SubtitleConfigurationFactory.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System.Collections.Generic;
using MediaBrowser.Common.Configuration;
diff --git a/MediaBrowser.Common/System/OperatingSystem.cs b/MediaBrowser.Common/System/OperatingSystem.cs
index 7d38ddb6e..f23af4799 100644
--- a/MediaBrowser.Common/System/OperatingSystem.cs
+++ b/MediaBrowser.Common/System/OperatingSystem.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Runtime.InteropServices;
diff --git a/MediaBrowser.Common/Updates/IInstallationManager.cs b/MediaBrowser.Common/Updates/IInstallationManager.cs
index e49812f15..a09c1916c 100644
--- a/MediaBrowser.Common/Updates/IInstallationManager.cs
+++ b/MediaBrowser.Common/Updates/IInstallationManager.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
using System.Collections.Generic;
@@ -103,17 +104,16 @@ namespace MediaBrowser.Common.Updates
Task InstallPackage(PackageVersionInfo package, CancellationToken cancellationToken = default);
/// <summary>
- /// Uninstalls a plugin
+ /// Uninstalls a plugin.
/// </summary>
/// <param name="plugin">The plugin.</param>
- /// <exception cref="ArgumentException"></exception>
void UninstallPlugin(IPlugin plugin);
/// <summary>
- /// Cancels the installation
+ /// Cancels the installation.
/// </summary>
- /// <param name="id">The id of the package that is being installed</param>
- /// <returns>Returns true if the install was cancelled</returns>
+ /// <param name="id">The id of the package that is being installed.</param>
+ /// <returns>Returns true if the install was cancelled.</returns>
bool CancelInstallation(Guid id);
}
}
diff --git a/MediaBrowser.Common/Updates/InstallationEventArgs.cs b/MediaBrowser.Common/Updates/InstallationEventArgs.cs
index 36e124ddf..8bbb231ce 100644
--- a/MediaBrowser.Common/Updates/InstallationEventArgs.cs
+++ b/MediaBrowser.Common/Updates/InstallationEventArgs.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using MediaBrowser.Model.Updates;
diff --git a/MediaBrowser.Common/Updates/InstallationFailedEventArgs.cs b/MediaBrowser.Common/Updates/InstallationFailedEventArgs.cs
index 46f10c84f..c8967f9db 100644
--- a/MediaBrowser.Common/Updates/InstallationFailedEventArgs.cs
+++ b/MediaBrowser.Common/Updates/InstallationFailedEventArgs.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable SA1600
using System;
diff --git a/MediaBrowser.Controller/Authentication/AuthenticationResult.cs b/MediaBrowser.Controller/Authentication/AuthenticationResult.cs
index 3c65156e3..5248ea4c1 100644
--- a/MediaBrowser.Controller/Authentication/AuthenticationResult.cs
+++ b/MediaBrowser.Controller/Authentication/AuthenticationResult.cs
@@ -1,14 +1,19 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Dto;
-
namespace MediaBrowser.Controller.Authentication
{
public class AuthenticationResult
{
public UserDto User { get; set; }
+
public SessionInfo SessionInfo { get; set; }
+
public string AccessToken { get; set; }
+
public string ServerId { get; set; }
}
}
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs
index cba2c9dda..353c675cb 100644
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ b/MediaBrowser.Controller/Entities/BaseItem.cs
@@ -19,7 +19,6 @@ using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Library;
diff --git a/MediaBrowser.Model/Entities/MediaType.cs b/MediaBrowser.Model/Entities/MediaType.cs
index c56c8f8f2..d8b02c9ea 100644
--- a/MediaBrowser.Model/Entities/MediaType.cs
+++ b/MediaBrowser.Model/Entities/MediaType.cs
@@ -3,20 +3,23 @@ namespace MediaBrowser.Model.Entities
/// <summary>
/// Class MediaType
/// </summary>
- public class MediaType
+ public static class MediaType
{
/// <summary>
/// The video
/// </summary>
public const string Video = "Video";
+
/// <summary>
/// The audio
/// </summary>
public const string Audio = "Audio";
+
/// <summary>
/// The photo
/// </summary>
public const string Photo = "Photo";
+
/// <summary>
/// The book
/// </summary>
diff --git a/MediaBrowser.Model/MediaInfo/PlaybackInfoResponse.cs b/MediaBrowser.Model/MediaInfo/PlaybackInfoResponse.cs
index 38638af42..440818c3e 100644
--- a/MediaBrowser.Model/MediaInfo/PlaybackInfoResponse.cs
+++ b/MediaBrowser.Model/MediaInfo/PlaybackInfoResponse.cs
@@ -1,15 +1,20 @@
+using System;
+using System.Collections.Generic;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto;
namespace MediaBrowser.Model.MediaInfo
{
+ /// <summary>
+ /// Class PlaybackInfoResponse.
+ /// </summary>
public class PlaybackInfoResponse
{
/// <summary>
/// Gets or sets the media sources.
/// </summary>
/// <value>The media sources.</value>
- public MediaSourceInfo[] MediaSources { get; set; }
+ public IReadOnlyList<MediaSourceInfo> MediaSources { get; set; }
/// <summary>
/// Gets or sets the play session identifier.
@@ -23,9 +28,12 @@ namespace MediaBrowser.Model.MediaInfo
/// <value>The error code.</value>
public PlaybackErrorCode? ErrorCode { get; set; }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PlaybackInfoResponse" /> class.
+ /// </summary>
public PlaybackInfoResponse()
{
- MediaSources = new MediaSourceInfo[] { };
+ MediaSources = Array.Empty<MediaSourceInfo>();
}
}
}
diff --git a/jellyfin.ruleset b/jellyfin.ruleset
index 27d8a7cd9..92b7a03fd 100644
--- a/jellyfin.ruleset
+++ b/jellyfin.ruleset
@@ -24,6 +24,8 @@
<Rule Id="SA1413" Action="None" />
<!-- disable warning SA1512: Single-line comments must not be followed by blank line -->
<Rule Id="SA1512" Action="None" />
+ <!-- disable warning SA1515: Single-line comment should be preceded by blank line -->
+ <Rule Id="SA1515" Action="None" />
<!-- disable warning SA1633: The file header is missing or not located at the top of the file -->
<Rule Id="SA1633" Action="None" />
</Rules>
@@ -35,6 +37,8 @@
<Rule Id="CA1032" Action="Info" />
<!-- disable warning CA1062: Validate arguments of public methods -->
<Rule Id="CA1062" Action="Info" />
+ <!-- disable warning CA1716: Identifiers should not match keywords -->
+ <Rule Id="CA1716" Action="Info" />
<!-- disable warning CA1720: Identifiers should not contain type names -->
<Rule Id="CA1720" Action="Info" />
<!-- disable warning CA1812: internal class that is apparently never instantiated.
@@ -43,14 +47,18 @@
<Rule Id="CA1812" Action="Info" />
<!-- disable warning CA1822: Member does not access instance data and can be marked as static -->
<Rule Id="CA1822" Action="Info" />
+ <!-- disable warning CA2000: Dispose objects before losing scope -->
+ <Rule Id="CA2000" Action="Info" />
<!-- disable warning CA1054: Change the type of parameter url from string to System.Uri -->
<Rule Id="CA1054" Action="None" />
+ <!-- disable warning CA1055: URI return values should not be strings -->
+ <Rule Id="CA1055" Action="None" />
+ <!-- disable warning CA1056: URI properties should not be strings -->
+ <Rule Id="CA1056" Action="None" />
<!-- disable warning CA1303: Do not pass literals as localized parameters -->
<Rule Id="CA1303" Action="None" />
<!-- disable warning CA1308: Normalize strings to uppercase -->
<Rule Id="CA1308" Action="None" />
- <!-- disable warning CA2000: Dispose objects before losing scope -->
- <Rule Id="CA2000" Action="None" />
</Rules>
</RuleSet>
diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
index 1671b8d79..e0deeeabb 100644
--- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
+++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>netcoreapp3.0</TargetFramework>
+ <TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
diff --git a/tests/Jellyfin.Naming.Tests/Music/MultiDiscAlbumTests.cs b/tests/Jellyfin.Naming.Tests/Music/MultiDiscAlbumTests.cs
new file mode 100644
index 000000000..eb69d915c
--- /dev/null
+++ b/tests/Jellyfin.Naming.Tests/Music/MultiDiscAlbumTests.cs
@@ -0,0 +1,57 @@
+using Emby.Naming.Audio;
+using Emby.Naming.Common;
+using Xunit;
+
+namespace Jellyfin.Naming.Tests.Music
+{
+ public class MultiDiscAlbumTests
+ {
+ [Fact]
+ public void TestMultiDiscAlbums()
+ {
+ Assert.False(IsMultiDiscAlbumFolder(@"blah blah"));
+ Assert.False(IsMultiDiscAlbumFolder(@"d:/music\weezer/03 Pinkerton"));
+ Assert.False(IsMultiDiscAlbumFolder(@"d:/music/michael jackson/Bad (2012 Remaster)"));
+
+ Assert.True(IsMultiDiscAlbumFolder(@"cd1"));
+ Assert.True(IsMultiDiscAlbumFolder(@"disc1"));
+ Assert.True(IsMultiDiscAlbumFolder(@"disk1"));
+
+ // Add a space
+ Assert.True(IsMultiDiscAlbumFolder(@"cd 1"));
+ Assert.True(IsMultiDiscAlbumFolder(@"disc 1"));
+ Assert.True(IsMultiDiscAlbumFolder(@"disk 1"));
+
+ Assert.True(IsMultiDiscAlbumFolder(@"cd - 1"));
+ Assert.True(IsMultiDiscAlbumFolder(@"disc- 1"));
+ Assert.True(IsMultiDiscAlbumFolder(@"disk - 1"));
+
+ Assert.True(IsMultiDiscAlbumFolder(@"Disc 01 (Hugo Wolf · 24 Lieder)"));
+ Assert.True(IsMultiDiscAlbumFolder(@"Disc 04 (Encores and Folk Songs)"));
+ Assert.True(IsMultiDiscAlbumFolder(@"Disc04 (Encores and Folk Songs)"));
+ Assert.True(IsMultiDiscAlbumFolder(@"Disc 04(Encores and Folk Songs)"));
+ Assert.True(IsMultiDiscAlbumFolder(@"Disc04(Encores and Folk Songs)"));
+
+ Assert.True(IsMultiDiscAlbumFolder(@"D:/Video/MBTestLibrary/VideoTest/music/.38 special/anth/Disc 2"));
+ }
+
+ [Fact]
+ public void TestMultiDiscAlbums1()
+ {
+ Assert.False(IsMultiDiscAlbumFolder(@"[1985] Oppurtunities (Let's make lots of money) (1985)"));
+ }
+
+ [Fact]
+ public void TestMultiDiscAlbums2()
+ {
+ Assert.False(IsMultiDiscAlbumFolder(@"Blah 04(Encores and Folk Songs)"));
+ }
+
+ private bool IsMultiDiscAlbumFolder(string path)
+ {
+ var parser = new AlbumParser(new NamingOptions());
+
+ return parser.ParseMultiPart(path).IsMultiPart;
+ }
+ }
+}
diff --git a/tests/Jellyfin.Naming.Tests/Subtitles/SubtitleParserTests.cs b/tests/Jellyfin.Naming.Tests/Subtitles/SubtitleParserTests.cs
new file mode 100644
index 000000000..e8f14cdc4
--- /dev/null
+++ b/tests/Jellyfin.Naming.Tests/Subtitles/SubtitleParserTests.cs
@@ -0,0 +1,40 @@
+using Emby.Naming.Common;
+using Emby.Naming.Subtitles;
+using Xunit;
+
+namespace Jellyfin.Naming.Tests.Subtitles
+{
+ public class SubtitleParserTests
+ {
+ private SubtitleParser GetParser()
+ {
+ var options = new NamingOptions();
+
+ return new SubtitleParser(options);
+ }
+
+ [Fact]
+ public void TestSubtitles()
+ {
+ Test("The Skin I Live In (2011).srt", null, false, false);
+ Test("The Skin I Live In (2011).eng.srt", "eng", false, false);
+ Test("The Skin I Live In (2011).eng.default.srt", "eng", true, false);
+ Test("The Skin I Live In (2011).eng.forced.srt", "eng", false, true);
+ Test("The Skin I Live In (2011).eng.foreign.srt", "eng", false, true);
+ Test("The Skin I Live In (2011).eng.default.foreign.srt", "eng", true, true);
+
+ Test("The Skin I Live In (2011).default.foreign.eng.srt", "eng", true, true);
+ }
+
+ private void Test(string input, string language, bool isDefault, bool isForced)
+ {
+ var parser = GetParser();
+
+ var result = parser.ParseFile(input);
+
+ Assert.Equal(language, result.Language, true);
+ Assert.Equal(isDefault, result.IsDefault);
+ Assert.Equal(isForced, result.IsForced);
+ }
+ }
+}
diff --git a/tests/Jellyfin.Naming.Tests/TV/AbsoluteEpisodeNumberTests.cs b/tests/Jellyfin.Naming.Tests/TV/AbsoluteEpisodeNumberTests.cs
new file mode 100644
index 000000000..9abbcc7bf
--- /dev/null
+++ b/tests/Jellyfin.Naming.Tests/TV/AbsoluteEpisodeNumberTests.cs
@@ -0,0 +1,61 @@
+using Emby.Naming.Common;
+using Emby.Naming.TV;
+using Xunit;
+
+namespace Jellyfin.Naming.Tests.TV
+{
+ public class AbsoluteEpisodeNumberTests
+ {
+ [Fact]
+ public void TestAbsoluteEpisodeNumber1()
+ {
+ Assert.Equal(12, GetEpisodeNumberFromFile(@"The Simpsons/12.avi"));
+ }
+
+ [Fact]
+ public void TestAbsoluteEpisodeNumber2()
+ {
+ Assert.Equal(12, GetEpisodeNumberFromFile(@"The Simpsons/The Simpsons 12.avi"));
+ }
+
+ [Fact]
+ public void TestAbsoluteEpisodeNumber3()
+ {
+ Assert.Equal(82, GetEpisodeNumberFromFile(@"The Simpsons/The Simpsons 82.avi"));
+ }
+
+ [Fact]
+ public void TestAbsoluteEpisodeNumber4()
+ {
+ Assert.Equal(112, GetEpisodeNumberFromFile(@"The Simpsons/The Simpsons 112.avi"));
+ }
+
+ [Fact]
+ public void TestAbsoluteEpisodeNumber5()
+ {
+ Assert.Equal(2, GetEpisodeNumberFromFile(@"The Simpsons/Foo_ep_02.avi"));
+ }
+
+ [Fact]
+ public void TestAbsoluteEpisodeNumber6()
+ {
+ Assert.Equal(889, GetEpisodeNumberFromFile(@"The Simpsons/The Simpsons 889.avi"));
+ }
+
+ [Fact]
+ public void TestAbsoluteEpisodeNumber7()
+ {
+ Assert.Equal(101, GetEpisodeNumberFromFile(@"The Simpsons/The Simpsons 101.avi"));
+ }
+
+ private int? GetEpisodeNumberFromFile(string path)
+ {
+ var options = new NamingOptions();
+
+ var result = new EpisodeResolver(options)
+ .Resolve(path, false, null, null, true);
+
+ return result.EpisodeNumber;
+ }
+ }
+}
diff --git a/tests/Jellyfin.Naming.Tests/TV/DailyEpisodeTests.cs b/tests/Jellyfin.Naming.Tests/TV/DailyEpisodeTests.cs
new file mode 100644
index 000000000..29daf8cc3
--- /dev/null
+++ b/tests/Jellyfin.Naming.Tests/TV/DailyEpisodeTests.cs
@@ -0,0 +1,69 @@
+using Emby.Naming.Common;
+using Emby.Naming.TV;
+using Xunit;
+
+namespace Jellyfin.Naming.Tests.TV
+{
+ public class DailyEpisodeTests
+ {
+ [Fact]
+ public void TestDailyEpisode1()
+ {
+ Test(@"/server/anything_1996.11.14.mp4", "anything", 1996, 11, 14);
+ }
+
+ [Fact]
+ public void TestDailyEpisode2()
+ {
+ Test(@"/server/anything_1996-11-14.mp4", "anything", 1996, 11, 14);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestDailyEpisode3()
+ {
+ Test(@"/server/anything_14.11.1996.mp4", "anything", 1996, 11, 14);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestDailyEpisode4()
+ {
+ Test(@"/server/A Daily Show - (2015-01-15) - Episode Name - [720p].mkv", "A Daily Show", 2015, 01, 15);
+ }
+
+ [Fact]
+ public void TestDailyEpisode5()
+ {
+ Test(@"/server/james.corden.2017.04.20.anne.hathaway.720p.hdtv.x264-crooks.mkv", "james.corden", 2017, 04, 20);
+ }
+
+ [Fact]
+ public void TestDailyEpisode6()
+ {
+ Test(@"/server/ABC News 2018_03_24_19_00_00.mkv", "ABC News", 2018, 03, 24);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestDailyEpisode7()
+ {
+ Test(@"/server/Last Man Standing_KTLADT_2018_05_25_01_28_00.wtv", "Last Man Standing", 2018, 05, 25);
+ }
+
+ private void Test(string path, string seriesName, int? year, int? month, int? day)
+ {
+ var options = new NamingOptions();
+
+ var result = new EpisodeResolver(options)
+ .Resolve(path, false);
+
+ Assert.Null(result.SeasonNumber);
+ Assert.Null(result.EpisodeNumber);
+ Assert.Equal(year, result.Year);
+ Assert.Equal(month, result.Month);
+ Assert.Equal(day, result.Day);
+ Assert.Equal(seriesName, result.SeriesName, true);
+ }
+ }
+}
diff --git a/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs b/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs
new file mode 100644
index 000000000..1ae637281
--- /dev/null
+++ b/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs
@@ -0,0 +1,424 @@
+using Emby.Naming.Common;
+using Emby.Naming.TV;
+using Xunit;
+
+namespace Jellyfin.Naming.Tests.TV
+{
+ public class EpisodeNumberTests
+ {
+ [Fact]
+ public void TestEpisodeNumber1()
+ {
+ Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 02/S02E03 blah.avi"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber40()
+ {
+ Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 2/02x03 - 02x04 - 02x15 - Ep Name.mp4"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber41()
+ {
+ Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 1/01x02 blah.avi"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber42()
+ {
+ Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 1/S01x02 blah.avi"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber43()
+ {
+ Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 1/S01E02 blah.avi"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber44()
+ {
+ Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 2/Elementary - 02x03-04-15 - Ep Name.mp4"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber45()
+ {
+ Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 1/S01xE02 blah.avi"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber46()
+ {
+ Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 1/seriesname S01E02 blah.avi"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber47()
+ {
+ Assert.Equal(36, GetEpisodeNumberFromFile(@"Season 2/[HorribleSubs] Hunter X Hunter - 136 [720p].mkv"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber50()
+ {
+ // This convention is not currently supported, just adding in case we want to look at it in the future
+ Assert.Equal(1, GetEpisodeNumberFromFile(@"2016/Season s2016e1.mp4"));
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestEpisodeNumber51()
+ {
+ // This convention is not currently supported, just adding in case we want to look at it in the future
+ Assert.Equal(1, GetEpisodeNumberFromFile(@"2016/Season 2016x1.mp4"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber52()
+ {
+ Assert.Equal(16, GetEpisodeNumberFromFile(@"Season 2/Episode - 16.avi"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber53()
+ {
+ // This is not supported. Expected to fail, although it would be a good one to add support for.
+ Assert.Equal(16, GetEpisodeNumberFromFile(@"Season 2/Episode 16.avi"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber54()
+ {
+ // This is not supported. Expected to fail, although it would be a good one to add support for.
+ Assert.Equal(16, GetEpisodeNumberFromFile(@"Season 2/Episode 16 - Some Title.avi"));
+ }
+
+ // [Fact]
+ public void TestEpisodeNumber55()
+ {
+ // This is not supported. Expected to fail, although it would be a good one to add support for.
+ Assert.Equal(16, GetEpisodeNumberFromFile(@"Season 2/Season 3 Episode 16.avi"));
+ }
+
+ // [Fact]
+ public void TestEpisodeNumber56()
+ {
+ // This is not supported. Expected to fail, although it would be a good one to add support for.
+ Assert.Equal(16, GetEpisodeNumberFromFile(@"Season 2/Season 3 Episode 16 - Some Title.avi"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber57()
+ {
+ Assert.Equal(16, GetEpisodeNumberFromFile(@"Season 2/16 Some Title.avi"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber58()
+ {
+ Assert.Equal(16, GetEpisodeNumberFromFile(@"Season 2/16 - 12 Some Title.avi"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber59()
+ {
+ Assert.Equal(7, GetEpisodeNumberFromFile(@"Season 2/7 - 12 Angry Men.avi"));
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestEpisodeNumber60()
+ {
+ Assert.Equal(16, GetEpisodeNumberFromFile(@"Season 2/16 12 Some Title.avi"));
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestEpisodeNumber61()
+ {
+ Assert.Equal(7, GetEpisodeNumberFromFile(@"Season 2/7 12 Angry Men.avi"));
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestEpisodeNumber62()
+ {
+ // This is not supported. Expected to fail, although it would be a good one to add support for.
+ Assert.Equal(3, GetEpisodeNumberFromFile(@"Season 4/Uchuu.Senkan.Yamato.2199.E03.avi"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber63()
+ {
+ Assert.Equal(3, GetEpisodeNumberFromFile(@"Season 4/Uchuu.Senkan.Yamato.2199.S04E03.avi"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber64()
+ {
+ Assert.Equal(368, GetEpisodeNumberFromFile(@"Running Man/Running Man S2017E368.mkv"));
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestEpisodeNumber65()
+ {
+ // Not supported yet
+ Assert.Equal(7, GetEpisodeNumberFromFile(@"/The.Legend.of.Condor.Heroes.2017.V2.web-dl.1080p.h264.aac-hdctv/The.Legend.of.Condor.Heroes.2017.E07.V2.web-dl.1080p.h264.aac-hdctv.mkv"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber30()
+ {
+ Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 2/02x03 - 02x04 - 02x15 - Ep Name.mp4"));
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestEpisodeNumber31()
+ {
+ Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 1/seriesname 01x02 blah.avi"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber32()
+ {
+ Assert.Equal(9, GetEpisodeNumberFromFile(@"Season 25/The Simpsons.S25E09.Steal this episode.mp4"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber33()
+ {
+ Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 1/seriesname S01x02 blah.avi"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber34()
+ {
+ Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 2/Elementary - 02x03 - 02x04 - 02x15 - Ep Name.mp4"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber35()
+ {
+ Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 1/seriesname S01xE02 blah.avi"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber36()
+ {
+ Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 02/02x03 - x04 - x15 - Ep Name.mp4"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber37()
+ {
+ Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 02/Elementary - 02x03 - x04 - x15 - Ep Name.mp4"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber38()
+ {
+ Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 02/02x03x04x15 - Ep Name.mp4"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber39()
+ {
+ Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 02/Elementary - 02x03x04x15 - Ep Name.mp4"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber20()
+ {
+ Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 2/02x03-04-15 - Ep Name.mp4"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber21()
+ {
+ Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 02/02x03-E15 - Ep Name.mp4"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber22()
+ {
+ Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 02/Elementary - 02x03-E15 - Ep Name.mp4"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber23()
+ {
+ Assert.Equal(23, GetEpisodeNumberFromFile(@"Season 1/Elementary - S01E23-E24-E26 - The Woman.mp4"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber24()
+ {
+ Assert.Equal(23, GetEpisodeNumberFromFile(@"Season 2009/S2009E23-E24-E26 - The Woman.mp4"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber25()
+ {
+ Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 2009/2009x02 blah.avi"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber26()
+ {
+ Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 2009/S2009x02 blah.avi"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber27()
+ {
+ Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 2009/S2009E02 blah.avi"));
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestEpisodeNumber28()
+ {
+ Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 2009/seriesname 2009x02 blah.avi"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber29()
+ {
+ Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 2009/Elementary - 2009x03x04x15 - Ep Name.mp4"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber11()
+ {
+ Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 2009/2009x03x04x15 - Ep Name.mp4"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber12()
+ {
+ Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 2009/Elementary - 2009x03-E15 - Ep Name.mp4"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber13()
+ {
+ Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 2009/S2009xE02 blah.avi"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber14()
+ {
+ Assert.Equal(23, GetEpisodeNumberFromFile(@"Season 2009/Elementary - S2009E23-E24-E26 - The Woman.mp4"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber15()
+ {
+ Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 2009/seriesname S2009xE02 blah.avi"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber16()
+ {
+ Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 2009/2009x03-E15 - Ep Name.mp4"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber17()
+ {
+ Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 2009/seriesname S2009E02 blah.avi"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber18()
+ {
+ Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 2009/2009x03 - 2009x04 - 2009x15 - Ep Name.mp4"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber19()
+ {
+ Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 2009/2009x03 - x04 - x15 - Ep Name.mp4"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber2()
+ {
+ Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 2009/seriesname S2009x02 blah.avi"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber3()
+ {
+ Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 2009/Elementary - 2009x03 - 2009x04 - 2009x15 - Ep Name.mp4"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber4()
+ {
+ Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 2009/Elementary - 2009x03-04-15 - Ep Name.mp4"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber5()
+ {
+ Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 2009/2009x03-04-15 - Ep Name.mp4"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber6()
+ {
+ Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 2009/Elementary - 2009x03 - x04 - x15 - Ep Name.mp4"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber7()
+ {
+ Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 1/02 - blah-02 a.avi"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber8()
+ {
+ Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 1/02 - blah.avi"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber9()
+ {
+ Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 2/02 - blah 14 blah.avi"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber10()
+ {
+ Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 2/02.avi"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber48()
+ {
+ Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 2/2. Infestation.avi"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumber49()
+ {
+ Assert.Equal(7, GetEpisodeNumberFromFile(@"The Wonder Years/The.Wonder.Years.S04.PDTV.x264-JCH/The Wonder Years s04e07 Christmas Party NTSC PDTV.avi"));
+ }
+
+ private int? GetEpisodeNumberFromFile(string path)
+ {
+ var options = new NamingOptions();
+
+ var result = new EpisodePathParser(options)
+ .Parse(path, false);
+
+ return result.EpisodeNumber;
+ }
+ }
+}
diff --git a/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberWithoutSeasonTests.cs b/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberWithoutSeasonTests.cs
new file mode 100644
index 000000000..00aa9ee7c
--- /dev/null
+++ b/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberWithoutSeasonTests.cs
@@ -0,0 +1,127 @@
+using Emby.Naming.Common;
+using Emby.Naming.TV;
+using Xunit;
+
+namespace Jellyfin.Naming.Tests.TV
+{
+ public class EpisodeNumberWithoutSeasonTests
+ {
+ [Fact]
+ public void TestEpisodeNumberWithoutSeason1()
+ {
+ Assert.Equal(8, GetEpisodeNumberFromFile(@"The Simpsons/The Simpsons.S25E08.Steal this episode.mp4"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumberWithoutSeason2()
+ {
+ Assert.Equal(2, GetEpisodeNumberFromFile(@"The Simpsons/The Simpsons - 02 - Ep Name.avi"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumberWithoutSeason3()
+ {
+ Assert.Equal(2, GetEpisodeNumberFromFile(@"The Simpsons/02.avi"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumberWithoutSeason4()
+ {
+ Assert.Equal(2, GetEpisodeNumberFromFile(@"The Simpsons/02 - Ep Name.avi"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumberWithoutSeason5()
+ {
+ Assert.Equal(2, GetEpisodeNumberFromFile(@"The Simpsons/02-Ep Name.avi"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumberWithoutSeason6()
+ {
+ Assert.Equal(2, GetEpisodeNumberFromFile(@"The Simpsons/02.EpName.avi"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumberWithoutSeason7()
+ {
+ Assert.Equal(2, GetEpisodeNumberFromFile(@"The Simpsons/The Simpsons - 02.avi"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumberWithoutSeason8()
+ {
+ Assert.Equal(2, GetEpisodeNumberFromFile(@"The Simpsons/The Simpsons - 02 Ep Name.avi"));
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestEpisodeNumberWithoutSeason9()
+ {
+ Assert.Equal(2, GetEpisodeNumberFromFile(@"The Simpsons/The Simpsons 5 - 02 - Ep Name.avi"));
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestEpisodeNumberWithoutSeason10()
+ {
+ Assert.Equal(2, GetEpisodeNumberFromFile(@"The Simpsons/The Simpsons 5 - 02 Ep Name.avi"));
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestEpisodeNumberWithoutSeason11()
+ {
+ Assert.Equal(7, GetEpisodeNumberFromFile(@"Seinfeld/Seinfeld 0807 The Checks.avi"));
+ Assert.Equal(8, GetSeasonNumberFromFile(@"Seinfeld/Seinfeld 0807 The Checks.avi"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumberWithoutSeason12()
+ {
+ Assert.Equal(7, GetEpisodeNumberFromFile(@"GJ Club (2013)/GJ Club - 07.mkv"));
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestEpisodeNumberWithoutSeason13()
+ {
+ // This is not supported anymore after removing the episode number 365+ hack from EpisodePathParser
+ Assert.Equal(13, GetEpisodeNumberFromFile(@"Case Closed (1996-2007)/Case Closed - 13.mkv"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumberWithoutSeason14()
+ {
+ Assert.Equal(3, GetSeasonNumberFromFile(@"Case Closed (1996-2007)/Case Closed - 317.mkv"));
+ Assert.Equal(17, GetEpisodeNumberFromFile(@"Case Closed (1996-2007)/Case Closed - 317.mkv"));
+ }
+
+ [Fact]
+ public void TestEpisodeNumberWithoutSeason15()
+ {
+ Assert.Equal(2017, GetSeasonNumberFromFile(@"Running Man/Running Man S2017E368.mkv"));
+ }
+
+ private int? GetEpisodeNumberFromFile(string path)
+ {
+ var options = new NamingOptions();
+
+ var result = new EpisodeResolver(options)
+ .Resolve(path, false);
+
+ return result.EpisodeNumber;
+ }
+
+ private int? GetSeasonNumberFromFile(string path)
+ {
+ var options = new NamingOptions();
+
+ var result = new EpisodeResolver(options)
+ .Resolve(path, false);
+
+ return result.SeasonNumber;
+ }
+
+ }
+}
diff --git a/tests/Jellyfin.Naming.Tests/EpisodePathParserTest.cs b/tests/Jellyfin.Naming.Tests/TV/EpisodePathParserTest.cs
index dd1e04215..da6e99310 100644
--- a/tests/Jellyfin.Naming.Tests/EpisodePathParserTest.cs
+++ b/tests/Jellyfin.Naming.Tests/TV/EpisodePathParserTest.cs
@@ -2,7 +2,7 @@ using Emby.Naming.Common;
using Emby.Naming.TV;
using Xunit;
-namespace Jellyfin.Naming.Tests
+namespace Jellyfin.Naming.Tests.TV
{
public class EpisodePathParserTest
{
@@ -11,6 +11,10 @@ namespace Jellyfin.Naming.Tests
[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)]
+ [InlineData("D:\\media\\Foo\\Foo-S01E01", "Foo", 1, 1)]
+ [InlineData("D:\\media\\Foo - S04E011", "Foo", 4, 11)]
+ [InlineData("D:\\media\\Foo\\Foo s01x01", "Foo", 1, 1)]
+ [InlineData("D:\\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();
@@ -21,18 +25,13 @@ namespace Jellyfin.Naming.Tests
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)]
+ [InlineData("D:\\media\\Foo\\Foo 889", "Foo", 889)]
+ [InlineData("D:\\media\\Foo\\[Bar] Foo Baz - 11 [1080p]", "Foo Baz", 11)]
public void ParseEpisodeWithoutSeason(string path, string name, int episode)
{
NamingOptions o = new NamingOptions();
@@ -43,13 +42,6 @@ namespace Jellyfin.Naming.Tests
Assert.Equal(name, res.SeriesName);
Assert.Null(res.SeasonNumber);
Assert.Equal(episode, res.EpisodeNumber);
-
- // testing other paths delimeter
- var res2 = p.Parse(path.Replace('/', '\\'), false, fillExtendedInfo: false);
- Assert.True(res2.Success);
- Assert.Equal(name, res2.SeriesName);
- Assert.Null(res2.SeasonNumber);
- Assert.Equal(episode, res2.EpisodeNumber);
}
}
}
diff --git a/tests/Jellyfin.Naming.Tests/TV/EpisodeWithoutSeasonTests.cs b/tests/Jellyfin.Naming.Tests/TV/EpisodeWithoutSeasonTests.cs
new file mode 100644
index 000000000..c2851ccdb
--- /dev/null
+++ b/tests/Jellyfin.Naming.Tests/TV/EpisodeWithoutSeasonTests.cs
@@ -0,0 +1,56 @@
+using Emby.Naming.Common;
+using Emby.Naming.TV;
+using Xunit;
+
+namespace Jellyfin.Naming.Tests.TV
+{
+ public class EpisodeWithoutSeasonTests
+ {
+ // FIXME
+ // [Fact]
+ public void TestWithoutSeason1()
+ {
+ Test(@"/server/anything_ep02.mp4", "anything", null, 2);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestWithoutSeason2()
+ {
+ Test(@"/server/anything_ep_02.mp4", "anything", null, 2);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestWithoutSeason3()
+ {
+ Test(@"/server/anything_part.II.mp4", "anything", null, null);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestWithoutSeason4()
+ {
+ Test(@"/server/anything_pt.II.mp4", "anything", null, null);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestWithoutSeason5()
+ {
+ Test(@"/server/anything_pt_II.mp4", "anything", null, null);
+ }
+
+ private void Test(string path, string seriesName, int? seasonNumber, int? episodeNumber)
+ {
+ var options = new NamingOptions();
+
+ var result = new EpisodeResolver(options)
+ .Resolve(path, false);
+
+ Assert.Equal(seasonNumber, result.SeasonNumber);
+ Assert.Equal(episodeNumber, result.EpisodeNumber);
+ Assert.Equal(seriesName, result.SeriesName, true);
+ }
+ }
+}
diff --git a/tests/Jellyfin.Naming.Tests/TV/MultiEpisodeTests.cs b/tests/Jellyfin.Naming.Tests/TV/MultiEpisodeTests.cs
new file mode 100644
index 000000000..b15dd6b74
--- /dev/null
+++ b/tests/Jellyfin.Naming.Tests/TV/MultiEpisodeTests.cs
@@ -0,0 +1,105 @@
+using Emby.Naming.Common;
+using Emby.Naming.TV;
+using Xunit;
+
+namespace Jellyfin.Naming.Tests.TV
+{
+ public class MultiEpisodeTests
+ {
+ [Fact]
+ public void TestGetEndingEpisodeNumberFromFile()
+ {
+ Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 1/4x01 – 20 Hours in America (1).mkv"));
+
+ Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 1/01x02 blah.avi"));
+ Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 1/S01x02 blah.avi"));
+ Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 1/S01E02 blah.avi"));
+ Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 1/S01xE02 blah.avi"));
+ Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 1/seriesname 01x02 blah.avi"));
+ Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 1/seriesname S01x02 blah.avi"));
+ Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 1/seriesname S01E02 blah.avi"));
+ Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 1/seriesname S01xE02 blah.avi"));
+ Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 2/02x03 - 04 Ep Name.mp4"));
+ Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 2/My show name 02x03 - 04 Ep Name.mp4"));
+ Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 2/Elementary - 02x03 - 02x04 - 02x15 - Ep Name.mp4"));
+ Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 2/02x03 - 02x04 - 02x15 - Ep Name.mp4"));
+ Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 2/02x03-04-15 - Ep Name.mp4"));
+ Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 2/Elementary - 02x03-04-15 - Ep Name.mp4"));
+ Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 02/02x03-E15 - Ep Name.mp4"));
+ Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 02/Elementary - 02x03-E15 - Ep Name.mp4"));
+ Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 02/02x03 - x04 - x15 - Ep Name.mp4"));
+ Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 02/Elementary - 02x03 - x04 - x15 - Ep Name.mp4"));
+ Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 02/02x03x04x15 - Ep Name.mp4"));
+ Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 02/Elementary - 02x03x04x15 - Ep Name.mp4"));
+ Assert.Equal(26, GetEndingEpisodeNumberFromFile(@"Season 1/Elementary - S01E23-E24-E26 - The Woman.mp4"));
+ Assert.Equal(26, GetEndingEpisodeNumberFromFile(@"Season 1/S01E23-E24-E26 - The Woman.mp4"));
+
+
+ // Four Digits seasons
+ Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 2009/2009x02 blah.avi"));
+ Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 2009/S2009x02 blah.avi"));
+ Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 2009/S2009E02 blah.avi"));
+ Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 2009/S2009xE02 blah.avi"));
+ Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 2009/seriesname 2009x02 blah.avi"));
+ Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 2009/seriesname S2009x02 blah.avi"));
+ Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 2009/seriesname S2009E02 blah.avi"));
+ Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 2009/seriesname S2009xE02 blah.avi"));
+ Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 2009/Elementary - 2009x03 - 2009x04 - 2009x15 - Ep Name.mp4"));
+ Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 2009/2009x03 - 2009x04 - 2009x15 - Ep Name.mp4"));
+ Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 2009/2009x03-04-15 - Ep Name.mp4"));
+ Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 2009/Elementary - 2009x03-04-15 - Ep Name.mp4"));
+ Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 2009/2009x03-E15 - Ep Name.mp4"));
+ Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 2009/Elementary - 2009x03-E15 - Ep Name.mp4"));
+ Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 2009/2009x03 - x04 - x15 - Ep Name.mp4"));
+ Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 2009/Elementary - 2009x03 - x04 - x15 - Ep Name.mp4"));
+ Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 2009/2009x03x04x15 - Ep Name.mp4"));
+ Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 2009/Elementary - 2009x03x04x15 - Ep Name.mp4"));
+ Assert.Equal(26, GetEndingEpisodeNumberFromFile(@"Season 2009/Elementary - S2009E23-E24-E26 - The Woman.mp4"));
+ Assert.Equal(26, GetEndingEpisodeNumberFromFile(@"Season 2009/S2009E23-E24-E26 - The Woman.mp4"));
+
+ // Without season number
+ Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 1/02 - blah.avi"));
+ Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 2/02 - blah 14 blah.avi"));
+ Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 1/02 - blah-02 a.avi"));
+ Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 2/02.avi"));
+
+ Assert.Equal(3, GetEndingEpisodeNumberFromFile(@"Season 1/02-03 - blah.avi"));
+ Assert.Equal(4, GetEndingEpisodeNumberFromFile(@"Season 2/02-04 - blah 14 blah.avi"));
+ Assert.Equal(5, GetEndingEpisodeNumberFromFile(@"Season 1/02-05 - blah-02 a.avi"));
+ Assert.Equal(4, GetEndingEpisodeNumberFromFile(@"Season 2/02-04.avi"));
+ Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 2/[HorribleSubs] Hunter X Hunter - 136 [720p].mkv"));
+
+ // With format specification that must not be detected as ending episode number
+ Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 1/series-s09e14-1080p.mkv"));
+ Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 1/series-s09e14-720p.mkv"));
+ Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 1/series-s09e14-720i.mkv"));
+ Assert.Equal(4, GetEndingEpisodeNumberFromFile(@"Season 1/MOONLIGHTING_s01e01-e04.mkv"));
+ }
+
+ [Fact]
+ public void TestGetEndingEpisodeNumberFromFolder()
+ {
+ Assert.Equal(4, GetEndingEpisodeNumberFromFolder(@"Season 1/MOONLIGHTING_s01e01-e04"));
+ }
+
+ private int? GetEndingEpisodeNumberFromFolder(string path)
+ {
+ var options = new NamingOptions();
+
+ var result = new EpisodePathParser(options)
+ .Parse(path, true);
+
+ return result.EndingEpsiodeNumber;
+ }
+
+ private int? GetEndingEpisodeNumberFromFile(string path)
+ {
+ var options = new NamingOptions();
+
+ var result = new EpisodePathParser(options)
+ .Parse(path, false);
+
+ return result.EndingEpsiodeNumber;
+ }
+ }
+}
diff --git a/tests/Jellyfin.Naming.Tests/TV/SeasonFolderTests.cs b/tests/Jellyfin.Naming.Tests/TV/SeasonFolderTests.cs
new file mode 100644
index 000000000..ffa8d3483
--- /dev/null
+++ b/tests/Jellyfin.Naming.Tests/TV/SeasonFolderTests.cs
@@ -0,0 +1,112 @@
+using Emby.Naming.TV;
+using Xunit;
+
+namespace Jellyfin.Naming.Tests.TV
+{
+ public class SeasonFolderTests
+ {
+ [Fact]
+ public void TestGetSeasonNumberFromPath1()
+ {
+ Assert.Equal(1, GetSeasonNumberFromPath(@"/Drive/Season 1"));
+ }
+
+ [Fact]
+ public void TestGetSeasonNumberFromPath2()
+ {
+ Assert.Equal(2, GetSeasonNumberFromPath(@"/Drive/Season 2"));
+ }
+
+ [Fact]
+ public void TestGetSeasonNumberFromPath3()
+ {
+ Assert.Equal(2, GetSeasonNumberFromPath(@"/Drive/Season 02"));
+ }
+
+ [Fact]
+ public void TestGetSeasonNumberFromPath4()
+ {
+ Assert.Equal(1, GetSeasonNumberFromPath(@"/Drive/Season 1"));
+ }
+
+ [Fact]
+ public void TestGetSeasonNumberFromPath5()
+ {
+ Assert.Equal(2, GetSeasonNumberFromPath(@"/Drive/Seinfeld/S02"));
+ }
+
+ [Fact]
+ public void TestGetSeasonNumberFromPath6()
+ {
+ Assert.Equal(2, GetSeasonNumberFromPath(@"/Drive/Seinfeld/2"));
+ }
+
+ [Fact]
+ public void TestGetSeasonNumberFromPath7()
+ {
+ Assert.Equal(2009, GetSeasonNumberFromPath(@"/Drive/Season 2009"));
+ }
+
+ [Fact]
+ public void TestGetSeasonNumberFromPath8()
+ {
+ Assert.Equal(1, GetSeasonNumberFromPath(@"/Drive/Season1"));
+ }
+
+ [Fact]
+ public void TestGetSeasonNumberFromPath9()
+ {
+ Assert.Equal(4, GetSeasonNumberFromPath(@"The Wonder Years/The.Wonder.Years.S04.PDTV.x264-JCH"));
+ }
+
+ [Fact]
+ public void TestGetSeasonNumberFromPath10()
+ {
+ Assert.Equal(7, GetSeasonNumberFromPath(@"/Drive/Season 7 (2016)"));
+ }
+
+ [Fact]
+ public void TestGetSeasonNumberFromPath11()
+ {
+ Assert.Equal(7, GetSeasonNumberFromPath(@"/Drive/Staffel 7 (2016)"));
+ }
+
+ [Fact]
+ public void TestGetSeasonNumberFromPath12()
+ {
+ Assert.Equal(7, GetSeasonNumberFromPath(@"/Drive/Stagione 7 (2016)"));
+ }
+
+ [Fact]
+ public void TestGetSeasonNumberFromPath14()
+ {
+ Assert.Null(GetSeasonNumberFromPath(@"/Drive/Season (8)"));
+ }
+
+ [Fact]
+ public void TestGetSeasonNumberFromPath13()
+ {
+ Assert.Equal(3, GetSeasonNumberFromPath(@"/Drive/3.Staffel"));
+ }
+
+ [Fact]
+ public void TestGetSeasonNumberFromPath15()
+ {
+ Assert.Null(GetSeasonNumberFromPath(@"/Drive/s06e05"));
+ }
+
+ [Fact]
+ public void TestGetSeasonNumberFromPath16()
+ {
+ Assert.Null(GetSeasonNumberFromPath(@"/Drive/The.Legend.of.Condor.Heroes.2017.V2.web-dl.1080p.h264.aac-hdctv"));
+ }
+
+ private int? GetSeasonNumberFromPath(string path)
+ {
+ var result = new SeasonPathParser()
+ .Parse(path, true, true);
+
+ return result.SeasonNumber;
+ }
+ }
+}
diff --git a/tests/Jellyfin.Naming.Tests/TV/SeasonNumberTests.cs b/tests/Jellyfin.Naming.Tests/TV/SeasonNumberTests.cs
new file mode 100644
index 000000000..ba3c5ecac
--- /dev/null
+++ b/tests/Jellyfin.Naming.Tests/TV/SeasonNumberTests.cs
@@ -0,0 +1,305 @@
+using Emby.Naming.Common;
+using Emby.Naming.TV;
+using Xunit;
+
+namespace Jellyfin.Naming.Tests.TV
+{
+ public class SeasonNumberTests
+ {
+ private int? GetSeasonNumberFromEpisodeFile(string path)
+ {
+ var options = new NamingOptions();
+
+ var result = new EpisodeResolver(options)
+ .Resolve(path, false);
+
+ return result.SeasonNumber;
+ }
+
+ [Fact]
+ public void TestSeasonNumber1()
+ {
+ Assert.Equal(2, GetSeasonNumberFromEpisodeFile(@"/Show/Season 02/S02E03 blah.avi"));
+ }
+
+ [Fact]
+ public void TestSeasonNumber2()
+ {
+ Assert.Equal(1, GetSeasonNumberFromEpisodeFile(@"Season 1/seriesname S01x02 blah.avi"));
+ }
+
+ [Fact]
+ public void TestSeasonNumber3()
+ {
+ Assert.Equal(1, GetSeasonNumberFromEpisodeFile(@"Season 1/S01x02 blah.avi"));
+ }
+
+ [Fact]
+ public void TestSeasonNumber4()
+ {
+ Assert.Equal(1, GetSeasonNumberFromEpisodeFile(@"Season 1/seriesname S01xE02 blah.avi"));
+ }
+
+ [Fact]
+ public void TestSeasonNumber5()
+ {
+ Assert.Equal(1, GetSeasonNumberFromEpisodeFile(@"Season 1/01x02 blah.avi"));
+ }
+
+ [Fact]
+ public void TestSeasonNumber6()
+ {
+ Assert.Equal(1, GetSeasonNumberFromEpisodeFile(@"Season 1/S01E02 blah.avi"));
+ }
+
+ [Fact]
+ public void TestSeasonNumber7()
+ {
+ Assert.Equal(1, GetSeasonNumberFromEpisodeFile(@"Season 1/S01xE02 blah.avi"));
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestSeasonNumber8()
+ {
+ Assert.Equal(1, GetSeasonNumberFromEpisodeFile(@"Season 1/seriesname 01x02 blah.avi"));
+ }
+
+ [Fact]
+ public void TestSeasonNumber9()
+ {
+ Assert.Equal(1, GetSeasonNumberFromEpisodeFile(@"Season 1/seriesname S01x02 blah.avi"));
+ }
+
+ [Fact]
+ public void TestSeasonNumber10()
+ {
+ Assert.Equal(1, GetSeasonNumberFromEpisodeFile(@"Season 1/seriesname S01E02 blah.avi"));
+ }
+
+ [Fact]
+ public void TestSeasonNumber11()
+ {
+ Assert.Equal(2, GetSeasonNumberFromEpisodeFile(@"Season 2/Elementary - 02x03 - 02x04 - 02x15 - Ep Name.mp4"));
+ }
+
+ [Fact]
+ public void TestSeasonNumber12()
+ {
+ Assert.Equal(2, GetSeasonNumberFromEpisodeFile(@"Season 2/02x03 - 02x04 - 02x15 - Ep Name.mp4"));
+ }
+
+ [Fact]
+ public void TestSeasonNumber13()
+ {
+ Assert.Equal(2, GetSeasonNumberFromEpisodeFile(@"Season 2/02x03-04-15 - Ep Name.mp4"));
+ }
+
+ [Fact]
+ public void TestSeasonNumber14()
+ {
+ Assert.Equal(2, GetSeasonNumberFromEpisodeFile(@"Season 2/Elementary - 02x03-04-15 - Ep Name.mp4"));
+ }
+
+ [Fact]
+ public void TestSeasonNumber15()
+ {
+ Assert.Equal(2, GetSeasonNumberFromEpisodeFile(@"Season 02/02x03-E15 - Ep Name.mp4"));
+ }
+
+ [Fact]
+ public void TestSeasonNumber16()
+ {
+ Assert.Equal(2, GetSeasonNumberFromEpisodeFile(@"Season 02/Elementary - 02x03-E15 - Ep Name.mp4"));
+ }
+
+ [Fact]
+ public void TestSeasonNumber17()
+ {
+ Assert.Equal(2, GetSeasonNumberFromEpisodeFile(@"Season 02/02x03 - x04 - x15 - Ep Name.mp4"));
+ }
+
+ [Fact]
+ public void TestSeasonNumber18()
+ {
+ Assert.Equal(2, GetSeasonNumberFromEpisodeFile(@"Season 02/Elementary - 02x03 - x04 - x15 - Ep Name.mp4"));
+ }
+
+ [Fact]
+ public void TestSeasonNumber19()
+ {
+ Assert.Equal(2, GetSeasonNumberFromEpisodeFile(@"Season 02/02x03x04x15 - Ep Name.mp4"));
+ }
+
+ [Fact]
+ public void TestSeasonNumber20()
+ {
+ Assert.Equal(2, GetSeasonNumberFromEpisodeFile(@"Season 02/Elementary - 02x03x04x15 - Ep Name.mp4"));
+ }
+
+ [Fact]
+ public void TestSeasonNumber21()
+ {
+ Assert.Equal(1, GetSeasonNumberFromEpisodeFile(@"Season 1/Elementary - S01E23-E24-E26 - The Woman.mp4"));
+ }
+
+ [Fact]
+ public void TestSeasonNumber22()
+ {
+ Assert.Equal(1, GetSeasonNumberFromEpisodeFile(@"Season 1/S01E23-E24-E26 - The Woman.mp4"));
+ }
+
+ [Fact]
+ public void TestSeasonNumber23()
+ {
+ Assert.Equal(25, GetSeasonNumberFromEpisodeFile(@"Season 25/The Simpsons.S25E09.Steal this episode.mp4"));
+ }
+
+ [Fact]
+ public void TestSeasonNumber24()
+ {
+ Assert.Equal(25, GetSeasonNumberFromEpisodeFile(@"The Simpsons/The Simpsons.S25E09.Steal this episode.mp4"));
+ }
+
+ [Fact]
+ public void TestSeasonNumber25()
+ {
+ Assert.Equal(2016, GetSeasonNumberFromEpisodeFile(@"2016/Season s2016e1.mp4"));
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestSeasonNumber26()
+ {
+ // This convention is not currently supported, just adding in case we want to look at it in the future
+ Assert.Equal(2016, GetSeasonNumberFromEpisodeFile(@"2016/Season 2016x1.mp4"));
+ }
+
+ [Fact]
+ public void TestFourDigitSeasonNumber1()
+ {
+ Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/2009x02 blah.avi"));
+ }
+
+ [Fact]
+ public void TestFourDigitSeasonNumber2()
+ {
+ Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/S2009x02 blah.avi"));
+ }
+
+ [Fact]
+ public void TestFourDigitSeasonNumber3()
+ {
+ Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/S2009E02 blah.avi"));
+ }
+
+ [Fact]
+ public void TestFourDigitSeasonNumber4()
+ {
+ Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/S2009xE02 blah.avi"));
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestFourDigitSeasonNumber5()
+ {
+ Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/seriesname 2009x02 blah.avi"));
+ }
+
+ [Fact]
+ public void TestFourDigitSeasonNumber6()
+ {
+ Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/seriesname S2009x02 blah.avi"));
+ }
+
+ [Fact]
+ public void TestFourDigitSeasonNumber7()
+ {
+ Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/seriesname S2009E02 blah.avi"));
+ }
+
+ [Fact]
+ public void TestFourDigitSeasonNumber8()
+ {
+ Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/Elementary - 2009x03 - 2009x04 - 2009x15 - Ep Name.mp4"));
+ }
+
+ [Fact]
+ public void TestFourDigitSeasonNumber9()
+ {
+ Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/2009x03 - 2009x04 - 2009x15 - Ep Name.mp4"));
+ }
+
+ [Fact]
+ public void TestFourDigitSeasonNumber10()
+ {
+ Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/2009x03-04-15 - Ep Name.mp4"));
+ }
+
+ [Fact]
+ public void TestFourDigitSeasonNumber11()
+ {
+ Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/Elementary - 2009x03 - x04 - x15 - Ep Name.mp4"));
+ }
+
+ [Fact]
+ public void TestFourDigitSeasonNumber12()
+ {
+ Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/2009x03x04x15 - Ep Name.mp4"));
+ }
+
+ [Fact]
+ public void TestFourDigitSeasonNumber13()
+ {
+ Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/Elementary - 2009x03x04x15 - Ep Name.mp4"));
+ }
+
+ [Fact]
+ public void TestFourDigitSeasonNumber14()
+ {
+ Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/Elementary - S2009E23-E24-E26 - The Woman.mp4"));
+ }
+
+ [Fact]
+ public void TestFourDigitSeasonNumber15()
+ {
+ Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/S2009E23-E24-E26 - The Woman.mp4"));
+ }
+
+ [Fact]
+ public void TestFourDigitSeasonNumber16()
+ {
+ Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/Elementary - 2009x03 - x04 - x15 - Ep Name.mp4"));
+ }
+
+ [Fact]
+ public void TestFourDigitSeasonNumber17()
+ {
+ Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/2009x03x04x15 - Ep Name.mp4"));
+ }
+
+ [Fact]
+ public void TestFourDigitSeasonNumber18()
+ {
+ Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/Elementary - 2009x03x04x15 - Ep Name.mp4"));
+ }
+
+ [Fact]
+ public void TestFourDigitSeasonNumber19()
+ {
+ Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/Elementary - S2009E23-E24-E26 - The Woman.mp4"));
+ }
+
+ [Fact]
+ public void TestFourDigitSeasonNumber20()
+ {
+ Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/S2009E23-E24-E26 - The Woman.mp4"));
+ }
+
+ [Fact]
+ public void TestNoSeriesFolder()
+ {
+ Assert.Equal(1, GetSeasonNumberFromEpisodeFile(@"Series/1-12 - The Woman.mp4"));
+ }
+ }
+}
diff --git a/tests/Jellyfin.Naming.Tests/TV/SimpleEpisodeTests.cs b/tests/Jellyfin.Naming.Tests/TV/SimpleEpisodeTests.cs
new file mode 100644
index 000000000..c9323c218
--- /dev/null
+++ b/tests/Jellyfin.Naming.Tests/TV/SimpleEpisodeTests.cs
@@ -0,0 +1,95 @@
+using Emby.Naming.Common;
+using Emby.Naming.TV;
+using Xunit;
+
+namespace Jellyfin.Naming.Tests.TV
+{
+ public class SimpleEpisodeTests
+ {
+ [Fact]
+ public void TestSimpleEpisodePath1()
+ {
+ Test(@"/server/anything_s01e02.mp4", "anything", 1, 2);
+ }
+
+ [Fact]
+ public void TestSimpleEpisodePath2()
+ {
+ Test(@"/server/anything_s1e2.mp4", "anything", 1, 2);
+ }
+
+ [Fact]
+ public void TestSimpleEpisodePath3()
+ {
+ Test(@"/server/anything_s01.e02.mp4", "anything", 1, 2);
+ }
+
+ [Fact]
+ public void TestSimpleEpisodePath4()
+ {
+ Test(@"/server/anything_s01_e02.mp4", "anything", 1, 2);
+ }
+
+ [Fact]
+ public void TestSimpleEpisodePath5()
+ {
+ Test(@"/server/anything_102.mp4", "anything", 1, 2);
+ }
+
+ [Fact]
+ public void TestSimpleEpisodePath6()
+ {
+ Test(@"/server/anything_1x02.mp4", "anything", 1, 2);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestSimpleEpisodePath7()
+ {
+ Test(@"/server/The Walking Dead 4x01.mp4", "The Walking Dead", 4, 1);
+ }
+
+ [Fact]
+ public void TestSimpleEpisodePath8()
+ {
+ Test(@"/server/the_simpsons-s02e01_18536.mp4", "the_simpsons", 2, 1);
+ }
+
+
+ [Fact]
+ public void TestSimpleEpisodePath9()
+ {
+ Test(@"/server/Temp/S01E02 foo.mp4", string.Empty, 1, 2);
+ }
+
+ [Fact]
+ public void TestSimpleEpisodePath10()
+ {
+ Test(@"Series/4-12 - The Woman.mp4", string.Empty, 4, 12);
+ }
+
+ [Fact]
+ public void TestSimpleEpisodePath11()
+ {
+ Test(@"Series/4x12 - The Woman.mp4", string.Empty, 4, 12);
+ }
+
+ [Fact]
+ public void TestSimpleEpisodePath12()
+ {
+ Test(@"Series/LA X, Pt. 1_s06e32.mp4", "LA X, Pt. 1", 6, 32);
+ }
+
+ private void Test(string path, string seriesName, int? seasonNumber, int? episodeNumber)
+ {
+ var options = new NamingOptions();
+
+ var result = new EpisodeResolver(options)
+ .Resolve(path, false);
+
+ Assert.Equal(seasonNumber, result.SeasonNumber);
+ Assert.Equal(episodeNumber, result.EpisodeNumber);
+ Assert.Equal(seriesName, result.SeriesName, true);
+ }
+ }
+}
diff --git a/tests/Jellyfin.Naming.Tests/Video/BaseVideoTest.cs b/tests/Jellyfin.Naming.Tests/Video/BaseVideoTest.cs
new file mode 100644
index 000000000..b993e241c
--- /dev/null
+++ b/tests/Jellyfin.Naming.Tests/Video/BaseVideoTest.cs
@@ -0,0 +1,15 @@
+using Emby.Naming.Common;
+using Emby.Naming.Video;
+
+namespace Jellyfin.Naming.Tests.Video
+{
+ public abstract class BaseVideoTest
+ {
+ protected VideoResolver GetParser()
+ {
+ var options = new NamingOptions();
+
+ return new VideoResolver(options);
+ }
+ }
+}
diff --git a/tests/Jellyfin.Naming.Tests/Video/CleanDateTimeTests.cs b/tests/Jellyfin.Naming.Tests/Video/CleanDateTimeTests.cs
new file mode 100644
index 000000000..bba73ad91
--- /dev/null
+++ b/tests/Jellyfin.Naming.Tests/Video/CleanDateTimeTests.cs
@@ -0,0 +1,143 @@
+using System.IO;
+using Xunit;
+
+namespace Jellyfin.Naming.Tests.Video
+{
+ public class CleanDateTimeTests : BaseVideoTest
+ {
+ // FIXME
+ // [Fact]
+ public void TestCleanDateTime()
+ {
+ Test(@"The Wolf of Wall Street (2013).mkv", "The Wolf of Wall Street", 2013);
+ Test(@"The Wolf of Wall Street 2 (2013).mkv", "The Wolf of Wall Street 2", 2013);
+ Test(@"The Wolf of Wall Street - 2 (2013).mkv", "The Wolf of Wall Street - 2", 2013);
+ Test(@"The Wolf of Wall Street 2001 (2013).mkv", "The Wolf of Wall Street 2001", 2013);
+
+ Test(@"300 (2006).mkv", "300", 2006);
+ Test(@"d:/movies/300 (2006).mkv", "300", 2006);
+ Test(@"300 2 (2006).mkv", "300 2", 2006);
+ Test(@"300 - 2 (2006).mkv", "300 - 2", 2006);
+ Test(@"300 2001 (2006).mkv", "300 2001", 2006);
+
+ Test(@"curse.of.chucky.2013.stv.unrated.multi.1080p.bluray.x264-rough", "curse.of.chucky", 2013);
+ Test(@"curse.of.chucky.2013.stv.unrated.multi.2160p.bluray.x264-rough", "curse.of.chucky", 2013);
+
+ Test(@"/server/Movies/300 (2007)/300 (2006).bluray.disc", "300", 2006);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestCleanDateTime1()
+ {
+ Test(@"Arrival.2016.2160p.Blu-Ray.HEVC.mkv", "Arrival", 2016);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestCleanDateTimeWithoutFileExtension()
+ {
+ Test(@"The Wolf of Wall Street (2013)", "The Wolf of Wall Street", 2013);
+ Test(@"The Wolf of Wall Street 2 (2013)", "The Wolf of Wall Street 2", 2013);
+ Test(@"The Wolf of Wall Street - 2 (2013)", "The Wolf of Wall Street - 2", 2013);
+ Test(@"The Wolf of Wall Street 2001 (2013)", "The Wolf of Wall Street 2001", 2013);
+
+ Test(@"300 (2006)", "300", 2006);
+ Test(@"d:/movies/300 (2006)", "300", 2006);
+ Test(@"300 2 (2006)", "300 2", 2006);
+ Test(@"300 - 2 (2006)", "300 - 2", 2006);
+ Test(@"300 2001 (2006)", "300 2001", 2006);
+
+ Test(@"/server/Movies/300 (2007)/300 (2006)", "300", 2006);
+ Test(@"/server/Movies/300 (2007)/300 (2006).mkv", "300", 2006);
+ }
+
+ [Fact]
+ public void TestCleanDateTimeWithoutDate()
+ {
+ Test(@"American.Psycho.mkv", "American.Psycho.mkv", null);
+ Test(@"American Psycho.mkv", "American Psycho.mkv", null);
+ }
+
+ [Fact]
+ public void TestCleanDateTimeWithBracketedName()
+ {
+ Test(@"[rec].mkv", "[rec].mkv", null);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestCleanDateTimeWithoutExtension()
+ {
+ Test(@"St. Vincent (2014)", "St. Vincent", 2014);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestCleanDateTimeWithoutDate1()
+ {
+ Test("Super movie(2009).mp4", "Super movie", 2009);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestCleanDateTimeWithoutParenthesis()
+ {
+ Test("Drug War 2013.mp4", "Drug War", 2013);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestCleanDateTimeWithMultipleYears()
+ {
+ Test("My Movie (1997) - GreatestReleaseGroup 2019.mp4", "My Movie", 1997);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestCleanDateTimeWithYearAndResolution()
+ {
+ Test("First Man 2018 1080p.mkv", "First Man", 2018);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestCleanDateTimeWithYearAndResolution1()
+ {
+ Test("First Man (2018) 1080p.mkv", "First Man", 2018);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestCleanDateTimeWithSceneRelease()
+ {
+ Test("Maximum Ride - 2016 - WEBDL-1080p - x264 AC3.mkv", "Maximum Ride", 2016);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestYearInBrackets()
+ {
+ Test("Robin Hood [Multi-Subs] [2018].mkv", "Robin Hood", 2018);
+ }
+
+ private void Test(string input, string expectedName, int? expectedYear)
+ {
+ input = Path.GetFileName(input);
+
+ var result = GetParser().CleanDateTime(input);
+
+ Assert.Equal(expectedName, result.Name, true);
+ Assert.Equal(expectedYear, result.Year);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestCleanDateAndStringsSequence()
+ {
+ // In this test case, running CleanDateTime first produces no date, so it will attempt to run CleanString first and then CleanDateTime again
+
+ Test(@"3.Days.to.Kill.2014.720p.BluRay.x264.YIFY.mkv", "3.Days.to.Kill", 2014);
+ }
+ }
+}
diff --git a/tests/Jellyfin.Naming.Tests/Video/CleanStringTests.cs b/tests/Jellyfin.Naming.Tests/Video/CleanStringTests.cs
new file mode 100644
index 000000000..cd90ac236
--- /dev/null
+++ b/tests/Jellyfin.Naming.Tests/Video/CleanStringTests.cs
@@ -0,0 +1,133 @@
+using System;
+using System.Globalization;
+using Xunit;
+
+namespace Jellyfin.Naming.Tests.Video
+{
+ public class CleanStringTests : BaseVideoTest
+ {
+ // FIXME
+ // [Fact]
+ public void TestCleanString()
+ {
+ Test("Super movie 480p.mp4", "Super movie");
+ Test("Super movie 480p 2001.mp4", "Super movie");
+ Test("Super movie [480p].mp4", "Super movie");
+ Test("480 Super movie [tmdbid=12345].mp4", "480 Super movie");
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestCleanString1()
+ {
+ Test("Super movie(2009).mp4", "Super movie(2009).mp4");
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestCleanString2()
+ {
+ Test("Run lola run (lola rennt) (2009).mp4", "Run lola run (lola rennt) (2009).mp4");
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestStringWithoutDate()
+ {
+ Test(@"American.Psycho.mkv", "American.Psycho.mkv");
+ Test(@"American Psycho.mkv", "American Psycho.mkv");
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestNameWithBrackets()
+ {
+ Test(@"[rec].mkv", "[rec].mkv");
+ }
+
+ // FIXME
+ // [Fact]
+ public void Test4k()
+ {
+ Test("Crouching.Tiger.Hidden.Dragon.4k.mkv", "Crouching.Tiger.Hidden.Dragon");
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestUltraHd()
+ {
+ Test("Crouching.Tiger.Hidden.Dragon.UltraHD.mkv", "Crouching.Tiger.Hidden.Dragon");
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestUHd()
+ {
+ Test("Crouching.Tiger.Hidden.Dragon.UHD.mkv", "Crouching.Tiger.Hidden.Dragon");
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestHDR()
+ {
+ Test("Crouching.Tiger.Hidden.Dragon.HDR.mkv", "Crouching.Tiger.Hidden.Dragon");
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestHDC()
+ {
+ Test("Crouching.Tiger.Hidden.Dragon.HDC.mkv", "Crouching.Tiger.Hidden.Dragon");
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestHDC1()
+ {
+ Test("Crouching.Tiger.Hidden.Dragon-HDC.mkv", "Crouching.Tiger.Hidden.Dragon");
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestBDrip()
+ {
+ Test("Crouching.Tiger.Hidden.Dragon.BDrip.mkv", "Crouching.Tiger.Hidden.Dragon");
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestBDripHDC()
+ {
+ Test("Crouching.Tiger.Hidden.Dragon.BDrip-HDC.mkv", "Crouching.Tiger.Hidden.Dragon");
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestMulti()
+ {
+ Test("Crouching.Tiger.Hidden.Dragon.4K.UltraHD.HDR.BDrip-HDC.mkv", "Crouching.Tiger.Hidden.Dragon");
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestLeadingBraces()
+ {
+ // Not actually supported, just reported by a user
+ Test("[0004] - After The Sunset.el.mkv", "After The Sunset");
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestTrailingBraces()
+ {
+ Test("After The Sunset - [0004].mkv", "After The Sunset");
+ }
+
+ private void Test(string input, string expectedName)
+ {
+ var result = GetParser().CleanString(input).ToString();
+
+ Assert.Equal(expectedName, result, true);
+ }
+ }
+}
diff --git a/tests/Jellyfin.Naming.Tests/Video/ExtraTests.cs b/tests/Jellyfin.Naming.Tests/Video/ExtraTests.cs
new file mode 100644
index 000000000..1646237a0
--- /dev/null
+++ b/tests/Jellyfin.Naming.Tests/Video/ExtraTests.cs
@@ -0,0 +1,77 @@
+using Emby.Naming.Common;
+using Emby.Naming.Video;
+using MediaBrowser.Model.Entities;
+using Xunit;
+
+namespace Jellyfin.Naming.Tests.Video
+{
+ public class ExtraTests : BaseVideoTest
+ {
+ // Requirements
+ // movie-deleted = ExtraType deletedscene
+
+ // All of the above rules should be configurable through the options objects (ideally, even the ExtraTypes)
+
+ [Fact]
+ public void TestKodiExtras()
+ {
+ var videoOptions = new NamingOptions();
+
+ Test("trailer.mp4", ExtraType.Trailer, videoOptions);
+ Test("300-trailer.mp4", ExtraType.Trailer, videoOptions);
+
+ Test("theme.mp3", ExtraType.ThemeSong, videoOptions);
+ }
+
+ [Fact]
+ public void TestExpandedExtras()
+ {
+ var videoOptions = new NamingOptions();
+
+ Test("trailer.mp4", ExtraType.Trailer, videoOptions);
+ Test("trailer.mp3", null, videoOptions);
+ Test("300-trailer.mp4", ExtraType.Trailer, videoOptions);
+
+ Test("theme.mp3", ExtraType.ThemeSong, videoOptions);
+ Test("theme.mkv", null, videoOptions);
+
+ Test("300-scene.mp4", ExtraType.Scene, videoOptions);
+ Test("300-scene2.mp4", ExtraType.Scene, videoOptions);
+ Test("300-clip.mp4", ExtraType.Clip, videoOptions);
+
+ Test("300-deleted.mp4", ExtraType.DeletedScene, videoOptions);
+ Test("300-deletedscene.mp4", ExtraType.DeletedScene, videoOptions);
+ Test("300-interview.mp4", ExtraType.Interview, videoOptions);
+ Test("300-behindthescenes.mp4", ExtraType.BehindTheScenes, videoOptions);
+ }
+
+ [Fact]
+ public void TestSample()
+ {
+ var videoOptions = new NamingOptions();
+
+ Test("300-sample.mp4", ExtraType.Sample, videoOptions);
+ }
+
+ private void Test(string input, ExtraType? expectedType, NamingOptions videoOptions)
+ {
+ var parser = GetExtraTypeParser(videoOptions);
+
+ var extraType = parser.GetExtraInfo(input).ExtraType;
+
+ if (expectedType == null)
+ {
+ Assert.Null(extraType);
+ }
+ else
+ {
+ Assert.Equal(expectedType, extraType);
+ }
+ }
+
+ private ExtraResolver GetExtraTypeParser(NamingOptions videoOptions)
+ {
+ return new ExtraResolver(videoOptions);
+ }
+ }
+}
diff --git a/tests/Jellyfin.Naming.Tests/Video/Format3DTests.cs b/tests/Jellyfin.Naming.Tests/Video/Format3DTests.cs
new file mode 100644
index 000000000..ed3112936
--- /dev/null
+++ b/tests/Jellyfin.Naming.Tests/Video/Format3DTests.cs
@@ -0,0 +1,78 @@
+using Emby.Naming.Common;
+using Emby.Naming.Video;
+using Xunit;
+
+namespace Jellyfin.Naming.Tests.Video
+{
+ public class Format3DTests : BaseVideoTest
+ {
+ [Fact]
+ public void TestKodiFormat3D()
+ {
+ var options = new NamingOptions();
+
+ Test("Super movie.3d.mp4", false, null, options);
+ Test("Super movie.3d.hsbs.mp4", true, "hsbs", options);
+ Test("Super movie.3d.sbs.mp4", true, "sbs", options);
+ Test("Super movie.3d.htab.mp4", true, "htab", options);
+ Test("Super movie.3d.tab.mp4", true, "tab", options);
+ Test("Super movie 3d hsbs.mp4", true, "hsbs", options);
+ }
+
+ [Fact]
+ public void Test3DName()
+ {
+ var result =
+ GetParser().ResolveFile(@"C:/Users/media/Desktop/Video Test/Movies/Oblivion/Oblivion.3d.hsbs.mkv");
+
+ Assert.Equal("hsbs", result.Format3D);
+ Assert.Equal("Oblivion", result.Name);
+ }
+
+ [Fact]
+ public void TestExpandedFormat3D()
+ {
+ // These were introduced for Media Browser 3
+ // Kodi conventions are preferred but these still need to be supported
+ var options = new NamingOptions();
+
+ Test("Super movie.3d.mp4", false, null, options);
+ Test("Super movie.3d.hsbs.mp4", true, "hsbs", options);
+ Test("Super movie.3d.sbs.mp4", true, "sbs", options);
+ Test("Super movie.3d.htab.mp4", true, "htab", options);
+ Test("Super movie.3d.tab.mp4", true, "tab", options);
+
+ Test("Super movie.hsbs.mp4", true, "hsbs", options);
+ Test("Super movie.sbs.mp4", true, "sbs", options);
+ Test("Super movie.htab.mp4", true, "htab", options);
+ Test("Super movie.tab.mp4", true, "tab", options);
+ Test("Super movie.sbs3d.mp4", true, "sbs3d", options);
+ Test("Super movie.3d.mvc.mp4", true, "mvc", options);
+
+ Test("Super movie [3d].mp4", false, null, options);
+ Test("Super movie [hsbs].mp4", true, "hsbs", options);
+ Test("Super movie [fsbs].mp4", true, "fsbs", options);
+ Test("Super movie [ftab].mp4", true, "ftab", options);
+ Test("Super movie [htab].mp4", true, "htab", options);
+ Test("Super movie [sbs3d].mp4", true, "sbs3d", options);
+ }
+
+ private void Test(string input, bool is3D, string format3D, NamingOptions options)
+ {
+ var parser = new Format3DParser(options);
+
+ var result = parser.Parse(input);
+
+ Assert.Equal(is3D, result.Is3D);
+
+ if (format3D == null)
+ {
+ Assert.Null(result.Format3D);
+ }
+ else
+ {
+ Assert.Equal(format3D, result.Format3D, true);
+ }
+ }
+ }
+}
diff --git a/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs b/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs
new file mode 100644
index 000000000..b8674ec49
--- /dev/null
+++ b/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs
@@ -0,0 +1,438 @@
+using System.Linq;
+using Emby.Naming.Common;
+using Emby.Naming.Video;
+using MediaBrowser.Model.IO;
+using Xunit;
+
+namespace Jellyfin.Naming.Tests.Video
+{
+ public class MultiVersionTests
+ {
+ // FIXME
+ // [Fact]
+ public void TestMultiEdition1()
+ {
+ var files = new[]
+ {
+ @"/movies/X-Men Days of Future Past/X-Men Days of Future Past - 1080p.mkv",
+ @"/movies/X-Men Days of Future Past/X-Men Days of Future Past-trailer.mp4",
+ @"/movies/X-Men Days of Future Past/X-Men Days of Future Past - [hsbs].mkv",
+ @"/movies/X-Men Days of Future Past/X-Men Days of Future Past [hsbs].mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Single(result);
+ Assert.Single(result[0].Extras);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestMultiEdition2()
+ {
+ var files = new[]
+ {
+ @"/movies/X-Men Days of Future Past/X-Men Days of Future Past - apple.mkv",
+ @"/movies/X-Men Days of Future Past/X-Men Days of Future Past-trailer.mp4",
+ @"/movies/X-Men Days of Future Past/X-Men Days of Future Past - banana.mkv",
+ @"/movies/X-Men Days of Future Past/X-Men Days of Future Past [banana].mp4"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Single(result);
+ Assert.Single(result[0].Extras);
+ Assert.Equal(2, result[0].AlternateVersions.Count);
+ }
+
+ [Fact]
+ public void TestMultiEdition3()
+ {
+ // This is currently not supported and will fail, but we should try to figure it out
+ var files = new[]
+ {
+ @"/movies/The Phantom of the Opera (1925)/The Phantom of the Opera (1925) - 1925 version.mkv",
+ @"/movies/The Phantom of the Opera (1925)/The Phantom of the Opera (1925) - 1929 version.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Single(result);
+ Assert.Single(result[0].AlternateVersions);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestLetterFolders()
+ {
+ var files = new[]
+ {
+ @"/movies/M/Movie 1.mkv",
+ @"/movies/M/Movie 2.mkv",
+ @"/movies/M/Movie 3.mkv",
+ @"/movies/M/Movie 4.mkv",
+ @"/movies/M/Movie 5.mkv",
+ @"/movies/M/Movie 6.mkv",
+ @"/movies/M/Movie 7.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Equal(7, result.Count);
+ Assert.Empty(result[0].Extras);
+ Assert.Empty(result[0].AlternateVersions);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestMultiVersionLimit()
+ {
+ var files = new[]
+ {
+ @"/movies/Movie/Movie.mkv",
+ @"/movies/Movie/Movie-2.mkv",
+ @"/movies/Movie/Movie-3.mkv",
+ @"/movies/Movie/Movie-4.mkv",
+ @"/movies/Movie/Movie-5.mkv",
+ @"/movies/Movie/Movie-6.mkv",
+ @"/movies/Movie/Movie-7.mkv",
+ @"/movies/Movie/Movie-8.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Single(result);
+ Assert.Empty(result[0].Extras);
+ Assert.Equal(7, result[0].AlternateVersions.Count);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestMultiVersionLimit2()
+ {
+ var files = new[]
+ {
+ @"/movies/Mo/Movie 1.mkv",
+ @"/movies/Mo/Movie 2.mkv",
+ @"/movies/Mo/Movie 3.mkv",
+ @"/movies/Mo/Movie 4.mkv",
+ @"/movies/Mo/Movie 5.mkv",
+ @"/movies/Mo/Movie 6.mkv",
+ @"/movies/Mo/Movie 7.mkv",
+ @"/movies/Mo/Movie 8.mkv",
+ @"/movies/Mo/Movie 9.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Equal(9, result.Count);
+ Assert.Empty(result[0].Extras);
+ Assert.Empty(result[0].AlternateVersions);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestMultiVersion3()
+ {
+ var files = new[]
+ {
+ @"/movies/Movie/Movie 1.mkv",
+ @"/movies/Movie/Movie 2.mkv",
+ @"/movies/Movie/Movie 3.mkv",
+ @"/movies/Movie/Movie 4.mkv",
+ @"/movies/Movie/Movie 5.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Equal(5, result.Count);
+ Assert.Empty(result[0].Extras);
+ Assert.Empty(result[0].AlternateVersions);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestMultiVersion4()
+ {
+ // Test for false positive
+
+ var files = new[]
+ {
+ @"/movies/Iron Man/Iron Man.mkv",
+ @"/movies/Iron Man/Iron Man (2008).mkv",
+ @"/movies/Iron Man/Iron Man (2009).mkv",
+ @"/movies/Iron Man/Iron Man (2010).mkv",
+ @"/movies/Iron Man/Iron Man (2011).mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Equal(5, result.Count);
+ Assert.Empty(result[0].Extras);
+ Assert.Empty(result[0].AlternateVersions);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestMultiVersion5()
+ {
+ var files = new[]
+ {
+ @"/movies/Iron Man/Iron Man.mkv",
+ @"/movies/Iron Man/Iron Man-720p.mkv",
+ @"/movies/Iron Man/Iron Man-test.mkv",
+ @"/movies/Iron Man/Iron Man-bluray.mkv",
+ @"/movies/Iron Man/Iron Man-3d.mkv",
+ @"/movies/Iron Man/Iron Man-3d-hsbs.mkv",
+ @"/movies/Iron Man/Iron Man-3d.hsbs.mkv",
+ @"/movies/Iron Man/Iron Man[test].mkv",
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Single(result);
+ Assert.Empty(result[0].Extras);
+ Assert.Equal(7, result[0].AlternateVersions.Count);
+ Assert.False(result[0].AlternateVersions[2].Is3D);
+ Assert.True(result[0].AlternateVersions[3].Is3D);
+ Assert.True(result[0].AlternateVersions[4].Is3D);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestMultiVersion6()
+ {
+ var files = new[]
+ {
+ @"/movies/Iron Man/Iron Man.mkv",
+ @"/movies/Iron Man/Iron Man - 720p.mkv",
+ @"/movies/Iron Man/Iron Man - test.mkv",
+ @"/movies/Iron Man/Iron Man - bluray.mkv",
+ @"/movies/Iron Man/Iron Man - 3d.mkv",
+ @"/movies/Iron Man/Iron Man - 3d-hsbs.mkv",
+ @"/movies/Iron Man/Iron Man - 3d.hsbs.mkv",
+ @"/movies/Iron Man/Iron Man [test].mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Single(result);
+ Assert.Empty(result[0].Extras);
+ Assert.Equal(7, result[0].AlternateVersions.Count);
+ Assert.False(result[0].AlternateVersions[3].Is3D);
+ Assert.True(result[0].AlternateVersions[4].Is3D);
+ Assert.True(result[0].AlternateVersions[5].Is3D);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestMultiVersion7()
+ {
+ var files = new[]
+ {
+ @"/movies/Iron Man/Iron Man - B (2006).mkv",
+ @"/movies/Iron Man/Iron Man - C (2007).mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Equal(2, result.Count);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestMultiVersion8()
+ {
+ // This is not actually supported yet
+
+ var files = new[]
+ {
+ @"/movies/Iron Man/Iron Man.mkv",
+ @"/movies/Iron Man/Iron Man_720p.mkv",
+ @"/movies/Iron Man/Iron Man_test.mkv",
+ @"/movies/Iron Man/Iron Man_bluray.mkv",
+ @"/movies/Iron Man/Iron Man_3d.mkv",
+ @"/movies/Iron Man/Iron Man_3d-hsbs.mkv",
+ @"/movies/Iron Man/Iron Man_3d.hsbs.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Single(result);
+ Assert.Empty(result[0].Extras);
+ Assert.Equal(6, result[0].AlternateVersions.Count);
+ Assert.False(result[0].AlternateVersions[2].Is3D);
+ Assert.True(result[0].AlternateVersions[3].Is3D);
+ Assert.True(result[0].AlternateVersions[4].Is3D);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestMultiVersion9()
+ {
+ // Test for false positive
+
+ var files = new[]
+ {
+ @"/movies/Iron Man/Iron Man (2007).mkv",
+ @"/movies/Iron Man/Iron Man (2008).mkv",
+ @"/movies/Iron Man/Iron Man (2009).mkv",
+ @"/movies/Iron Man/Iron Man (2010).mkv",
+ @"/movies/Iron Man/Iron Man (2011).mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Equal(5, result.Count);
+ Assert.Empty(result[0].Extras);
+ Assert.Empty(result[0].AlternateVersions);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestMultiVersion10()
+ {
+ var files = new[]
+ {
+ @"/movies/Blade Runner (1982)/Blade Runner (1982) [Final Cut] [1080p HEVC AAC].mkv",
+ @"/movies/Blade Runner (1982)/Blade Runner (1982) [EE by ADM] [480p HEVC AAC,AAC,AAC].mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Single(result);
+ Assert.Empty(result[0].Extras);
+ Assert.Single(result[0].AlternateVersions);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestMultiVersion11()
+ {
+ // Currently not supported but we should probably handle this.
+
+ var files = new[]
+ {
+ @"/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016) [1080p] Blu-ray.x264.DTS.mkv",
+ @"/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016) [2160p] Blu-ray.x265.AAC.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Single(result);
+ Assert.Empty(result[0].Extras);
+ Assert.Single(result[0].AlternateVersions);
+ }
+
+ private VideoListResolver GetResolver()
+ {
+ var options = new NamingOptions();
+ return new VideoListResolver(options);
+ }
+ }
+}
diff --git a/tests/Jellyfin.Naming.Tests/Video/StackTests.cs b/tests/Jellyfin.Naming.Tests/Video/StackTests.cs
new file mode 100644
index 000000000..5faef0e3d
--- /dev/null
+++ b/tests/Jellyfin.Naming.Tests/Video/StackTests.cs
@@ -0,0 +1,478 @@
+using Emby.Naming.Common;
+using Emby.Naming.Video;
+using MediaBrowser.Model.IO;
+using Xunit;
+
+namespace Jellyfin.Naming.Tests.Video
+{
+ public class StackTests : BaseVideoTest
+ {
+ [Fact]
+ public void TestSimpleStack()
+ {
+ var files = new[]
+ {
+ "Bad Boys (2006) part1.mkv",
+ "Bad Boys (2006) part2.mkv",
+ "Bad Boys (2006) part3.mkv",
+ "Bad Boys (2006) part4.mkv",
+ "Bad Boys (2006)-trailer.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.ResolveFiles(files);
+
+ Assert.Single(result.Stacks);
+ TestStackInfo(result.Stacks[0], "Bad Boys (2006)", 4);
+ }
+
+ [Fact]
+ public void TestFalsePositives()
+ {
+ var files = new[]
+ {
+ "Bad Boys (2006).mkv",
+ "Bad Boys (2007).mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.ResolveFiles(files);
+
+ Assert.Empty(result.Stacks);
+ }
+
+ [Fact]
+ public void TestFalsePositives2()
+ {
+ var files = new[]
+ {
+ "Bad Boys 2006.mkv",
+ "Bad Boys 2007.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.ResolveFiles(files);
+
+ Assert.Empty(result.Stacks);
+ }
+
+ [Fact]
+ public void TestFalsePositives3()
+ {
+ var files = new[]
+ {
+ "300 (2006).mkv",
+ "300 (2007).mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.ResolveFiles(files);
+
+ Assert.Empty(result.Stacks);
+ }
+
+ [Fact]
+ public void TestFalsePositives4()
+ {
+ var files = new[]
+ {
+ "300 2006.mkv",
+ "300 2007.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.ResolveFiles(files);
+
+ Assert.Empty(result.Stacks);
+ }
+
+ [Fact]
+ public void TestFalsePositives5()
+ {
+ var files = new[]
+ {
+ "Star Trek 1 - The motion picture.mkv",
+ "Star Trek 2- The wrath of khan.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.ResolveFiles(files);
+
+ Assert.Empty(result.Stacks);
+ }
+
+ [Fact]
+ public void TestFalsePositives6()
+ {
+ var files = new[]
+ {
+ "Red Riding in the Year of Our Lord 1983 (2009).mkv",
+ "Red Riding in the Year of Our Lord 1980 (2009).mkv",
+ "Red Riding in the Year of Our Lord 1974 (2009).mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.ResolveFiles(files);
+
+ Assert.Empty(result.Stacks);
+ }
+
+ [Fact]
+ public void TestStackName()
+ {
+ var files = new[]
+ {
+ "d:/movies/300 2006 part1.mkv",
+ "d:/movies/300 2006 part2.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.ResolveFiles(files);
+
+ Assert.Single(result.Stacks);
+ TestStackInfo(result.Stacks[0], "300 2006", 2);
+ }
+
+ [Fact]
+ public void TestDirtyNames()
+ {
+ var files = new[]
+ {
+ "Bad Boys (2006).part1.stv.unrated.multi.1080p.bluray.x264-rough.mkv",
+ "Bad Boys (2006).part2.stv.unrated.multi.1080p.bluray.x264-rough.mkv",
+ "Bad Boys (2006).part3.stv.unrated.multi.1080p.bluray.x264-rough.mkv",
+ "Bad Boys (2006).part4.stv.unrated.multi.1080p.bluray.x264-rough.mkv",
+ "Bad Boys (2006)-trailer.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.ResolveFiles(files);
+
+ Assert.Single(result.Stacks);
+ TestStackInfo(result.Stacks[0], "Bad Boys (2006).stv.unrated.multi.1080p.bluray.x264-rough", 4);
+ }
+
+ [Fact]
+ public void TestNumberedFiles()
+ {
+ var files = new[]
+ {
+ "Bad Boys (2006).mkv",
+ "Bad Boys (2006) 1.mkv",
+ "Bad Boys (2006) 2.mkv",
+ "Bad Boys (2006) 3.mkv",
+ "Bad Boys (2006)-trailer.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.ResolveFiles(files);
+
+ Assert.Empty(result.Stacks);
+ }
+
+ [Fact]
+ public void TestSimpleStackWithNumericName()
+ {
+ var files = new[]
+ {
+ "300 (2006) part1.mkv",
+ "300 (2006) part2.mkv",
+ "300 (2006) part3.mkv",
+ "300 (2006) part4.mkv",
+ "300 (2006)-trailer.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.ResolveFiles(files);
+
+ Assert.Single(result.Stacks);
+ TestStackInfo(result.Stacks[0], "300 (2006)", 4);
+ }
+
+ [Fact]
+ public void TestMixedExpressionsNotAllowed()
+ {
+ var files = new[]
+ {
+ "Bad Boys (2006) part1.mkv",
+ "Bad Boys (2006) part2.mkv",
+ "Bad Boys (2006) part3.mkv",
+ "Bad Boys (2006) parta.mkv",
+ "Bad Boys (2006)-trailer.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.ResolveFiles(files);
+
+ Assert.Single(result.Stacks);
+ TestStackInfo(result.Stacks[0], "Bad Boys (2006)", 3);
+ }
+
+ [Fact]
+ public void TestDualStacks()
+ {
+ var files = new[]
+ {
+ "Bad Boys (2006) part1.mkv",
+ "Bad Boys (2006) part2.mkv",
+ "Bad Boys (2006) part3.mkv",
+ "Bad Boys (2006) part4.mkv",
+ "Bad Boys (2006)-trailer.mkv",
+ "300 (2006) part1.mkv",
+ "300 (2006) part2.mkv",
+ "300 (2006) part3.mkv",
+ "300 (2006)-trailer.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.ResolveFiles(files);
+
+ Assert.Equal(2, result.Stacks.Count);
+ TestStackInfo(result.Stacks[1], "Bad Boys (2006)", 4);
+ TestStackInfo(result.Stacks[0], "300 (2006)", 3);
+ }
+
+ [Fact]
+ public void TestDirectories()
+ {
+ var files = new[]
+ {
+ "blah blah - cd 1",
+ "blah blah - cd 2"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.ResolveDirectories(files);
+
+ Assert.Single(result.Stacks);
+ TestStackInfo(result.Stacks[0], "blah blah", 2);
+ }
+
+ [Fact]
+ public void TestFalsePositive()
+ {
+ var files = new[]
+ {
+ "300a.mkv",
+ "300b.mkv",
+ "300c.mkv",
+ "300-trailer.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.ResolveFiles(files);
+
+ Assert.Single(result.Stacks);
+
+ TestStackInfo(result.Stacks[0], "300", 3);
+ }
+
+ [Fact]
+ public void TestFailSequence()
+ {
+ var files = new[]
+ {
+ "300 part1.mkv",
+ "300 part2.mkv",
+ "Avatar",
+ "Avengers part1.mkv",
+ "Avengers part2.mkv",
+ "Avengers part3.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.ResolveFiles(files);
+
+ Assert.Equal(2, result.Stacks.Count);
+
+ TestStackInfo(result.Stacks[0], "300", 2);
+ TestStackInfo(result.Stacks[1], "Avengers", 3);
+ }
+
+ [Fact]
+ public void TestMixedExpressions()
+ {
+ var files = new[]
+ {
+ "Bad Boys (2006) part1.mkv",
+ "Bad Boys (2006) part2.mkv",
+ "Bad Boys (2006) part3.mkv",
+ "Bad Boys (2006) part4.mkv",
+ "Bad Boys (2006)-trailer.mkv",
+ "300 (2006) parta.mkv",
+ "300 (2006) partb.mkv",
+ "300 (2006) partc.mkv",
+ "300 (2006) partd.mkv",
+ "300 (2006)-trailer.mkv",
+ "300a.mkv",
+ "300b.mkv",
+ "300c.mkv",
+ "300-trailer.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.ResolveFiles(files);
+
+ Assert.Equal(3, result.Stacks.Count);
+
+ TestStackInfo(result.Stacks[0], "300 (2006)", 4);
+ TestStackInfo(result.Stacks[1], "300", 3);
+ TestStackInfo(result.Stacks[2], "Bad Boys (2006)", 4);
+ }
+
+ [Fact]
+ public void TestAlphaLimitOfFour()
+ {
+ var files = new[]
+ {
+ "300 (2006) parta.mkv",
+ "300 (2006) partb.mkv",
+ "300 (2006) partc.mkv",
+ "300 (2006) partd.mkv",
+ "300 (2006) parte.mkv",
+ "300 (2006) partf.mkv",
+ "300 (2006) partg.mkv",
+ "300 (2006)-trailer.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.ResolveFiles(files);
+
+ Assert.Single(result.Stacks);
+
+ TestStackInfo(result.Stacks[0], "300 (2006)", 4);
+ }
+
+ [Fact]
+ public void TestMixed()
+ {
+ var files = new[]
+ {
+ new FileSystemMetadata{FullName = "Bad Boys (2006) part1.mkv", IsDirectory = false},
+ new FileSystemMetadata{FullName = "Bad Boys (2006) part2.mkv", IsDirectory = false},
+ new FileSystemMetadata{FullName = "300 (2006) part2", IsDirectory = true},
+ new FileSystemMetadata{FullName = "300 (2006) part3", IsDirectory = true},
+ new FileSystemMetadata{FullName = "300 (2006) part1", IsDirectory = true}
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files);
+
+ Assert.Equal(2, result.Stacks.Count);
+ TestStackInfo(result.Stacks[0], "300 (2006)", 3);
+ TestStackInfo(result.Stacks[1], "Bad Boys (2006)", 2);
+ }
+
+ [Fact]
+ public void TestDirectories2()
+ {
+ //TestDirectory(@"blah blah", false, @"blah blah");
+ //TestDirectory(@"d:/music/weezer/03 Pinkerton", false, "03 Pinkerton");
+ //TestDirectory(@"d:/music/michael jackson/Bad (2012 Remaster)", false, "Bad (2012 Remaster)");
+
+ //TestDirectory(@"blah blah - cd1", true, "blah blah");
+ //TestDirectory(@"blah blah - disc1", true, "blah blah");
+ //TestDirectory(@"blah blah - disk1", true, "blah blah");
+ //TestDirectory(@"blah blah - pt1", true, "blah blah");
+ //TestDirectory(@"blah blah - part1", true, "blah blah");
+ //TestDirectory(@"blah blah - dvd1", true, "blah blah");
+
+ //// Add a space
+ //TestDirectory(@"blah blah - cd 1", true, "blah blah");
+ //TestDirectory(@"blah blah - disc 1", true, "blah blah");
+ //TestDirectory(@"blah blah - disk 1", true, "blah blah");
+ //TestDirectory(@"blah blah - pt 1", true, "blah blah");
+ //TestDirectory(@"blah blah - part 1", true, "blah blah");
+ //TestDirectory(@"blah blah - dvd 1", true, "blah blah");
+
+ //// Not case sensitive
+ //TestDirectory(@"blah blah - Disc1", true, "blah blah");
+ }
+
+ [Fact]
+ public void TestNamesWithoutParts()
+ {
+ // No stacking here because there is no part/disc/etc
+ var files = new[]
+ {
+ "Harry Potter and the Deathly Hallows.mkv",
+ "Harry Potter and the Deathly Hallows 1.mkv",
+ "Harry Potter and the Deathly Hallows 2.mkv",
+ "Harry Potter and the Deathly Hallows 3.mkv",
+ "Harry Potter and the Deathly Hallows 4.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.ResolveFiles(files);
+
+ Assert.Empty(result.Stacks);
+ }
+
+ [Fact]
+ public void TestNumbersAppearingBeforePartNumber()
+ {
+ // No stacking here because there is no part/disc/etc
+ var files = new[]
+ {
+ "Neverland (2011)[720p][PG][Voted 6.5][Family-Fantasy]part1.mkv",
+ "Neverland (2011)[720p][PG][Voted 6.5][Family-Fantasy]part2.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.ResolveFiles(files);
+
+ Assert.Single(result.Stacks);
+ Assert.Equal(2, result.Stacks[0].Files.Count);
+ }
+
+ [Fact]
+ public void TestMultiDiscs()
+ {
+ // No stacking here because there is no part/disc/etc
+ var files = new[]
+ {
+ @"M:/Movies (DVD)/Movies (Musical)/The Sound of Music/The Sound of Music (1965) (Disc 01)",
+ @"M:/Movies (DVD)/Movies (Musical)/The Sound of Music/The Sound of Music (1965) (Disc 02)"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.ResolveDirectories(files);
+
+ Assert.Single(result.Stacks);
+ Assert.Equal(2, result.Stacks[0].Files.Count);
+ }
+
+ private void TestStackInfo(FileStack stack, string name, int fileCount)
+ {
+ Assert.Equal(fileCount, stack.Files.Count);
+ Assert.Equal(name, stack.Name);
+ }
+
+ private StackResolver GetResolver()
+ {
+ return new StackResolver(new NamingOptions());
+ }
+ }
+}
diff --git a/tests/Jellyfin.Naming.Tests/Video/StubTests.cs b/tests/Jellyfin.Naming.Tests/Video/StubTests.cs
new file mode 100644
index 000000000..96fa8c5a5
--- /dev/null
+++ b/tests/Jellyfin.Naming.Tests/Video/StubTests.cs
@@ -0,0 +1,55 @@
+using System;
+using System.Globalization;
+using Emby.Naming.Common;
+using Emby.Naming.Video;
+using Xunit;
+
+namespace Jellyfin.Naming.Tests.Video
+{
+ public class StubTests : BaseVideoTest
+ {
+ [Fact]
+ public void TestStubs()
+ {
+ Test("video.mkv", false, null);
+ Test("video.disc", true, null);
+ Test("video.dvd.disc", true, "dvd");
+ Test("video.hddvd.disc", true, "hddvd");
+ Test("video.bluray.disc", true, "bluray");
+ Test("video.brrip.disc", true, "bluray");
+ Test("video.bd25.disc", true, "bluray");
+ Test("video.bd50.disc", true, "bluray");
+ Test("video.vhs.disc", true, "vhs");
+ Test("video.hdtv.disc", true, "tv");
+ Test("video.pdtv.disc", true, "tv");
+ Test("video.dsr.disc", true, "tv");
+ }
+
+ [Fact]
+ public void TestStubName()
+ {
+ var result =
+ GetParser().ResolveFile(@"C:/Users/media/Desktop/Video Test/Movies/Oblivion/Oblivion.dvd.disc");
+
+ Assert.Equal("Oblivion", result.Name);
+ }
+
+ private void Test(string path, bool isStub, string stubType)
+ {
+ var options = new NamingOptions();
+
+ var resultStubType = StubResolver.ResolveFile(path, options);
+
+ Assert.Equal(isStub, resultStubType.IsStub);
+
+ if (stubType == null)
+ {
+ Assert.Null(resultStubType.StubType);
+ }
+ else
+ {
+ Assert.Equal(stubType, resultStubType.StubType, true);
+ }
+ }
+ }
+}
diff --git a/tests/Jellyfin.Naming.Tests/Video/VideoListResolverTests.cs b/tests/Jellyfin.Naming.Tests/Video/VideoListResolverTests.cs
new file mode 100644
index 000000000..ef8a17898
--- /dev/null
+++ b/tests/Jellyfin.Naming.Tests/Video/VideoListResolverTests.cs
@@ -0,0 +1,457 @@
+using System.Linq;
+using Emby.Naming.Common;
+using Emby.Naming.Video;
+using MediaBrowser.Model.IO;
+using Xunit;
+
+namespace Jellyfin.Naming.Tests.Video
+{
+ public class VideoListResolverTests
+ {
+ // FIXME
+ // [Fact]
+ public void TestStackAndExtras()
+ {
+ // No stacking here because there is no part/disc/etc
+ var files = new[]
+ {
+ "Harry Potter and the Deathly Hallows-trailer.mkv",
+ "Harry Potter and the Deathly Hallows.trailer.mkv",
+ "Harry Potter and the Deathly Hallows part1.mkv",
+ "Harry Potter and the Deathly Hallows part2.mkv",
+ "Harry Potter and the Deathly Hallows part3.mkv",
+ "Harry Potter and the Deathly Hallows part4.mkv",
+ "Batman-deleted.mkv",
+ "Batman-sample.mkv",
+ "Batman-trailer.mkv",
+ "Batman part1.mkv",
+ "Batman part2.mkv",
+ "Batman part3.mkv",
+ "Avengers.mkv",
+ "Avengers-trailer.mkv",
+
+ // Despite having a keyword in the name that will return an ExtraType, there's no original video to match it to
+ // So this is just a standalone video
+ "trailer.mkv",
+
+ // Same as above
+ "WillyWonka-trailer.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Equal(5, result.Count);
+
+ Assert.Equal(3, result[1].Files.Count);
+ Assert.Equal(3, result[1].Extras.Count);
+ Assert.Equal("Batman", result[1].Name);
+
+ Assert.Equal(4, result[2].Files.Count);
+ Assert.Equal(2, result[2].Extras.Count);
+ Assert.Equal("Harry Potter and the Deathly Hallows", result[2].Name);
+ }
+
+ [Fact]
+ public void TestWithMetadata()
+ {
+ var files = new[]
+ {
+ "300.mkv",
+ "300.nfo"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Single(result);
+ }
+
+ [Fact]
+ public void TestWithExtra()
+ {
+ var files = new[]
+ {
+ "300.mkv",
+ "300 trailer.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Single(result);
+ }
+
+ [Fact]
+ public void TestVariationWithFolderName()
+ {
+ var files = new[]
+ {
+ "X-Men Days of Future Past - 1080p.mkv",
+ "X-Men Days of Future Past-trailer.mp4"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Single(result);
+ }
+
+ [Fact]
+ public void TestTrailer2()
+ {
+ var files = new[]
+ {
+ "X-Men Days of Future Past - 1080p.mkv",
+ "X-Men Days of Future Past-trailer.mp4",
+ "X-Men Days of Future Past-trailer2.mp4"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Single(result);
+ }
+
+ [Fact]
+ public void TestDifferentNames()
+ {
+ var files = new[]
+ {
+ "Looper (2012)-trailer.mkv",
+ "Looper.2012.bluray.720p.x264.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Single(result);
+ }
+
+ [Fact]
+ public void TestSeparateFiles()
+ {
+ // These should be considered separate, unrelated videos
+ var files = new[]
+ {
+ "My video 1.mkv",
+ "My video 2.mkv",
+ "My video 3.mkv",
+ "My video 4.mkv",
+ "My video 5.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Equal(5, result.Count);
+ }
+
+ [Fact]
+ public void TestMultiDisc()
+ {
+ var files = new[]
+ {
+ @"M:/Movies (DVD)/Movies (Musical)/Sound of Music (1965)/Sound of Music Disc 1",
+ @"M:/Movies (DVD)/Movies (Musical)/Sound of Music (1965)/Sound of Music Disc 2"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = true,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Single(result);
+ }
+
+ [Fact]
+ public void TestPoundSign()
+ {
+ // These should be considered separate, unrelated videos
+ var files = new[]
+ {
+ @"My movie #1.mp4",
+ @"My movie #2.mp4"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = true,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Equal(2, result.Count);
+ }
+
+ [Fact]
+ public void TestStackedWithTrailer()
+ {
+ var files = new[]
+ {
+ @"No (2012) part1.mp4",
+ @"No (2012) part2.mp4",
+ @"No (2012) part1-trailer.mp4"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Single(result);
+ }
+
+ [Fact]
+ public void TestStackedWithTrailer2()
+ {
+ var files = new[]
+ {
+ @"No (2012) part1.mp4",
+ @"No (2012) part2.mp4",
+ @"No (2012)-trailer.mp4"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Single(result);
+ }
+
+ [Fact]
+ public void TestExtrasByFolderName()
+ {
+ var files = new[]
+ {
+ @"/Movies/Top Gun (1984)/movie.mp4",
+ @"/Movies/Top Gun (1984)/Top Gun (1984)-trailer.mp4",
+ @"/Movies/Top Gun (1984)/Top Gun (1984)-trailer2.mp4",
+ @"trailer.mp4"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Single(result);
+ }
+
+ [Fact]
+ public void TestDoubleTags()
+ {
+ var files = new[]
+ {
+ @"/MCFAMILY-PC/Private3$/Heterosexual/Breast In Class 2 Counterfeit Racks (2011)/Breast In Class 2 Counterfeit Racks (2011) Disc 1 cd1.avi",
+ @"/MCFAMILY-PC/Private3$/Heterosexual/Breast In Class 2 Counterfeit Racks (2011)/Breast In Class 2 Counterfeit Racks (2011) Disc 1 cd2.avi",
+ @"/MCFAMILY-PC/Private3$/Heterosexual/Breast In Class 2 Counterfeit Racks (2011)/Breast In Class 2 Disc 2 cd1.avi",
+ @"/MCFAMILY-PC/Private3$/Heterosexual/Breast In Class 2 Counterfeit Racks (2011)/Breast In Class 2 Disc 2 cd2.avi"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Equal(2, result.Count);
+ }
+
+ [Fact]
+ public void TestArgumentOutOfRangeException()
+ {
+ var files = new[]
+ {
+ @"/nas-markrobbo78/Videos/INDEX HTPC/Movies/Watched/3 - ACTION/Argo (2012)/movie.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Single(result);
+ }
+
+ [Fact]
+ public void TestColony()
+ {
+ var files = new[]
+ {
+ @"The Colony.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Single(result);
+ }
+
+ [Fact]
+ public void TestFourSisters()
+ {
+ var files = new[]
+ {
+ @"Four Sisters and a Wedding - A.avi",
+ @"Four Sisters and a Wedding - B.avi"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Single(result);
+ }
+
+ [Fact]
+ public void TestMovieTrailer()
+ {
+ var files = new[]
+ {
+ @"/Server/Despicable Me/Despicable Me (2010).mkv",
+ @"/Server/Despicable Me/movie-trailer.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Single(result);
+ }
+
+ [Fact]
+ public void TestTrailerFalsePositives()
+ {
+ var files = new[]
+ {
+ @"/Server/Despicable Me/Skyscraper (2018) - Big Game Spot.mkv",
+ @"/Server/Despicable Me/Skyscraper (2018) - Trailer.mkv",
+ @"/Server/Despicable Me/Baywatch (2017) - Big Game Spot.mkv",
+ @"/Server/Despicable Me/Baywatch (2017) - Trailer.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Equal(4, result.Count);
+ }
+
+ [Fact]
+ public void TestSubfolders()
+ {
+ var files = new[]
+ {
+ @"/Movies/Despicable Me/Despicable Me.mkv",
+ @"/Movies/Despicable Me/trailers/trailer.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Single(result);
+ }
+
+ private VideoListResolver GetResolver()
+ {
+ var options = new NamingOptions();
+ return new VideoListResolver(options);
+ }
+ }
+}
diff --git a/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs b/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs
new file mode 100644
index 000000000..5a3ce8886
--- /dev/null
+++ b/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs
@@ -0,0 +1,275 @@
+using MediaBrowser.Model.Entities;
+using Xunit;
+
+namespace Jellyfin.Naming.Tests.Video
+{
+ public class VideoResolverTests : BaseVideoTest
+ {
+ // FIXME
+ // [Fact]
+ public void TestSimpleFile()
+ {
+ var parser = GetParser();
+
+ var result =
+ parser.ResolveFile(@"/server/Movies/Brave (2007)/Brave (2006).mkv");
+
+ Assert.Equal(2006, result.Year);
+ Assert.False(result.IsStub);
+ Assert.False(result.Is3D);
+ Assert.Equal("Brave", result.Name);
+ Assert.Null(result.ExtraType);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestSimpleFile2()
+ {
+ var parser = GetParser();
+
+ var result =
+ parser.ResolveFile(@"/server/Movies/Bad Boys (1995)/Bad Boys (1995).mkv");
+
+ Assert.Equal(1995, result.Year);
+ Assert.False(result.IsStub);
+ Assert.False(result.Is3D);
+ Assert.Equal("Bad Boys", result.Name);
+ Assert.Null(result.ExtraType);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestSimpleFileWithNumericName()
+ {
+ var parser = GetParser();
+
+ var result =
+ parser.ResolveFile(@"/server/Movies/300 (2007)/300 (2006).mkv");
+
+ Assert.Equal(2006, result.Year);
+ Assert.False(result.IsStub);
+ Assert.False(result.Is3D);
+ Assert.Equal("300", result.Name);
+ Assert.Null(result.ExtraType);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestExtra()
+ {
+ var parser = GetParser();
+
+ var result =
+ parser.ResolveFile(@"/server/Movies/Brave (2007)/Brave (2006)-trailer.mkv");
+
+ Assert.Equal(2006, result.Year);
+ Assert.False(result.IsStub);
+ Assert.False(result.Is3D);
+ Assert.Equal(ExtraType.Trailer, result.ExtraType);
+ Assert.Equal("Brave (2006)-trailer", result.Name);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestExtraWithNumericName()
+ {
+ var parser = GetParser();
+
+ var result =
+ parser.ResolveFile(@"/server/Movies/300 (2007)/300 (2006)-trailer.mkv");
+
+ Assert.Equal(2006, result.Year);
+ Assert.False(result.IsStub);
+ Assert.False(result.Is3D);
+ Assert.Equal("300 (2006)-trailer", result.Name);
+ Assert.Equal(ExtraType.Trailer, result.ExtraType);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestStubFileWithNumericName()
+ {
+ var parser = GetParser();
+
+ var result =
+ parser.ResolveFile(@"/server/Movies/300 (2007)/300 (2006).bluray.disc");
+
+ Assert.Equal(2006, result.Year);
+ Assert.True(result.IsStub);
+ Assert.Equal("bluray", result.StubType);
+ Assert.False(result.Is3D);
+ Assert.Equal("300", result.Name);
+ Assert.Null(result.ExtraType);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestStubFile()
+ {
+ var parser = GetParser();
+
+ var result =
+ parser.ResolveFile(@"/server/Movies/Brave (2007)/Brave (2006).bluray.disc");
+
+ Assert.Equal(2006, result.Year);
+ Assert.True(result.IsStub);
+ Assert.Equal("bluray", result.StubType);
+ Assert.False(result.Is3D);
+ Assert.Equal("Brave", result.Name);
+ Assert.Null(result.ExtraType);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestExtraStubWithNumericNameNotSupported()
+ {
+ var parser = GetParser();
+
+ var result =
+ parser.ResolveFile(@"/server/Movies/300 (2007)/300 (2006)-trailer.bluray.disc");
+
+ Assert.Equal(2006, result.Year);
+ Assert.True(result.IsStub);
+ Assert.Equal("bluray", result.StubType);
+ Assert.False(result.Is3D);
+ Assert.Equal("300", result.Name);
+ Assert.Null(result.ExtraType);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestExtraStubNotSupported()
+ {
+ // Using a stub for an extra is currently not supported
+ var parser = GetParser();
+
+ var result =
+ parser.ResolveFile(@"/server/Movies/brave (2007)/brave (2006)-trailer.bluray.disc");
+
+ Assert.Equal(2006, result.Year);
+ Assert.True(result.IsStub);
+ Assert.Equal("bluray", result.StubType);
+ Assert.False(result.Is3D);
+ Assert.Equal("brave", result.Name);
+ Assert.Null(result.ExtraType);
+ }
+
+ // FIXME
+ // [Fact]
+ public void Test3DFileWithNumericName()
+ {
+ var parser = GetParser();
+
+ var result =
+ parser.ResolveFile(@"/server/Movies/300 (2007)/300 (2006).3d.sbs.mkv");
+
+ Assert.Equal(2006, result.Year);
+ Assert.False(result.IsStub);
+ Assert.True(result.Is3D);
+ Assert.Equal("sbs", result.Format3D);
+ Assert.Equal("300", result.Name);
+ Assert.Null(result.ExtraType);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestBad3DFileWithNumericName()
+ {
+ var parser = GetParser();
+
+ var result =
+ parser.ResolveFile(@"/server/Movies/300 (2007)/300 (2006).3d1.sbas.mkv");
+
+ Assert.Equal(2006, result.Year);
+ Assert.False(result.IsStub);
+ Assert.False(result.Is3D);
+ Assert.Equal("300", result.Name);
+ Assert.Null(result.ExtraType);
+ Assert.Null(result.Format3D);
+ }
+
+ // FIXME
+ // [Fact]
+ public void Test3DFile()
+ {
+ var parser = GetParser();
+
+ var result =
+ parser.ResolveFile(@"/server/Movies/brave (2007)/brave (2006).3d.sbs.mkv");
+
+ Assert.Equal(2006, result.Year);
+ Assert.False(result.IsStub);
+ Assert.True(result.Is3D);
+ Assert.Equal("sbs", result.Format3D);
+ Assert.Equal("brave", result.Name);
+ Assert.Null(result.ExtraType);
+ }
+
+ [Fact]
+ public void TestNameWithoutDate()
+ {
+ var parser = GetParser();
+
+ var result =
+ parser.ResolveFile(@"/server/Movies/American Psycho/American.Psycho.mkv");
+
+ Assert.Null(result.Year);
+ Assert.False(result.IsStub);
+ Assert.False(result.Is3D);
+ Assert.Null(result.Format3D);
+ Assert.Equal("American.Psycho", result.Name);
+ Assert.Null(result.ExtraType);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestCleanDateAndStringsSequence()
+ {
+ var parser = GetParser();
+
+ // In this test case, running CleanDateTime first produces no date, so it will attempt to run CleanString first and then CleanDateTime again
+ var result =
+ parser.ResolveFile(@"/server/Movies/3.Days.to.Kill/3.Days.to.Kill.2014.720p.BluRay.x264.YIFY.mkv");
+
+ Assert.Equal(2014, result.Year);
+ Assert.False(result.IsStub);
+ Assert.False(result.Is3D);
+ Assert.Null(result.Format3D);
+ Assert.Equal("3.Days.to.Kill", result.Name);
+ Assert.Null(result.ExtraType);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestCleanDateAndStringsSequence1()
+ {
+ var parser = GetParser();
+
+ // In this test case, running CleanDateTime first produces no date, so it will attempt to run CleanString first and then CleanDateTime again
+ var result =
+ parser.ResolveFile(@"/server/Movies/3 days to kill (2005)/3 days to kill (2005).mkv");
+
+ Assert.Equal(2005, result.Year);
+ Assert.False(result.IsStub);
+ Assert.False(result.Is3D);
+ Assert.Null(result.Format3D);
+ Assert.Equal("3 days to kill", result.Name);
+ Assert.Null(result.ExtraType);
+ }
+
+ [Fact]
+ public void TestFolderNameWithExtension()
+ {
+ var parser = GetParser();
+
+ var result =
+ parser.ResolveFile(@"/server/Movies/7 Psychos.mkv/7 Psychos.mkv");
+
+ Assert.Null(result.Year);
+ Assert.False(result.IsStub);
+ Assert.False(result.Is3D);
+ Assert.Equal("7 Psychos", result.Name);
+ Assert.Null(result.ExtraType);
+ }
+ }
+}