aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Emby.Dlna/PlayTo/Device.cs53
-rw-r--r--Emby.Dlna/PlayTo/PlayToController.cs2
-rw-r--r--Emby.Naming/TV/SeasonPathParser.cs2
-rw-r--r--Emby.Server.Implementations/Emby.Server.Implementations.csproj2
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs5
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs18
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs2
-rw-r--r--Emby.Server.Implementations/Localization/Core/bg-BG.json22
-rw-r--r--Jellyfin.Api/Controllers/PluginsController.cs4
-rw-r--r--Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs2
-rw-r--r--Jellyfin.Api/Jellyfin.Api.csproj10
-rw-r--r--Jellyfin.Server.Implementations/Properties/AssemblyInfo.cs23
-rw-r--r--Jellyfin.Server.Implementations/Users/UserManager.cs28
-rw-r--r--Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs1
-rw-r--r--Jellyfin.sln (renamed from MediaBrowser.sln)0
-rw-r--r--MediaBrowser.Common/Json/JsonDefaults.cs1
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs10
-rw-r--r--MediaBrowser.Model/Configuration/ServerConfiguration.cs9
-rw-r--r--MediaBrowser.Providers/Manager/ImageSaver.cs6
-rw-r--r--MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs5
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs3
-rw-r--r--MediaBrowser.Providers/Studios/StudiosImageProvider.cs2
-rw-r--r--MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs76
-rw-r--r--MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs18
-rw-r--r--MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs18
-rw-r--r--MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs18
-rw-r--r--MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs17
-rw-r--r--MediaBrowser.XbmcMetadata/Properties/AssemblyInfo.cs2
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs21
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs21
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs11
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs21
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/MovieNfoProvider.cs17
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/MusicVideoNfoProvider.cs21
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/SeasonNfoProvider.cs21
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/SeriesNfoProvider.cs21
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/VideoNfoProvider.cs21
-rw-r--r--jellyfin.ruleset4
-rw-r--r--tests/Jellyfin.Api.Tests/Helpers/RequestHelpersTests.cs34
-rw-r--r--tests/Jellyfin.Naming.Tests/Video/VideoListResolverTests.cs110
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj1
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Users/UserManagerTests.cs28
-rw-r--r--tests/Jellyfin.XbmcMetadata.Tests/Location/MovieNfoLocationTests.cs65
-rw-r--r--tests/Jellyfin.XbmcMetadata.Tests/Parsers/EpisodeNfoProviderTests.cs10
-rw-r--r--tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs74
-rw-r--r--tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicAlbumNfoProviderTests.cs6
-rw-r--r--tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicArtistNfoParserTests.cs6
-rw-r--r--tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicVideoNfoParserTests.cs73
-rw-r--r--tests/Jellyfin.XbmcMetadata.Tests/Parsers/SeasonNfoProviderTests.cs6
-rw-r--r--tests/Jellyfin.XbmcMetadata.Tests/Parsers/SeriesNfoParserTests.cs26
-rw-r--r--tests/Jellyfin.XbmcMetadata.Tests/Test Data/American Gods.nfo6
-rw-r--r--tests/Jellyfin.XbmcMetadata.Tests/Test Data/Dancing Queen.nfo50
-rw-r--r--tests/Jellyfin.XbmcMetadata.Tests/Test Data/Imdb.nfo1
-rw-r--r--tests/Jellyfin.XbmcMetadata.Tests/Test Data/Justice League.nfo19
-rw-r--r--tests/Jellyfin.XbmcMetadata.Tests/Test Data/The Bone Orchard.nfo4
-rw-r--r--tests/Jellyfin.XbmcMetadata.Tests/Test Data/Tmdb.nfo1
-rw-r--r--tests/Jellyfin.XbmcMetadata.Tests/Test Data/Tvdb.nfo1
58 files changed, 815 insertions, 246 deletions
diff --git a/Emby.Dlna/PlayTo/Device.cs b/Emby.Dlna/PlayTo/Device.cs
index 9961d15b0..7bf7047fb 100644
--- a/Emby.Dlna/PlayTo/Device.cs
+++ b/Emby.Dlna/PlayTo/Device.cs
@@ -235,7 +235,13 @@ namespace Emby.Dlna.PlayTo
_logger.LogDebug("Setting mute");
var value = mute ? 1 : 0;
- await new SsdpHttpClient(_httpClientFactory).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType, value))
+ await new SsdpHttpClient(_httpClientFactory)
+ .SendCommandAsync(
+ Properties.BaseUrl,
+ service,
+ command.Name,
+ rendererCommands.BuildPost(command, service.ServiceType, value),
+ cancellationToken: cancellationToken)
.ConfigureAwait(false);
IsMuted = mute;
@@ -270,7 +276,13 @@ namespace Emby.Dlna.PlayTo
// Remote control will perform better
Volume = value;
- await new SsdpHttpClient(_httpClientFactory).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType, value))
+ await new SsdpHttpClient(_httpClientFactory)
+ .SendCommandAsync(
+ Properties.BaseUrl,
+ service,
+ command.Name,
+ rendererCommands.BuildPost(command, service.ServiceType, value),
+ cancellationToken: cancellationToken)
.ConfigureAwait(false);
}
@@ -291,7 +303,13 @@ namespace Emby.Dlna.PlayTo
throw new InvalidOperationException("Unable to find service");
}
- await new SsdpHttpClient(_httpClientFactory).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, string.Format(CultureInfo.InvariantCulture, "{0:hh}:{0:mm}:{0:ss}", value), "REL_TIME"))
+ await new SsdpHttpClient(_httpClientFactory)
+ .SendCommandAsync(
+ Properties.BaseUrl,
+ service,
+ command.Name,
+ avCommands.BuildPost(command, service.ServiceType, string.Format(CultureInfo.InvariantCulture, "{0:hh}:{0:mm}:{0:ss}", value), "REL_TIME"),
+ cancellationToken: cancellationToken)
.ConfigureAwait(false);
RestartTimer(true);
@@ -325,14 +343,21 @@ namespace Emby.Dlna.PlayTo
}
var post = avCommands.BuildPost(command, service.ServiceType, url, dictionary);
- await new SsdpHttpClient(_httpClientFactory).SendCommandAsync(Properties.BaseUrl, service, command.Name, post, header: header)
+ await new SsdpHttpClient(_httpClientFactory)
+ .SendCommandAsync(
+ Properties.BaseUrl,
+ service,
+ command.Name,
+ post,
+ header: header,
+ cancellationToken: cancellationToken)
.ConfigureAwait(false);
- await Task.Delay(50).ConfigureAwait(false);
+ await Task.Delay(50, cancellationToken).ConfigureAwait(false);
try
{
- await SetPlay(avCommands, CancellationToken.None).ConfigureAwait(false);
+ await SetPlay(avCommands, cancellationToken).ConfigureAwait(false);
}
catch
{
@@ -396,7 +421,13 @@ namespace Emby.Dlna.PlayTo
var service = GetAvTransportService();
- await new SsdpHttpClient(_httpClientFactory).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, 1))
+ await new SsdpHttpClient(_httpClientFactory)
+ .SendCommandAsync(
+ Properties.BaseUrl,
+ service,
+ command.Name,
+ avCommands.BuildPost(command, service.ServiceType, 1),
+ cancellationToken: cancellationToken)
.ConfigureAwait(false);
RestartTimer(true);
@@ -414,7 +445,13 @@ namespace Emby.Dlna.PlayTo
var service = GetAvTransportService();
- await new SsdpHttpClient(_httpClientFactory).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, 1))
+ await new SsdpHttpClient(_httpClientFactory)
+ .SendCommandAsync(
+ Properties.BaseUrl,
+ service,
+ command.Name,
+ avCommands.BuildPost(command, service.ServiceType, 1),
+ cancellationToken: cancellationToken)
.ConfigureAwait(false);
TransportState = TransportState.Paused;
diff --git a/Emby.Dlna/PlayTo/PlayToController.cs b/Emby.Dlna/PlayTo/PlayToController.cs
index 315be1e8b..e4923b9eb 100644
--- a/Emby.Dlna/PlayTo/PlayToController.cs
+++ b/Emby.Dlna/PlayTo/PlayToController.cs
@@ -777,7 +777,7 @@ namespace Emby.Dlna.PlayTo
var currentWait = 0;
while (_device.TransportState != TransportState.Playing && currentWait < MaxWait)
{
- await Task.Delay(Interval).ConfigureAwait(false);
+ await Task.Delay(Interval, cancellationToken).ConfigureAwait(false);
currentWait += Interval;
}
diff --git a/Emby.Naming/TV/SeasonPathParser.cs b/Emby.Naming/TV/SeasonPathParser.cs
index d11c7c99e..6236f86c4 100644
--- a/Emby.Naming/TV/SeasonPathParser.cs
+++ b/Emby.Naming/TV/SeasonPathParser.cs
@@ -60,7 +60,7 @@ namespace Emby.Naming.TV
bool supportSpecialAliases,
bool supportNumericSeasonFolders)
{
- var filename = Path.GetFileName(path) ?? string.Empty;
+ string filename = Path.GetFileName(path);
if (supportSpecialAliases)
{
diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
index 08047ba47..522667153 100644
--- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj
+++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
@@ -29,7 +29,7 @@
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="5.0.0" />
<PackageReference Include="Mono.Nat" Version="3.0.1" />
<PackageReference Include="prometheus-net.DotNetRuntime" Version="3.4.1" />
- <PackageReference Include="sharpcompress" Version="0.27.1" />
+ <PackageReference Include="sharpcompress" Version="0.28.0" />
<PackageReference Include="SQLitePCL.pretty.netstandard" Version="2.1.0" />
<PackageReference Include="DotNet.Glob" Version="3.1.2" />
</ItemGroup>
diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs
index e9e688fa6..60f82806f 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs
@@ -79,11 +79,6 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
return new MusicArtist();
}
- if (_config.Configuration.EnableSimpleArtistDetection)
- {
- return null;
- }
-
// Avoid mis-identifying top folders
if (args.Parent.IsRoot)
{
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
index d32b70e03..c641b760b 100644
--- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
@@ -10,6 +10,7 @@ using System.Net.Http;
using System.Net.Mime;
using System.Text;
using System.Text.Json;
+using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common;
@@ -111,7 +112,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
options.Headers.TryAddWithoutValidation("token", token);
using var response = await Send(options, true, info, cancellationToken).ConfigureAwait(false);
await using var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
- var dailySchedules = await JsonSerializer.DeserializeAsync<List<ScheduleDirect.Day>>(responseStream, _jsonOptions).ConfigureAwait(false);
+ var dailySchedules = await JsonSerializer.DeserializeAsync<List<ScheduleDirect.Day>>(responseStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
_logger.LogDebug("Found {ScheduleCount} programs on {ChannelID} ScheduleDirect", dailySchedules.Count, channelId);
using var programRequestOptions = new HttpRequestMessage(HttpMethod.Post, ApiUrl + "/programs");
@@ -122,12 +123,12 @@ namespace Emby.Server.Implementations.LiveTv.Listings
using var innerResponse = await Send(programRequestOptions, true, info, cancellationToken).ConfigureAwait(false);
await using var innerResponseStream = await innerResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
- var programDetails = await JsonSerializer.DeserializeAsync<List<ScheduleDirect.ProgramDetails>>(innerResponseStream, _jsonOptions).ConfigureAwait(false);
+ var programDetails = await JsonSerializer.DeserializeAsync<List<ScheduleDirect.ProgramDetails>>(innerResponseStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
var programDict = programDetails.ToDictionary(p => p.programID, y => y);
- var programIdsWithImages =
- programDetails.Where(p => p.hasImageArtwork).Select(p => p.programID)
- .ToList();
+ var programIdsWithImages = programDetails
+ .Where(p => p.hasImageArtwork).Select(p => p.programID)
+ .ToList();
var images = await GetImageForPrograms(info, programIdsWithImages, cancellationToken).ConfigureAwait(false);
@@ -182,8 +183,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
private static int GetSizeOrder(ScheduleDirect.ImageData image)
{
- if (!string.IsNullOrWhiteSpace(image.height)
- && int.TryParse(image.height, out int value))
+ if (int.TryParse(image.height, out int value))
{
return value;
}
@@ -704,7 +704,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
httpResponse.EnsureSuccessStatusCode();
await using var stream = await httpResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
using var response = httpResponse.Content;
- var root = await JsonSerializer.DeserializeAsync<ScheduleDirect.Lineups>(stream, _jsonOptions).ConfigureAwait(false);
+ var root = await JsonSerializer.DeserializeAsync<ScheduleDirect.Lineups>(stream, _jsonOptions, cancellationToken).ConfigureAwait(false);
return root.lineups.Any(i => string.Equals(info.ListingsId, i.lineup, StringComparison.OrdinalIgnoreCase));
}
@@ -776,7 +776,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
using var httpResponse = await Send(options, true, info, cancellationToken).ConfigureAwait(false);
await using var stream = await httpResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
- var root = await JsonSerializer.DeserializeAsync<ScheduleDirect.Channel>(stream, _jsonOptions).ConfigureAwait(false);
+ var root = await JsonSerializer.DeserializeAsync<ScheduleDirect.Channel>(stream, _jsonOptions, cancellationToken).ConfigureAwait(false);
_logger.LogInformation("Found {ChannelCount} channels on the lineup on ScheduleDirect", root.map.Count);
_logger.LogInformation("Mapping Stations to Channel");
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
index cf653f87d..b16ccc561 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
@@ -92,7 +92,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
try
{
- await tcpClient.ConnectAsync(remoteAddress, HdHomerunManager.HdHomeRunPort).ConfigureAwait(false);
+ await tcpClient.ConnectAsync(remoteAddress, HdHomerunManager.HdHomeRunPort, openCancellationToken).ConfigureAwait(false);
localAddress = ((IPEndPoint)tcpClient.Client.LocalEndPoint).Address;
tcpClient.Close();
}
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
index f7507e6ba..eeb2426f4 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
@@ -159,7 +159,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
EnableStreamSharing = false;
await DeleteTempFiles(new List<string> { TempFilePath }).ConfigureAwait(false);
- });
+ }, CancellationToken.None);
}
private void Resolve(TaskCompletionSource<bool> openTaskCompletionSource)
diff --git a/Emby.Server.Implementations/Localization/Core/bg-BG.json b/Emby.Server.Implementations/Localization/Core/bg-BG.json
index 7ff30df71..9db3b50d9 100644
--- a/Emby.Server.Implementations/Localization/Core/bg-BG.json
+++ b/Emby.Server.Implementations/Localization/Core/bg-BG.json
@@ -55,26 +55,26 @@
"NotificationOptionPluginInstalled": "Приставката е инсталирана",
"NotificationOptionPluginUninstalled": "Приставката е деинсталирана",
"NotificationOptionPluginUpdateInstalled": "Обновлението на приставката е инсталирано",
- "NotificationOptionServerRestartRequired": "Нужно е повторно пускане на сървъра",
+ "NotificationOptionServerRestartRequired": "Сървърът трябва да се рестартира",
"NotificationOptionTaskFailed": "Грешка в планирана задача",
- "NotificationOptionUserLockedOut": "Потребителя е заключен",
+ "NotificationOptionUserLockedOut": "Потребителят е заключен",
"NotificationOptionVideoPlayback": "Възпроизвеждането на видео започна",
"NotificationOptionVideoPlaybackStopped": "Възпроизвеждането на видео е спряно",
"Photos": "Снимки",
"Playlists": "Списъци",
"Plugin": "Приставка",
- "PluginInstalledWithName": "{0} е инсталирано",
- "PluginUninstalledWithName": "{0} е деинсталирано",
- "PluginUpdatedWithName": "{0} е обновено",
+ "PluginInstalledWithName": "{0} е инсталиранa",
+ "PluginUninstalledWithName": "{0} е деинсталиранa",
+ "PluginUpdatedWithName": "{0} е обновенa",
"ProviderValue": "Доставчик: {0}",
"ScheduledTaskFailedWithName": "{0} се провали",
"ScheduledTaskStartedWithName": "{0} започна",
- "ServerNameNeedsToBeRestarted": "{0} е нужно да се рестартира",
+ "ServerNameNeedsToBeRestarted": "{0} трябва да се рестартира",
"Shows": "Сериали",
"Songs": "Песни",
"StartupEmbyServerIsLoading": "Сървърът зарежда. Моля, опитайте отново след малко.",
"SubtitleDownloadFailureForItem": "Неуспешно изтегляне на субтитри за {0}",
- "SubtitleDownloadFailureFromForItem": "Поднадписите за {1} от {0} не можаха да се изтеглят",
+ "SubtitleDownloadFailureFromForItem": "Субтитрите за {1} от {0} не можаха да бъдат изтеглени",
"Sync": "Синхронизиране",
"System": "Система",
"TvShows": "Телевизионни сериали",
@@ -92,12 +92,12 @@
"ValueHasBeenAddedToLibrary": "{0} беше добавен във Вашата библиотека",
"ValueSpecialEpisodeName": "Специални - {0}",
"VersionNumber": "Версия {0}",
- "TaskDownloadMissingSubtitlesDescription": "Търси Интернет за липсващи поднадписи, на база конфигурацията за мета-данни.",
- "TaskDownloadMissingSubtitles": "Изтегляне на липсващи поднадписи",
+ "TaskDownloadMissingSubtitlesDescription": "Търси Интернет за липсващи субтитри, на база конфигурацията за мета-данни.",
+ "TaskDownloadMissingSubtitles": "Изтегляне на липсващи субтитри",
"TaskRefreshChannelsDescription": "Обновява информацията за интернет канала.",
"TaskRefreshChannels": "Обновяване на Канали",
- "TaskCleanTranscodeDescription": "Изтрива прекодирани файлове по-стари от един ден.",
- "TaskCleanTranscode": "Изчиства директорията за прекодиране",
+ "TaskCleanTranscodeDescription": "Изтрива транскодирани файлове по-стари от един ден.",
+ "TaskCleanTranscode": "Изчиства директорията за транскодиране",
"TaskUpdatePluginsDescription": "Изтегля и инсталира актуализации за добавките, които са настроени за автоматична актуализация.",
"TaskUpdatePlugins": "Актуализира добавките",
"TaskRefreshPeopleDescription": "Актуализира мета-данните за артистите и режисьорите за Вашата медийна библиотека.",
diff --git a/Jellyfin.Api/Controllers/PluginsController.cs b/Jellyfin.Api/Controllers/PluginsController.cs
index b2e8bee91..a5aa9bfca 100644
--- a/Jellyfin.Api/Controllers/PluginsController.cs
+++ b/Jellyfin.Api/Controllers/PluginsController.cs
@@ -298,9 +298,7 @@ namespace Jellyfin.Api.Controllers
}
var imagePath = Path.Combine(plugin.Path, plugin.Manifest.ImagePath ?? string.Empty);
- if (((ServerConfiguration)_config.CommonConfiguration).DisablePluginImages
- || plugin.Manifest.ImagePath == null
- || !System.IO.File.Exists(imagePath))
+ if (plugin.Manifest.ImagePath == null || !System.IO.File.Exists(imagePath))
{
return NotFound();
}
diff --git a/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs b/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs
index cfa2c1229..89d36ab09 100644
--- a/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs
+++ b/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs
@@ -39,7 +39,7 @@ namespace Jellyfin.Api.Helpers
}
// Can't dispose the response as it's required up the call chain.
- var response = await httpClient.GetAsync(new Uri(state.MediaPath)).ConfigureAwait(false);
+ var response = await httpClient.GetAsync(new Uri(state.MediaPath), cancellationToken).ConfigureAwait(false);
var contentType = response.Content.Headers.ContentType?.ToString();
httpContext.Response.Headers[HeaderNames.AcceptRanges] = "none";
diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj
index c2f4ab522..5f7c64a7e 100644
--- a/Jellyfin.Api/Jellyfin.Api.csproj
+++ b/Jellyfin.Api/Jellyfin.Api.csproj
@@ -17,8 +17,8 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="5.0.3" />
<PackageReference Include="Microsoft.Extensions.Http" Version="5.0.0" />
- <PackageReference Include="Swashbuckle.AspNetCore" Version="5.6.3" />
- <PackageReference Include="Swashbuckle.AspNetCore.ReDoc" Version="6.0.2" />
+ <PackageReference Include="Swashbuckle.AspNetCore" Version="6.0.5" />
+ <PackageReference Include="Swashbuckle.AspNetCore.ReDoc" Version="6.0.5" />
</ItemGroup>
<ItemGroup>
@@ -38,4 +38,10 @@
<CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
+ <ItemGroup>
+ <AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
+ <_Parameter1>Jellyfin.Api.Tests</_Parameter1>
+ </AssemblyAttribute>
+ </ItemGroup>
+
</Project>
diff --git a/Jellyfin.Server.Implementations/Properties/AssemblyInfo.cs b/Jellyfin.Server.Implementations/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..6528d4fdc
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Properties/AssemblyInfo.cs
@@ -0,0 +1,23 @@
+using System.Reflection;
+using System.Resources;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Jellyfin.Server.Implementations")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Jellyfin Project")]
+[assembly: AssemblyProduct("Jellyfin Server")]
+[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+[assembly: NeutralResourcesLanguage("en")]
+[assembly: InternalsVisibleTo("Jellyfin.Server.Implementations.Tests")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs
index d1de5408c..76d1389ca 100644
--- a/Jellyfin.Server.Implementations/Users/UserManager.cs
+++ b/Jellyfin.Server.Implementations/Users/UserManager.cs
@@ -137,17 +137,14 @@ namespace Jellyfin.Server.Implementations.Users
throw new ArgumentNullException(nameof(user));
}
- if (string.IsNullOrWhiteSpace(newName))
- {
- throw new ArgumentException("Invalid username", nameof(newName));
- }
+ ThrowIfInvalidUsername(newName);
if (user.Username.Equals(newName, StringComparison.Ordinal))
{
throw new ArgumentException("The new and old names must be different.");
}
- if (Users.Any(u => u.Id != user.Id && u.Username.Equals(newName, StringComparison.Ordinal)))
+ if (Users.Any(u => u.Id != user.Id && u.Username.Equals(newName, StringComparison.OrdinalIgnoreCase)))
{
throw new ArgumentException(string.Format(
CultureInfo.InvariantCulture,
@@ -201,9 +198,14 @@ namespace Jellyfin.Server.Implementations.Users
/// <inheritdoc/>
public async Task<User> CreateUserAsync(string name)
{
- if (!IsValidUsername(name))
+ ThrowIfInvalidUsername(name);
+
+ if (Users.Any(u => u.Username.Equals(name, StringComparison.OrdinalIgnoreCase)))
{
- throw new ArgumentException("Usernames can contain unicode symbols, numbers (0-9), dashes (-), underscores (_), apostrophes ('), and periods (.)");
+ throw new ArgumentException(string.Format(
+ CultureInfo.InvariantCulture,
+ "A user with the name '{0}' already exists.",
+ name));
}
await using var dbContext = _dbProvider.CreateContext();
@@ -725,12 +727,22 @@ namespace Jellyfin.Server.Implementations.Users
_users[user.Id] = user;
}
+ internal static void ThrowIfInvalidUsername(string name)
+ {
+ if (!string.IsNullOrWhiteSpace(name) && IsValidUsername(name))
+ {
+ return;
+ }
+
+ throw new ArgumentException("Usernames can contain unicode symbols, numbers (0-9), dashes (-), underscores (_), apostrophes ('), and periods (.)", nameof(name));
+ }
+
private static bool IsValidUsername(string name)
{
// This is some regex that matches only on unicode "word" characters, as well as -, _ and @
// In theory this will cut out most if not all 'control' characters which should help minimize any weirdness
// Usernames can contain letters (a-z + whatever else unicode is cool with), numbers (0-9), at-signs (@), dashes (-), underscores (_), apostrophes ('), periods (.) and spaces ( )
- return Regex.IsMatch(name, @"^[\w\ \-'._@]*$");
+ return Regex.IsMatch(name, @"^[\w\ \-'._@]+$");
}
private IAuthenticationProvider GetAuthenticationProvider(User user)
diff --git a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs
index 545937207..77f6695bb 100644
--- a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs
+++ b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs
@@ -310,6 +310,7 @@ namespace Jellyfin.Server.Extensions
// Allow parameters to properly be nullable.
c.UseAllOfToExtendReferenceSchemas();
+ c.SupportNonNullableReferenceTypes();
// TODO - remove when all types are supported in System.Text.Json
c.AddSwaggerTypeMappings();
diff --git a/MediaBrowser.sln b/Jellyfin.sln
index 4e6687cce..4e6687cce 100644
--- a/MediaBrowser.sln
+++ b/Jellyfin.sln
diff --git a/MediaBrowser.Common/Json/JsonDefaults.cs b/MediaBrowser.Common/Json/JsonDefaults.cs
index 9a2ea6875..2ef24a884 100644
--- a/MediaBrowser.Common/Json/JsonDefaults.cs
+++ b/MediaBrowser.Common/Json/JsonDefaults.cs
@@ -31,7 +31,6 @@ namespace MediaBrowser.Common.Json
WriteIndented = false,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
NumberHandling = JsonNumberHandling.AllowReadingFromString,
- PropertyNameCaseInsensitive = true,
Converters =
{
new JsonGuidConverter(),
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index 53a78a027..e5877a484 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -131,6 +131,12 @@ namespace MediaBrowser.Controller.MediaEncoding
private bool IsVppTonemappingSupported(EncodingJobInfo state, EncodingOptions options)
{
var videoStream = state.VideoStream;
+ if (videoStream == null)
+ {
+ // Remote stream doesn't have media info, disable vpp tonemapping.
+ return false;
+ }
+
var codec = videoStream.Codec;
if (string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase))
{
@@ -2531,7 +2537,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
// If double rate deinterlacing is enabled and the input framerate is 30fps or below, otherwise the output framerate will be too high for many devices
- var doubleRateDeinterlace = options.DeinterlaceDoubleRate && (videoStream?.RealFrameRate ?? 60) <= 30;
+ var doubleRateDeinterlace = options.DeinterlaceDoubleRate && (videoStream?.AverageFrameRate ?? 60) <= 30;
var isScalingInAdvance = false;
var isCudaDeintInAdvance = false;
@@ -3081,7 +3087,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
inputModifier += " -deint 1";
- if (!encodingOptions.DeinterlaceDoubleRate || (videoStream?.RealFrameRate ?? 60) > 30)
+ if (!encodingOptions.DeinterlaceDoubleRate || (videoStream?.AverageFrameRate ?? 60) > 30)
{
inputModifier += " -drop_second_field 1";
}
diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
index 0f0ad0f9a..d1e999666 100644
--- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs
+++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
@@ -254,7 +254,7 @@ namespace MediaBrowser.Model.Configuration
/// Gets or sets the preferred metadata language.
/// </summary>
/// <value>The preferred metadata language.</value>
- public string PreferredMetadataLanguage { get; set; } = string.Empty;
+ public string PreferredMetadataLanguage { get; set; } = "en";
/// <summary>
/// Gets or sets the metadata country code.
@@ -418,8 +418,6 @@ namespace MediaBrowser.Model.Configuration
public PathSubstitution[] PathSubstitutions { get; set; } = Array.Empty<PathSubstitution>();
- public bool EnableSimpleArtistDetection { get; set; } = false;
-
public string[] UninstalledPlugins { get; set; } = Array.Empty<string>();
/// <summary>
@@ -461,10 +459,5 @@ namespace MediaBrowser.Model.Configuration
/// Gets or sets a value indicating whether older plugins should automatically be deleted from the plugin folder.
/// </summary>
public bool RemoveOldPlugins { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether plugin image should be disabled.
- /// </summary>
- public bool DisablePluginImages { get; set; }
}
}
diff --git a/MediaBrowser.Providers/Manager/ImageSaver.cs b/MediaBrowser.Providers/Manager/ImageSaver.cs
index 19a42d506..9dd87aef5 100644
--- a/MediaBrowser.Providers/Manager/ImageSaver.cs
+++ b/MediaBrowser.Providers/Manager/ImageSaver.cs
@@ -102,10 +102,8 @@ namespace MediaBrowser.Providers.Manager
{
saveLocally = false;
- var season = item as Season;
-
// If season is virtual under a physical series, save locally if using compatible convention
- if (season != null && _config.Configuration.ImageSavingConvention == ImageSavingConvention.Compatible)
+ if (item is Season season && _config.Configuration.ImageSavingConvention == ImageSavingConvention.Compatible)
{
var series = season.Series;
@@ -138,7 +136,7 @@ namespace MediaBrowser.Providers.Manager
var memoryStream = new MemoryStream();
await using (source.ConfigureAwait(false))
{
- await source.CopyToAsync(memoryStream).ConfigureAwait(false);
+ await source.CopyToAsync(memoryStream, cancellationToken).ConfigureAwait(false);
}
source = memoryStream;
diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs
index ef7933b1a..e5ad0f3e0 100644
--- a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs
+++ b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs
@@ -756,7 +756,10 @@ namespace MediaBrowser.Providers.Music
_stopWatchMusicBrainz.Restart();
using var request = new HttpRequestMessage(HttpMethod.Get, requestUrl);
- response = await _httpClientFactory.CreateClient(NamedClient.MusicBrainz).SendAsync(request).ConfigureAwait(false);
+ response = await _httpClientFactory
+ .CreateClient(NamedClient.MusicBrainz)
+ .SendAsync(request, cancellationToken)
+ .ConfigureAwait(false);
// We retry a finite number of times, and only whilst MB is indicating 503 (throttling).
}
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs
index bcf9459ef..f6926d680 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs
@@ -48,8 +48,9 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
if (tmdbId == 0)
{
var movieResults = await _tmdbClientManager
- .SearchMovieAsync(searchInfo.Name, searchInfo.MetadataLanguage, cancellationToken)
+ .SearchMovieAsync(searchInfo.Name, searchInfo.Year ?? 0, searchInfo.MetadataLanguage, cancellationToken)
.ConfigureAwait(false);
+
var remoteSearchResults = new List<RemoteSearchResult>();
for (var i = 0; i < movieResults.Count; i++)
{
diff --git a/MediaBrowser.Providers/Studios/StudiosImageProvider.cs b/MediaBrowser.Providers/Studios/StudiosImageProvider.cs
index 90e13f12f..5fcf6d9aa 100644
--- a/MediaBrowser.Providers/Studios/StudiosImageProvider.cs
+++ b/MediaBrowser.Providers/Studios/StudiosImageProvider.cs
@@ -144,7 +144,7 @@ namespace MediaBrowser.Providers.Studios
var httpClient = _httpClientFactory.CreateClient(NamedClient.Default);
Directory.CreateDirectory(Path.GetDirectoryName(file));
- await using var response = await httpClient.GetStreamAsync(url).ConfigureAwait(false);
+ await using var response = await httpClient.GetStreamAsync(url, cancellationToken).ConfigureAwait(false);
await using var fileStream = new FileStream(file, FileMode.Create);
await response.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false);
}
diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
index 67700f5fb..6f164caf3 100644
--- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
+++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
@@ -12,6 +12,7 @@ using System.Xml;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.XbmcMetadata.Configuration;
@@ -24,20 +25,31 @@ namespace MediaBrowser.XbmcMetadata.Parsers
where T : BaseItem
{
private readonly IConfigurationManager _config;
+ private readonly IUserManager _userManager;
+ private readonly IUserDataManager _userDataManager;
private Dictionary<string, string> _validProviderIds;
/// <summary>
/// Initializes a new instance of the <see cref="BaseNfoParser{T}" /> class.
/// </summary>
- /// <param name="logger">The logger.</param>
- /// <param name="config">the configuration manager.</param>
- /// <param name="providerManager">The provider manager.</param>
- public BaseNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager)
+ /// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param>
+ /// <param name="config">Instance of the <see cref="IConfigurationManager"/> interface.</param>
+ /// <param name="providerManager">Instance of the <see cref="IProviderManager"/> interface.</param>
+ /// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
+ /// <param name="userDataManager">Instance of the <see cref="IUserDataManager"/> interface.</param>
+ public BaseNfoParser(
+ ILogger logger,
+ IConfigurationManager config,
+ IProviderManager providerManager,
+ IUserManager userManager,
+ IUserDataManager userDataManager)
{
Logger = logger;
_config = config;
ProviderManager = providerManager;
_validProviderIds = new Dictionary<string, string>();
+ _userManager = userManager;
+ _userDataManager = userDataManager;
}
protected CultureInfo UsCulture { get; } = new CultureInfo("en-US");
@@ -261,6 +273,14 @@ namespace MediaBrowser.XbmcMetadata.Parsers
{
var item = itemResult.Item;
+ var nfoConfiguration = _config.GetNfoConfiguration();
+ UserItemData? userData = null;
+ if (!string.IsNullOrWhiteSpace(nfoConfiguration.UserId))
+ {
+ var user = _userManager.GetUserById(Guid.Parse(nfoConfiguration.UserId));
+ userData = _userDataManager.GetUserData(user, item);
+ }
+
switch (reader.Name)
{
// DateCreated
@@ -355,6 +375,50 @@ namespace MediaBrowser.XbmcMetadata.Parsers
break;
}
+ case "watched":
+ {
+ var val = reader.ReadElementContentAsBoolean();
+
+ if (userData != null)
+ {
+ userData.Played = val;
+ }
+
+ break;
+ }
+
+ case "playcount":
+ {
+ var val = reader.ReadElementContentAsString();
+ if (!string.IsNullOrWhiteSpace(val) && userData != null)
+ {
+ if (int.TryParse(val, NumberStyles.Integer, UsCulture, out var count))
+ {
+ userData.PlayCount = count;
+ }
+ }
+
+ break;
+ }
+
+ case "lastplayed":
+ {
+ var val = reader.ReadElementContentAsString();
+ if (!string.IsNullOrWhiteSpace(val) && userData != null)
+ {
+ if (DateTime.TryParse(val, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out var added))
+ {
+ userData.LastPlayedDate = added.ToUniversalTime();
+ }
+ else
+ {
+ Logger.LogWarning("Invalid lastplayed value found: {Value}", val);
+ }
+ }
+
+ break;
+ }
+
case "countrycode":
{
var val = reader.ReadElementContentAsString();
@@ -622,7 +686,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
case "premiered":
case "releasedate":
{
- var formatString = _config.GetNfoConfiguration().ReleaseDateFormat;
+ var formatString = nfoConfiguration.ReleaseDateFormat;
var val = reader.ReadElementContentAsString();
@@ -640,7 +704,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
case "enddate":
{
- var formatString = _config.GetNfoConfiguration().ReleaseDateFormat;
+ var formatString = nfoConfiguration.ReleaseDateFormat;
var val = reader.ReadElementContentAsString();
diff --git a/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs
index 81774b873..f0c50d8e5 100644
--- a/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs
+++ b/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs
@@ -6,6 +6,7 @@ using System.Threading;
using System.Xml;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using Microsoft.Extensions.Logging;
@@ -19,11 +20,18 @@ namespace MediaBrowser.XbmcMetadata.Parsers
/// <summary>
/// Initializes a new instance of the <see cref="EpisodeNfoParser"/> class.
/// </summary>
- /// <param name="logger">The logger.</param>
- /// <param name="config">the configuration manager.</param>
- /// <param name="providerManager">The provider manager.</param>
- public EpisodeNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager)
- : base(logger, config, providerManager)
+ /// <param name="logger">Instance of the <see cref="ILogger{BaseNfoParser}"/> interface.</param>
+ /// <param name="config">Instance of the <see cref="IConfigurationManager"/> interface.</param>
+ /// <param name="providerManager">Instance of the <see cref="IProviderManager"/> interface.</param>
+ /// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
+ /// <param name="userDataManager">Instance of the <see cref="IUserDataManager"/> interface.</param>
+ public EpisodeNfoParser(
+ ILogger logger,
+ IConfigurationManager config,
+ IProviderManager providerManager,
+ IUserManager userManager,
+ IUserDataManager userDataManager)
+ : base(logger, config, providerManager, userManager, userDataManager)
{
}
diff --git a/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs
index 33b0ae887..2d0eb8433 100644
--- a/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs
+++ b/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs
@@ -5,6 +5,7 @@ using System.Xml;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
+using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using Microsoft.Extensions.Logging;
@@ -19,11 +20,18 @@ namespace MediaBrowser.XbmcMetadata.Parsers
/// <summary>
/// Initializes a new instance of the <see cref="MovieNfoParser"/> class.
/// </summary>
- /// <param name="logger">The logger.</param>
- /// <param name="config">the configuration manager.</param>
- /// <param name="providerManager">The provider manager.</param>
- public MovieNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager)
- : base(logger, config, providerManager)
+ /// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param>
+ /// <param name="config">Instance of the <see cref="IConfigurationManager"/> interface.</param>
+ /// <param name="providerManager">Instance of the <see cref="IProviderManager"/> interface.</param>
+ /// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
+ /// <param name="userDataManager">Instance of the <see cref="IUserDataManager"/> interface.</param>
+ public MovieNfoParser(
+ ILogger logger,
+ IConfigurationManager config,
+ IProviderManager providerManager,
+ IUserManager userManager,
+ IUserDataManager userDataManager)
+ : base(logger, config, providerManager, userManager, userDataManager)
{
}
diff --git a/MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs
index d8cd88b9a..bd2607bd8 100644
--- a/MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs
+++ b/MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs
@@ -2,6 +2,7 @@ using System.Globalization;
using System.Xml;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using Microsoft.Extensions.Logging;
@@ -15,11 +16,18 @@ namespace MediaBrowser.XbmcMetadata.Parsers
/// <summary>
/// Initializes a new instance of the <see cref="SeasonNfoParser"/> class.
/// </summary>
- /// <param name="logger">The logger.</param>
- /// <param name="config">the configuration manager.</param>
- /// <param name="providerManager">The provider manager.</param>
- public SeasonNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager)
- : base(logger, config, providerManager)
+ /// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param>
+ /// <param name="config">Instance of the <see cref="IConfigurationManager"/> interface.</param>
+ /// <param name="providerManager">Instance of the <see cref="IProviderManager"/> interface.</param>
+ /// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
+ /// <param name="userDataManager">Instance of the <see cref="IUserDataManager"/> interface.</param>
+ public SeasonNfoParser(
+ ILogger logger,
+ IConfigurationManager config,
+ IProviderManager providerManager,
+ IUserManager userManager,
+ IUserDataManager userDataManager)
+ : base(logger, config, providerManager, userManager, userDataManager)
{
}
diff --git a/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs
index 74a724989..fbab8b521 100644
--- a/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs
+++ b/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs
@@ -17,11 +17,18 @@ namespace MediaBrowser.XbmcMetadata.Parsers
/// <summary>
/// Initializes a new instance of the <see cref="SeriesNfoParser"/> class.
/// </summary>
- /// <param name="logger">The logger.</param>
- /// <param name="config">the configuration manager.</param>
- /// <param name="providerManager">The provider manager.</param>
- public SeriesNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager)
- : base(logger, config, providerManager)
+ /// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param>
+ /// <param name="config">Instance of the <see cref="IConfigurationManager"/> interface.</param>
+ /// <param name="providerManager">Instance of the <see cref="IProviderManager"/> interface.</param>
+ /// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
+ /// <param name="userDataManager">Instance of the <see cref="IUserDataManager"/> interface.</param>
+ public SeriesNfoParser(
+ ILogger logger,
+ IConfigurationManager config,
+ IProviderManager providerManager,
+ IUserManager userManager,
+ IUserDataManager userDataManager)
+ : base(logger, config, providerManager, userManager, userDataManager)
{
}
diff --git a/MediaBrowser.XbmcMetadata/Properties/AssemblyInfo.cs b/MediaBrowser.XbmcMetadata/Properties/AssemblyInfo.cs
index b3e2f2717..cfe06b940 100644
--- a/MediaBrowser.XbmcMetadata/Properties/AssemblyInfo.cs
+++ b/MediaBrowser.XbmcMetadata/Properties/AssemblyInfo.cs
@@ -1,5 +1,6 @@
using System.Reflection;
using System.Resources;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
@@ -14,6 +15,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: NeutralResourcesLanguage("en")]
+[assembly: InternalsVisibleTo("Jellyfin.XbmcMetadata.Tests")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
diff --git a/MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs
index 433a936d9..24f127411 100644
--- a/MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs
+++ b/MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs
@@ -2,6 +2,7 @@ using System.IO;
using System.Threading;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.IO;
using MediaBrowser.XbmcMetadata.Parsers;
@@ -17,30 +18,38 @@ namespace MediaBrowser.XbmcMetadata.Providers
private readonly ILogger<AlbumNfoProvider> _logger;
private readonly IConfigurationManager _config;
private readonly IProviderManager _providerManager;
+ private readonly IUserManager _userManager;
+ private readonly IUserDataManager _userDataManager;
/// <summary>
/// Initializes a new instance of the <see cref="AlbumNfoProvider"/> class.
/// </summary>
- /// <param name="logger">The logger.</param>
- /// <param name="fileSystem">The file system.</param>
- /// <param name="config">the configuration manager.</param>
- /// <param name="providerManager">The provider manager.</param>
+ /// <param name="logger">Instance of the <see cref="ILogger{AlbumNfoProvider}"/> interface.</param>
+ /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
+ /// <param name="config">Instance of the <see cref="IConfigurationManager"/> interface.</param>
+ /// <param name="providerManager">Instance of the <see cref="IProviderManager"/> interface.</param>
+ /// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
+ /// <param name="userDataManager">Instance of the <see cref="IUserDataManager"/> interface.</param>
public AlbumNfoProvider(
ILogger<AlbumNfoProvider> logger,
IFileSystem fileSystem,
IConfigurationManager config,
- IProviderManager providerManager)
+ IProviderManager providerManager,
+ IUserManager userManager,
+ IUserDataManager userDataManager)
: base(fileSystem)
{
_logger = logger;
_config = config;
_providerManager = providerManager;
+ _userManager = userManager;
+ _userDataManager = userDataManager;
}
/// <inheritdoc />
protected override void Fetch(MetadataResult<MusicAlbum> result, string path, CancellationToken cancellationToken)
{
- new BaseNfoParser<MusicAlbum>(_logger, _config, _providerManager).Fetch(result, path, cancellationToken);
+ new BaseNfoParser<MusicAlbum>(_logger, _config, _providerManager, _userManager, _userDataManager).Fetch(result, path, cancellationToken);
}
/// <inheritdoc />
diff --git a/MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs
index d69cdc90a..fac28ab59 100644
--- a/MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs
+++ b/MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs
@@ -2,6 +2,7 @@ using System.IO;
using System.Threading;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.IO;
using MediaBrowser.XbmcMetadata.Parsers;
@@ -17,30 +18,38 @@ namespace MediaBrowser.XbmcMetadata.Providers
private readonly ILogger<ArtistNfoProvider> _logger;
private readonly IConfigurationManager _config;
private readonly IProviderManager _providerManager;
+ private readonly IUserManager _userManager;
+ private readonly IUserDataManager _userDataManager;
/// <summary>
/// Initializes a new instance of the <see cref="ArtistNfoProvider"/> class.
/// </summary>
- /// <param name="logger">The logger.</param>
- /// <param name="fileSystem">The file system.</param>
- /// <param name="config">the configuration manager.</param>
- /// <param name="providerManager">The provider manager.</param>
+ /// <param name="logger">Instance of the <see cref="ILogger{ArtistNfoProvider}"/> interface.</param>
+ /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
+ /// <param name="config">Instance of the <see cref="IConfigurationManager"/> interface.</param>
+ /// <param name="providerManager">Instance of the <see cref="IProviderManager"/> interface.</param>
+ /// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
+ /// <param name="userDataManager">Instance of the <see cref="IUserDataManager"/> interface.</param>
public ArtistNfoProvider(
IFileSystem fileSystem,
ILogger<ArtistNfoProvider> logger,
IConfigurationManager config,
- IProviderManager providerManager)
+ IProviderManager providerManager,
+ IUserManager userManager,
+ IUserDataManager userDataManager)
: base(fileSystem)
{
_logger = logger;
_config = config;
_providerManager = providerManager;
+ _userManager = userManager;
+ _userDataManager = userDataManager;
}
/// <inheritdoc />
protected override void Fetch(MetadataResult<MusicArtist> result, string path, CancellationToken cancellationToken)
{
- new BaseNfoParser<MusicArtist>(_logger, _config, _providerManager).Fetch(result, path, cancellationToken);
+ new BaseNfoParser<MusicArtist>(_logger, _config, _providerManager, _userManager, _userDataManager).Fetch(result, path, cancellationToken);
}
/// <inheritdoc />
diff --git a/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs
index e7aa3ca07..af722748b 100644
--- a/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs
+++ b/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs
@@ -4,6 +4,7 @@ using System.Linq;
using System.Threading;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.IO;
using MediaBrowser.XbmcMetadata.Parsers;
@@ -18,17 +19,23 @@ namespace MediaBrowser.XbmcMetadata.Providers
private readonly ILogger<BaseVideoNfoProvider<T>> _logger;
private readonly IConfigurationManager _config;
private readonly IProviderManager _providerManager;
+ private readonly IUserManager _userManager;
+ private readonly IUserDataManager _userDataManager;
public BaseVideoNfoProvider(
ILogger<BaseVideoNfoProvider<T>> logger,
IFileSystem fileSystem,
IConfigurationManager config,
- IProviderManager providerManager)
+ IProviderManager providerManager,
+ IUserManager userManager,
+ IUserDataManager userDataManager)
: base(fileSystem)
{
_logger = logger;
_config = config;
_providerManager = providerManager;
+ _userManager = userManager;
+ _userDataManager = userDataManager;
}
/// <inheritdoc />
@@ -38,7 +45,7 @@ namespace MediaBrowser.XbmcMetadata.Providers
{
Item = result.Item
};
- new MovieNfoParser(_logger, _config, _providerManager).Fetch(tmpItem, path, cancellationToken);
+ new MovieNfoParser(_logger, _config, _providerManager, _userManager, _userDataManager).Fetch(tmpItem, path, cancellationToken);
result.Item = (T)tmpItem.Item;
result.People = tmpItem.People;
diff --git a/MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs
index 26983b1a6..7233f99dc 100644
--- a/MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs
+++ b/MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs
@@ -2,6 +2,7 @@ using System.IO;
using System.Threading;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.IO;
using MediaBrowser.XbmcMetadata.Parsers;
@@ -17,30 +18,38 @@ namespace MediaBrowser.XbmcMetadata.Providers
private readonly ILogger<EpisodeNfoProvider> _logger;
private readonly IConfigurationManager _config;
private readonly IProviderManager _providerManager;
+ private readonly IUserManager _userManager;
+ private readonly IUserDataManager _userDataManager;
/// <summary>
/// Initializes a new instance of the <see cref="EpisodeNfoProvider"/> class.
/// </summary>
- /// <param name="logger">The logger.</param>
- /// <param name="fileSystem">The file system.</param>
- /// <param name="config">the configuration manager.</param>
- /// <param name="providerManager">The provider manager.</param>
+ /// <param name="logger">Instance of the <see cref="ILogger{EpisodeNfoProvider}"/> interface.</param>
+ /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
+ /// <param name="config">Instance of the <see cref="IConfigurationManager"/> interface.</param>
+ /// <param name="providerManager">Instance of the <see cref="IProviderManager"/> interface.</param>
+ /// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
+ /// <param name="userDataManager">Instance of the <see cref="IUserDataManager"/> interface.</param>
public EpisodeNfoProvider(
ILogger<EpisodeNfoProvider> logger,
IFileSystem fileSystem,
IConfigurationManager config,
- IProviderManager providerManager)
+ IProviderManager providerManager,
+ IUserManager userManager,
+ IUserDataManager userDataManager)
: base(fileSystem)
{
_logger = logger;
_config = config;
_providerManager = providerManager;
+ _userManager = userManager;
+ _userDataManager = userDataManager;
}
/// <inheritdoc />
protected override void Fetch(MetadataResult<Episode> result, string path, CancellationToken cancellationToken)
{
- new EpisodeNfoParser(_logger, _config, _providerManager).Fetch(result, path, cancellationToken);
+ new EpisodeNfoParser(_logger, _config, _providerManager, _userManager, _userDataManager).Fetch(result, path, cancellationToken);
}
/// <inheritdoc />
diff --git a/MediaBrowser.XbmcMetadata/Providers/MovieNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/MovieNfoProvider.cs
index e3f6bada1..811d39a9d 100644
--- a/MediaBrowser.XbmcMetadata/Providers/MovieNfoProvider.cs
+++ b/MediaBrowser.XbmcMetadata/Providers/MovieNfoProvider.cs
@@ -1,5 +1,6 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Entities.Movies;
+using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.IO;
using Microsoft.Extensions.Logging;
@@ -14,16 +15,20 @@ namespace MediaBrowser.XbmcMetadata.Providers
/// <summary>
/// Initializes a new instance of the <see cref="MovieNfoProvider"/> class.
/// </summary>
- /// <param name="logger">The logger.</param>
- /// <param name="fileSystem">The file system.</param>
- /// <param name="config">the configuration manager.</param>
- /// <param name="providerManager">The provider manager.</param>
+ /// <param name="logger">Instance of the <see cref="ILogger{MovieNfoProvider}"/> interface.</param>
+ /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
+ /// <param name="config">Instance of the <see cref="IConfigurationManager"/> interface.</param>
+ /// <param name="providerManager">Instance of the <see cref="IProviderManager"/> interface.</param>
+ /// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
+ /// <param name="userDataManager">Instance of the <see cref="IUserDataManager"/> interface.</param>
public MovieNfoProvider(
ILogger<MovieNfoProvider> logger,
IFileSystem fileSystem,
IConfigurationManager config,
- IProviderManager providerManager)
- : base(logger, fileSystem, config, providerManager)
+ IProviderManager providerManager,
+ IUserManager userManager,
+ IUserDataManager userDataManager)
+ : base(logger, fileSystem, config, providerManager, userManager, userDataManager)
{
}
}
diff --git a/MediaBrowser.XbmcMetadata/Providers/MusicVideoNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/MusicVideoNfoProvider.cs
index b490a7120..09df509ee 100644
--- a/MediaBrowser.XbmcMetadata/Providers/MusicVideoNfoProvider.cs
+++ b/MediaBrowser.XbmcMetadata/Providers/MusicVideoNfoProvider.cs
@@ -1,5 +1,6 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.IO;
using Microsoft.Extensions.Logging;
@@ -14,12 +15,20 @@ namespace MediaBrowser.XbmcMetadata.Providers
/// <summary>
/// Initializes a new instance of the <see cref="MusicVideoNfoProvider"/> class.
/// </summary>
- /// <param name="logger">The logger.</param>
- /// <param name="fileSystem">The file system.</param>
- /// <param name="config">the configuration manager.</param>
- /// <param name="providerManager">The provider manager.</param>
- public MusicVideoNfoProvider(ILogger<MusicVideoNfoProvider> logger, IFileSystem fileSystem, IConfigurationManager config, IProviderManager providerManager)
- : base(logger, fileSystem, config, providerManager)
+ /// <param name="logger">Instance of the <see cref="ILogger{SeasonNfoProvider}"/> interface.</param>
+ /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
+ /// <param name="config">Instance of the <see cref="IConfigurationManager"/> interface.</param>
+ /// <param name="providerManager">Instance of the <see cref="IProviderManager"/> interface.</param>
+ /// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
+ /// <param name="userDataManager">Instance of the <see cref="IUserDataManager"/> interface.</param>
+ public MusicVideoNfoProvider(
+ ILogger<MusicVideoNfoProvider> logger,
+ IFileSystem fileSystem,
+ IConfigurationManager config,
+ IProviderManager providerManager,
+ IUserManager userManager,
+ IUserDataManager userDataManager)
+ : base(logger, fileSystem, config, providerManager, userManager, userDataManager)
{
}
}
diff --git a/MediaBrowser.XbmcMetadata/Providers/SeasonNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/SeasonNfoProvider.cs
index 0603fd0d1..8f0ed6df7 100644
--- a/MediaBrowser.XbmcMetadata/Providers/SeasonNfoProvider.cs
+++ b/MediaBrowser.XbmcMetadata/Providers/SeasonNfoProvider.cs
@@ -2,6 +2,7 @@ using System.IO;
using System.Threading;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.IO;
using MediaBrowser.XbmcMetadata.Parsers;
@@ -17,30 +18,38 @@ namespace MediaBrowser.XbmcMetadata.Providers
private readonly ILogger<SeasonNfoProvider> _logger;
private readonly IConfigurationManager _config;
private readonly IProviderManager _providerManager;
+ private readonly IUserManager _userManager;
+ private readonly IUserDataManager _userDataManager;
/// <summary>
/// Initializes a new instance of the <see cref="SeasonNfoProvider"/> class.
/// </summary>
- /// <param name="logger">The logger.</param>
- /// <param name="fileSystem">The file system.</param>
- /// <param name="config">the configuration manager.</param>
- /// <param name="providerManager">The provider manager.</param>
+ /// <param name="logger">Instance of the <see cref="ILogger{SeasonNfoProvider}"/> interface.</param>
+ /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
+ /// <param name="config">Instance of the <see cref="IConfigurationManager"/> interface.</param>
+ /// <param name="providerManager">Instance of the <see cref="IProviderManager"/> interface.</param>
+ /// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
+ /// <param name="userDataManager">Instance of the <see cref="IUserDataManager"/> interface.</param>
public SeasonNfoProvider(
ILogger<SeasonNfoProvider> logger,
IFileSystem fileSystem,
IConfigurationManager config,
- IProviderManager providerManager)
+ IProviderManager providerManager,
+ IUserManager userManager,
+ IUserDataManager userDataManager)
: base(fileSystem)
{
_logger = logger;
_config = config;
_providerManager = providerManager;
+ _userManager = userManager;
+ _userDataManager = userDataManager;
}
/// <inheritdoc />
protected override void Fetch(MetadataResult<Season> result, string path, CancellationToken cancellationToken)
{
- new SeasonNfoParser(_logger, _config, _providerManager).Fetch(result, path, cancellationToken);
+ new SeasonNfoParser(_logger, _config, _providerManager, _userManager, _userDataManager).Fetch(result, path, cancellationToken);
}
/// <inheritdoc />
diff --git a/MediaBrowser.XbmcMetadata/Providers/SeriesNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/SeriesNfoProvider.cs
index 7e059e0aa..3e496dc58 100644
--- a/MediaBrowser.XbmcMetadata/Providers/SeriesNfoProvider.cs
+++ b/MediaBrowser.XbmcMetadata/Providers/SeriesNfoProvider.cs
@@ -2,6 +2,7 @@ using System.IO;
using System.Threading;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.IO;
using MediaBrowser.XbmcMetadata.Parsers;
@@ -17,30 +18,38 @@ namespace MediaBrowser.XbmcMetadata.Providers
private readonly ILogger<SeriesNfoProvider> _logger;
private readonly IConfigurationManager _config;
private readonly IProviderManager _providerManager;
+ private readonly IUserManager _userManager;
+ private readonly IUserDataManager _userDataManager;
/// <summary>
/// Initializes a new instance of the <see cref="SeriesNfoProvider"/> class.
/// </summary>
- /// <param name="logger">The logger.</param>
- /// <param name="fileSystem">The file system.</param>
- /// <param name="config">the configuration manager.</param>
- /// <param name="providerManager">The provider manager.</param>
+ /// <param name="logger">Instance of the <see cref="ILogger{SeriesNfoProvider}"/> interface.</param>
+ /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
+ /// <param name="config">Instance of the <see cref="IConfigurationManager"/> interface.</param>
+ /// <param name="providerManager">Instance of the <see cref="IProviderManager"/> interface.</param>
+ /// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
+ /// <param name="userDataManager">Instance of the <see cref="IUserDataManager"/> interface.</param>
public SeriesNfoProvider(
ILogger<SeriesNfoProvider> logger,
IFileSystem fileSystem,
IConfigurationManager config,
- IProviderManager providerManager)
+ IProviderManager providerManager,
+ IUserManager userManager,
+ IUserDataManager userDataManager)
: base(fileSystem)
{
_logger = logger;
_config = config;
_providerManager = providerManager;
+ _userManager = userManager;
+ _userDataManager = userDataManager;
}
/// <inheritdoc />
protected override void Fetch(MetadataResult<Series> result, string path, CancellationToken cancellationToken)
{
- new SeriesNfoParser(_logger, _config, _providerManager).Fetch(result, path, cancellationToken);
+ new SeriesNfoParser(_logger, _config, _providerManager, _userManager, _userDataManager).Fetch(result, path, cancellationToken);
}
/// <inheritdoc />
diff --git a/MediaBrowser.XbmcMetadata/Providers/VideoNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/VideoNfoProvider.cs
index f66ad30ca..4717d81e6 100644
--- a/MediaBrowser.XbmcMetadata/Providers/VideoNfoProvider.cs
+++ b/MediaBrowser.XbmcMetadata/Providers/VideoNfoProvider.cs
@@ -1,5 +1,6 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.IO;
using Microsoft.Extensions.Logging;
@@ -14,12 +15,20 @@ namespace MediaBrowser.XbmcMetadata.Providers
/// <summary>
/// Initializes a new instance of the <see cref="VideoNfoProvider"/> class.
/// </summary>
- /// <param name="logger">The logger.</param>
- /// <param name="fileSystem">The file system.</param>
- /// <param name="config">the configuration manager.</param>
- /// <param name="providerManager">The provider manager.</param>
- public VideoNfoProvider(ILogger<VideoNfoProvider> logger, IFileSystem fileSystem, IConfigurationManager config, IProviderManager providerManager)
- : base(logger, fileSystem, config, providerManager)
+ /// <param name="logger">Instance of the <see cref="ILogger{VideoNfoProvider}"/> interface.</param>
+ /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
+ /// <param name="config">Instance of the <see cref="IConfigurationManager"/> interface.</param>
+ /// <param name="providerManager">Instance of the <see cref="IProviderManager"/> interface.</param>
+ /// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
+ /// <param name="userDataManager">Instance of the <see cref="IUserDataManager"/> interface.</param>
+ public VideoNfoProvider(
+ ILogger<VideoNfoProvider> logger,
+ IFileSystem fileSystem,
+ IConfigurationManager config,
+ IProviderManager providerManager,
+ IUserManager userManager,
+ IUserDataManager userDataManager)
+ : base(logger, fileSystem, config, providerManager, userManager, userDataManager)
{
}
}
diff --git a/jellyfin.ruleset b/jellyfin.ruleset
index 371f02566..fa09bfb66 100644
--- a/jellyfin.ruleset
+++ b/jellyfin.ruleset
@@ -37,6 +37,10 @@
</Rules>
<Rules AnalyzerId="Microsoft.CodeAnalysis.FxCopAnalyzers" RuleNamespace="Microsoft.Design">
+ <!-- error on CA2016: Forward the CancellationToken parameter to methods that take one
+ or pass in 'CancellationToken.None' explicitly to indicate intentionally not propagating the token -->
+ <Rule Id="CA2016" Action="Error" />
+
<!-- disable warning CA1031: Do not catch general exception types -->
<Rule Id="CA1031" Action="Info" />
<!-- disable warning CA1032: Implement standard exception constructors -->
diff --git a/tests/Jellyfin.Api.Tests/Helpers/RequestHelpersTests.cs b/tests/Jellyfin.Api.Tests/Helpers/RequestHelpersTests.cs
index 606041c7f..97e441b1d 100644
--- a/tests/Jellyfin.Api.Tests/Helpers/RequestHelpersTests.cs
+++ b/tests/Jellyfin.Api.Tests/Helpers/RequestHelpersTests.cs
@@ -6,11 +6,11 @@ using Xunit;
namespace Jellyfin.Api.Tests.Helpers
{
- public class RequestHelpersTests
+ public static class RequestHelpersTests
{
[Theory]
[MemberData(nameof(GetOrderBy_Success_TestData))]
- public void GetOrderBy_Success(IReadOnlyList<string> sortBy, IReadOnlyList<SortOrder> requestedSortOrder, (string, SortOrder)[] expected)
+ public static void GetOrderBy_Success(IReadOnlyList<string> sortBy, IReadOnlyList<SortOrder> requestedSortOrder, (string, SortOrder)[] expected)
{
Assert.Equal(expected, RequestHelpers.GetOrderBy(sortBy, requestedSortOrder));
}
@@ -55,5 +55,35 @@ namespace Jellyfin.Api.Tests.Helpers
}
};
}
+
+ [Fact]
+ public static void GetItemTypeStrings_Empty_Empty()
+ {
+ Assert.Empty(RequestHelpers.GetItemTypeStrings(Array.Empty<BaseItemKind>()));
+ }
+
+ [Fact]
+ public static void GetItemTypeStrings_Valid_Success()
+ {
+ BaseItemKind[] input =
+ {
+ BaseItemKind.AggregateFolder,
+ BaseItemKind.Audio,
+ BaseItemKind.BasePluginFolder,
+ BaseItemKind.CollectionFolder
+ };
+
+ string[] expected =
+ {
+ "AggregateFolder",
+ "Audio",
+ "BasePluginFolder",
+ "CollectionFolder"
+ };
+
+ var res = RequestHelpers.GetItemTypeStrings(input);
+
+ Assert.Equal(expected, res);
+ }
}
}
diff --git a/tests/Jellyfin.Naming.Tests/Video/VideoListResolverTests.cs b/tests/Jellyfin.Naming.Tests/Video/VideoListResolverTests.cs
index 215c7e540..08af76669 100644
--- a/tests/Jellyfin.Naming.Tests/Video/VideoListResolverTests.cs
+++ b/tests/Jellyfin.Naming.Tests/Video/VideoListResolverTests.cs
@@ -1,3 +1,4 @@
+using System;
using System.Linq;
using Emby.Naming.Common;
using Emby.Naming.Video;
@@ -8,11 +9,10 @@ namespace Jellyfin.Naming.Tests.Video
{
public class VideoListResolverTests
{
- private readonly NamingOptions _namingOptions = new NamingOptions();
+ private readonly VideoListResolver _videoListResolver = new VideoListResolver(new NamingOptions());
- // FIXME
- // [Fact]
- private void TestStackAndExtras()
+ [Fact]
+ public void TestStackAndExtras()
{
// No stacking here because there is no part/disc/etc
var files = new[]
@@ -40,23 +40,22 @@ namespace Jellyfin.Naming.Tests.Video
"WillyWonka-trailer.mkv"
};
- var resolver = GetResolver();
-
- var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ var result = _videoListResolver.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);
+ var batman = result.FirstOrDefault(x => string.Equals(x.Name, "Batman", StringComparison.Ordinal));
+ Assert.NotNull(batman);
+ Assert.Equal(3, batman!.Files.Count);
+ Assert.Equal(3, batman!.Extras.Count);
+
+ var harry = result.FirstOrDefault(x => string.Equals(x.Name, "Harry Potter and the Deathly Hallows", StringComparison.Ordinal));
+ Assert.NotNull(harry);
+ Assert.Equal(4, harry!.Files.Count);
+ Assert.Equal(2, harry!.Extras.Count);
}
[Fact]
@@ -68,9 +67,7 @@ namespace Jellyfin.Naming.Tests.Video
"300.nfo"
};
- var resolver = GetResolver();
-
- var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
{
IsDirectory = false,
FullName = i
@@ -88,9 +85,7 @@ namespace Jellyfin.Naming.Tests.Video
"300 trailer.mkv"
};
- var resolver = GetResolver();
-
- var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
{
IsDirectory = false,
FullName = i
@@ -108,9 +103,7 @@ namespace Jellyfin.Naming.Tests.Video
"X-Men Days of Future Past-trailer.mp4"
};
- var resolver = GetResolver();
-
- var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
{
IsDirectory = false,
FullName = i
@@ -129,9 +122,7 @@ namespace Jellyfin.Naming.Tests.Video
"X-Men Days of Future Past-trailer2.mp4"
};
- var resolver = GetResolver();
-
- var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
{
IsDirectory = false,
FullName = i
@@ -149,9 +140,7 @@ namespace Jellyfin.Naming.Tests.Video
"Looper.2012.bluray.720p.x264.mkv"
};
- var resolver = GetResolver();
-
- var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
{
IsDirectory = false,
FullName = i
@@ -173,9 +162,7 @@ namespace Jellyfin.Naming.Tests.Video
"My video 5.mkv"
};
- var resolver = GetResolver();
-
- var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
{
IsDirectory = false,
FullName = i
@@ -193,9 +180,7 @@ namespace Jellyfin.Naming.Tests.Video
@"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
+ var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
{
IsDirectory = true,
FullName = i
@@ -214,9 +199,7 @@ namespace Jellyfin.Naming.Tests.Video
@"My movie #2.mp4"
};
- var resolver = GetResolver();
-
- var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
{
IsDirectory = true,
FullName = i
@@ -235,9 +218,7 @@ namespace Jellyfin.Naming.Tests.Video
@"No (2012) part1-trailer.mp4"
};
- var resolver = GetResolver();
-
- var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
{
IsDirectory = false,
FullName = i
@@ -256,9 +237,7 @@ namespace Jellyfin.Naming.Tests.Video
@"No (2012)-trailer.mp4"
};
- var resolver = GetResolver();
-
- var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
{
IsDirectory = false,
FullName = i
@@ -278,9 +257,7 @@ namespace Jellyfin.Naming.Tests.Video
@"trailer.mp4"
};
- var resolver = GetResolver();
-
- var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
{
IsDirectory = false,
FullName = i
@@ -300,9 +277,7 @@ namespace Jellyfin.Naming.Tests.Video
@"/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
+ var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
{
IsDirectory = false,
FullName = i
@@ -319,9 +294,7 @@ namespace Jellyfin.Naming.Tests.Video
@"/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
+ var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
{
IsDirectory = false,
FullName = i
@@ -338,9 +311,7 @@ namespace Jellyfin.Naming.Tests.Video
@"The Colony.mkv"
};
- var resolver = GetResolver();
-
- var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
{
IsDirectory = false,
FullName = i
@@ -358,9 +329,7 @@ namespace Jellyfin.Naming.Tests.Video
@"Four Sisters and a Wedding - B.avi"
};
- var resolver = GetResolver();
-
- var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
{
IsDirectory = false,
FullName = i
@@ -378,9 +347,7 @@ namespace Jellyfin.Naming.Tests.Video
@"Four Rooms - A.mp4"
};
- var resolver = GetResolver();
-
- var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
{
IsDirectory = false,
FullName = i
@@ -398,9 +365,7 @@ namespace Jellyfin.Naming.Tests.Video
@"/Server/Despicable Me/movie-trailer.mkv"
};
- var resolver = GetResolver();
-
- var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
{
IsDirectory = false,
FullName = i
@@ -420,9 +385,7 @@ namespace Jellyfin.Naming.Tests.Video
@"/Server/Despicable Me/Baywatch (2017) - Trailer.mkv"
};
- var resolver = GetResolver();
-
- var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
{
IsDirectory = false,
FullName = i
@@ -440,9 +403,7 @@ namespace Jellyfin.Naming.Tests.Video
@"/Movies/Despicable Me/trailers/trailer.mkv"
};
- var resolver = GetResolver();
-
- var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
{
IsDirectory = false,
FullName = i
@@ -457,10 +418,5 @@ namespace Jellyfin.Naming.Tests.Video
var stack = new FileStack();
Assert.False(stack.ContainsFile("XX", true));
}
-
- private VideoListResolver GetResolver()
- {
- return new VideoListResolver(_namingOptions);
- }
}
}
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj
index 174f29b09..c3b3155fe 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj
+++ b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj
@@ -39,6 +39,7 @@
<ItemGroup>
<ProjectReference Include="..\..\Emby.Server.Implementations\Emby.Server.Implementations.csproj" />
+ <ProjectReference Include="..\..\Jellyfin.Server.Implementations\Jellyfin.Server.Implementations.csproj" />
</ItemGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Users/UserManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Users/UserManagerTests.cs
new file mode 100644
index 000000000..867dda29d
--- /dev/null
+++ b/tests/Jellyfin.Server.Implementations.Tests/Users/UserManagerTests.cs
@@ -0,0 +1,28 @@
+using System;
+using Jellyfin.Server.Implementations.Users;
+using Xunit;
+
+namespace Jellyfin.Server.Implementations.Tests.Users
+{
+ public class UserManagerTests
+ {
+ [Theory]
+ [InlineData("this_is_valid")]
+ [InlineData("this is also valid")]
+ [InlineData("0@_-' .")]
+ public void ThrowIfInvalidUsername_WhenValidUsername_DoesNotThrowArgumentException(string username)
+ {
+ var ex = Record.Exception(() => UserManager.ThrowIfInvalidUsername(username));
+ Assert.Null(ex);
+ }
+
+ [Theory]
+ [InlineData(" ")]
+ [InlineData("")]
+ [InlineData("special characters like & $ ? are not allowed")]
+ public void ThrowIfInvalidUsername_WhenInvalidUsername_ThrowsArgumentException(string username)
+ {
+ Assert.Throws<ArgumentException>(() => UserManager.ThrowIfInvalidUsername(username));
+ }
+ }
+}
diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Location/MovieNfoLocationTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Location/MovieNfoLocationTests.cs
new file mode 100644
index 000000000..357d61c0b
--- /dev/null
+++ b/tests/Jellyfin.XbmcMetadata.Tests/Location/MovieNfoLocationTests.cs
@@ -0,0 +1,65 @@
+using System.Linq;
+using MediaBrowser.Controller.Entities.Movies;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.System;
+using MediaBrowser.XbmcMetadata.Savers;
+using Xunit;
+
+namespace Jellyfin.XbmcMetadata.Tests.Location
+{
+ public class MovieNfoLocationTests
+ {
+ [Fact]
+ public static void Movie_MixedFolder_Success()
+ {
+ var movie = new Movie() { Path = "/media/movies/Avengers Endgame.mp4", IsInMixedFolder = true };
+
+ var paths = MovieNfoSaver.GetMovieSavePaths(new ItemInfo(movie)).ToArray();
+ Assert.Single(paths);
+ Assert.Contains("/media/movies/Avengers Endgame.nfo", paths);
+ }
+
+ [Fact]
+ public static void Movie_SeparateFolder_Success()
+ {
+ var movie = new Movie() { Path = "/media/movies/Avengers Endgame/Avengers Endgame.mp4" };
+ var path1 = "/media/movies/Avengers Endgame/Avengers Endgame.nfo";
+ var path2 = "/media/movies/Avengers Endgame/movie.nfo";
+
+ // uses ContainingFolderPath which uses Operating system specific paths
+ if (MediaBrowser.Common.System.OperatingSystem.Id == OperatingSystemId.Windows)
+ {
+ movie.Path = movie.Path.Replace('/', '\\');
+ path1 = path1.Replace('/', '\\');
+ path2 = path2.Replace('/', '\\');
+ }
+
+ var paths = MovieNfoSaver.GetMovieSavePaths(new ItemInfo(movie)).ToArray();
+ Assert.Equal(2, paths.Length);
+ Assert.Contains(path1, paths);
+ Assert.Contains(path2, paths);
+ }
+
+ [Fact]
+ public void Movie_DVD_Success()
+ {
+ var movie = new Movie() { Path = "/media/movies/Avengers Endgame", VideoType = VideoType.Dvd };
+ var path1 = "/media/movies/Avengers Endgame/Avengers Endgame.nfo";
+ var path2 = "/media/movies/Avengers Endgame/VIDEO_TS/VIDEO_TS.nfo";
+
+ // uses ContainingFolderPath which uses Operating system specific paths
+ if (MediaBrowser.Common.System.OperatingSystem.Id == OperatingSystemId.Windows)
+ {
+ movie.Path = movie.Path.Replace('/', '\\');
+ path1 = path1.Replace('/', '\\');
+ path2 = path2.Replace('/', '\\');
+ }
+
+ var paths = MovieNfoSaver.GetMovieSavePaths(new ItemInfo(movie)).ToArray();
+ Assert.Equal(2, paths.Length);
+ Assert.Contains(path1, paths);
+ Assert.Contains(path2, paths);
+ }
+ }
+}
diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/EpisodeNfoProviderTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/EpisodeNfoProviderTests.cs
index d0cd8b287..d10ef9b47 100644
--- a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/EpisodeNfoProviderTests.cs
+++ b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/EpisodeNfoProviderTests.cs
@@ -3,6 +3,7 @@ using System.Linq;
using System.Threading;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
@@ -34,7 +35,10 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers
var config = new Mock<IConfigurationManager>();
config.Setup(x => x.GetConfiguration(It.IsAny<string>()))
.Returns(new XbmcMetadataOptions());
- _parser = new EpisodeNfoParser(new NullLogger<EpisodeNfoParser>(), config.Object, providerManager.Object);
+ var user = new Mock<IUserManager>();
+ var userData = new Mock<IUserDataManager>();
+
+ _parser = new EpisodeNfoParser(new NullLogger<EpisodeNfoParser>(), config.Object, providerManager.Object, user.Object, userData.Object);
}
[Fact]
@@ -62,6 +66,10 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers
Assert.Equal(2017, item.ProductionYear);
Assert.Single(item.Studios);
Assert.Contains("Starz", item.Studios);
+ Assert.Equal(1, item.IndexNumberEnd);
+ Assert.Equal(2, item.AirsAfterSeasonNumber);
+ Assert.Equal(3, item.AirsBeforeSeasonNumber);
+ Assert.Equal(1, item.AirsBeforeEpisodeNumber);
Assert.Equal("tt5017734", item.ProviderIds[MetadataProvider.Imdb.ToString()]);
Assert.Equal("1276153", item.ProviderIds[MetadataProvider.Tmdb.ToString()]);
diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs
index 2f7ee65d5..76231391e 100644
--- a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs
+++ b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs
@@ -1,8 +1,11 @@
using System;
using System.Linq;
using System.Threading;
+using Jellyfin.Data.Entities;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Movies;
+using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
@@ -18,9 +21,13 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers
public class MovieNfoParserTests
{
private readonly MovieNfoParser _parser;
+ private readonly IUserDataManager _userDataManager;
+ private readonly User _testUser;
public MovieNfoParserTests()
{
+ _testUser = new User("Test User", "Auth provider", "Reset provider");
+
var providerManager = new Mock<IProviderManager>();
var tmdbExternalId = new TmdbMovieExternalId();
@@ -29,10 +36,24 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers
providerManager.Setup(x => x.GetExternalIdInfos(It.IsAny<IHasProviderIds>()))
.Returns(new[] { externalIdInfo });
- var config = new Mock<IConfigurationManager>();
- config.Setup(x => x.GetConfiguration(It.IsAny<string>()))
- .Returns(new XbmcMetadataOptions());
- _parser = new MovieNfoParser(new NullLogger<MovieNfoParser>(), config.Object, providerManager.Object);
+ var nfoConfig = new XbmcMetadataOptions()
+ {
+ UserId = "F38E6443-090B-4F7A-BD12-9CFF5020F7BC"
+ };
+ var configManager = new Mock<IConfigurationManager>();
+ configManager.Setup(x => x.GetConfiguration(It.IsAny<string>()))
+ .Returns(nfoConfig);
+
+ var user = new Mock<IUserManager>();
+ user.Setup(x => x.GetUserById(It.IsAny<Guid>()))
+ .Returns(_testUser);
+
+ var userData = new Mock<IUserDataManager>();
+ userData.Setup(x => x.GetUserData(_testUser, It.IsAny<BaseItem>()))
+ .Returns(new UserItemData());
+
+ _userDataManager = userData.Object;
+ _parser = new MovieNfoParser(new NullLogger<MovieNfoParser>(), configManager.Object, providerManager.Object, user.Object, userData.Object);
}
[Fact]
@@ -40,11 +61,11 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers
{
var result = new MetadataResult<Video>()
{
- Item = new Video()
+ Item = new Movie()
};
_parser.Fetch(result, "Test Data/Justice League.nfo", CancellationToken.None);
- var item = result.Item;
+ var item = (Movie)result.Item;
Assert.Equal("Justice League", item.OriginalTitle);
Assert.Equal("Justice for all.", item.Tagline);
@@ -58,22 +79,31 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers
Assert.Contains("Sci-Fi", item.Genres);
Assert.Equal(new DateTime(2017, 11, 15), item.PremiereDate);
+ Assert.Equal(new DateTime(2017, 11, 16), item.EndDate);
Assert.Single(item.Studios);
Assert.Contains("DC Comics", item.Studios);
Assert.Equal("1.777778", item.AspectRatio);
+ Assert.Equal(Video3DFormat.HalfSideBySide, item.Video3DFormat);
Assert.Equal(1920, item.Width);
Assert.Equal(1080, item.Height);
Assert.Equal(new TimeSpan(0, 0, 6268).Ticks, item.RunTimeTicks);
Assert.True(item.HasSubtitles);
+ Assert.Equal(7.6f, item.CriticRating);
+ Assert.Equal("8.7", item.CustomRating);
+ Assert.Equal("en", item.PreferredMetadataLanguage);
+ Assert.Equal("us", item.PreferredMetadataCountryCode);
+ Assert.Single(item.RemoteTrailers);
+ Assert.Equal("https://www.youtube.com/watch?v=dQw4w9WgXcQ", item.RemoteTrailers[0].Url);
- Assert.Equal(19, result.People.Count);
+ Assert.Equal(20, result.People.Count);
var writers = result.People.Where(x => x.Type == PersonType.Writer).ToArray();
- Assert.Equal(2, writers.Length);
+ Assert.Equal(3, writers.Length);
var writerNames = writers.Select(x => x.Name);
Assert.Contains("Jerry Siegel", writerNames);
Assert.Contains("Joe Shuster", writerNames);
+ Assert.Contains("Test", writerNames);
var directors = result.People.Where(x => x.Type == PersonType.Director).ToArray();
Assert.Single(directors);
@@ -94,6 +124,32 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers
Assert.Equal("Test Lyricist", lyricist!.Name);
Assert.Equal(new DateTime(2019, 8, 6, 9, 1, 18), item.DateCreated);
+
+ // userData
+ var userData = _userDataManager.GetUserData(_testUser, item);
+ Assert.Equal(2, userData.PlayCount);
+ Assert.True(userData.Played);
+ Assert.Equal(new DateTime(2021, 02, 11, 07, 47, 23), userData.LastPlayedDate);
+
+ // Movie set
+ Assert.Equal("702342", item.ProviderIds[MetadataProvider.TmdbCollection.ToString()]);
+ Assert.Equal("Justice League Collection", item.CollectionName);
+ }
+
+ [Theory]
+ [InlineData("Test Data/Tmdb.nfo", "Tmdb", "30287")]
+ [InlineData("Test Data/Imdb.nfo", "Imdb", "tt0944947")]
+ public void Parse_UrlFile_Success(string path, string provider, string id)
+ {
+ var result = new MetadataResult<Video>()
+ {
+ Item = new Movie()
+ };
+
+ _parser.Fetch(result, path, CancellationToken.None);
+ var item = (Movie)result.Item;
+
+ Assert.Equal(id, item.ProviderIds[provider]);
}
[Fact]
@@ -109,7 +165,7 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers
{
var result = new MetadataResult<Video>()
{
- Item = new Video()
+ Item = new Movie()
};
Assert.Throws<ArgumentException>(() => _parser.Fetch(result, string.Empty, CancellationToken.None));
diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicAlbumNfoProviderTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicAlbumNfoProviderTests.cs
index 1fe56cadd..2183d2a2f 100644
--- a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicAlbumNfoProviderTests.cs
+++ b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicAlbumNfoProviderTests.cs
@@ -5,6 +5,7 @@ using System.Linq;
using System.Threading;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
@@ -35,7 +36,10 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers
var config = new Mock<IConfigurationManager>();
config.Setup(x => x.GetConfiguration(It.IsAny<string>()))
.Returns(new XbmcMetadataOptions());
- _parser = new BaseNfoParser<MusicAlbum>(new NullLogger<BaseNfoParser<MusicAlbum>>(), config.Object, providerManager.Object);
+ var user = new Mock<IUserManager>();
+ var userData = new Mock<IUserDataManager>();
+
+ _parser = new BaseNfoParser<MusicAlbum>(new NullLogger<BaseNfoParser<MusicAlbum>>(), config.Object, providerManager.Object, user.Object, userData.Object);
}
[Fact]
diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicArtistNfoParserTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicArtistNfoParserTests.cs
index 4869cf088..f86b7604e 100644
--- a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicArtistNfoParserTests.cs
+++ b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicArtistNfoParserTests.cs
@@ -3,6 +3,7 @@ using System.Linq;
using System.Threading;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
@@ -32,7 +33,10 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers
var config = new Mock<IConfigurationManager>();
config.Setup(x => x.GetConfiguration(It.IsAny<string>()))
.Returns(new XbmcMetadataOptions());
- _parser = new BaseNfoParser<MusicArtist>(new NullLogger<BaseNfoParser<MusicArtist>>(), config.Object, providerManager.Object);
+ var user = new Mock<IUserManager>();
+ var userData = new Mock<IUserDataManager>();
+
+ _parser = new BaseNfoParser<MusicArtist>(new NullLogger<BaseNfoParser<MusicArtist>>(), config.Object, providerManager.Object, user.Object, userData.Object);
}
[Fact]
diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicVideoNfoParserTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicVideoNfoParserTests.cs
new file mode 100644
index 000000000..898554936
--- /dev/null
+++ b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicVideoNfoParserTests.cs
@@ -0,0 +1,73 @@
+using System;
+using System.Linq;
+using System.Threading;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Configuration;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Providers;
+using MediaBrowser.XbmcMetadata.Parsers;
+using Microsoft.Extensions.Logging.Abstractions;
+using Moq;
+using Xunit;
+
+namespace Jellyfin.XbmcMetadata.Tests.Parsers
+{
+ public class MusicVideoNfoParserTests
+ {
+ private readonly MovieNfoParser _parser;
+
+ public MusicVideoNfoParserTests()
+ {
+ var providerManager = new Mock<IProviderManager>();
+ providerManager.Setup(x => x.GetExternalIdInfos(It.IsAny<IHasProviderIds>()))
+ .Returns(Enumerable.Empty<ExternalIdInfo>());
+ var config = new Mock<IConfigurationManager>();
+ config.Setup(x => x.GetConfiguration(It.IsAny<string>()))
+ .Returns(new XbmcMetadataOptions());
+
+ var user = new Mock<IUserManager>();
+ var userData = new Mock<IUserDataManager>();
+
+ _parser = new MovieNfoParser(new NullLogger<BaseNfoParser<MusicVideo>>(), config.Object, providerManager.Object, user.Object, userData.Object);
+ }
+
+ [Fact]
+ public void Fetch_Valid_Succes()
+ {
+ var result = new MetadataResult<Video>()
+ {
+ Item = new MusicVideo()
+ };
+
+ _parser.Fetch(result, "Test Data/Dancing Queen.nfo", CancellationToken.None);
+ var item = (MusicVideo)result.Item;
+
+ Assert.Equal("Dancing Queen", item.Name);
+ Assert.Single(item.Artists);
+ Assert.Contains("ABBA", item.Artists);
+ Assert.Equal("Arrival", item.Album);
+ }
+
+ [Fact]
+ public void Fetch_WithNullItem_ThrowsArgumentException()
+ {
+ var result = new MetadataResult<Video>();
+
+ Assert.Throws<ArgumentException>(() => _parser.Fetch(result, "Test Data/Dancing Queen.nfo", CancellationToken.None));
+ }
+
+ [Fact]
+ public void Fetch_NullResult_ThrowsArgumentException()
+ {
+ var result = new MetadataResult<Video>()
+ {
+ Item = new MusicVideo()
+ };
+
+ Assert.Throws<ArgumentException>(() => _parser.Fetch(result, string.Empty, CancellationToken.None));
+ }
+ }
+}
diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/SeasonNfoProviderTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/SeasonNfoProviderTests.cs
index 68b7239d2..602db7c09 100644
--- a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/SeasonNfoProviderTests.cs
+++ b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/SeasonNfoProviderTests.cs
@@ -5,6 +5,7 @@ using System.Linq;
using System.Threading;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
@@ -28,7 +29,10 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers
var config = new Mock<IConfigurationManager>();
config.Setup(x => x.GetConfiguration(It.IsAny<string>()))
.Returns(new XbmcMetadataOptions());
- _parser = new SeasonNfoParser(new NullLogger<SeasonNfoParser>(), config.Object, providerManager.Object);
+ var user = new Mock<IUserManager>();
+ var userData = new Mock<IUserDataManager>();
+
+ _parser = new SeasonNfoParser(new NullLogger<SeasonNfoParser>(), config.Object, providerManager.Object, user.Object, userData.Object);
}
[Fact]
diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/SeriesNfoParserTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/SeriesNfoParserTests.cs
index 9e535182e..f8eb04b3a 100644
--- a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/SeriesNfoParserTests.cs
+++ b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/SeriesNfoParserTests.cs
@@ -3,6 +3,7 @@ using System.Linq;
using System.Threading;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
@@ -26,7 +27,10 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers
var config = new Mock<IConfigurationManager>();
config.Setup(x => x.GetConfiguration(It.IsAny<string>()))
.Returns(new XbmcMetadataOptions());
- _parser = new SeriesNfoParser(new NullLogger<SeriesNfoParser>(), config.Object, providerManager.Object);
+ var user = new Mock<IUserManager>();
+ var userData = new Mock<IUserDataManager>();
+
+ _parser = new SeriesNfoParser(new NullLogger<SeriesNfoParser>(), config.Object, providerManager.Object, user.Object, userData.Object);
}
[Fact]
@@ -45,6 +49,7 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers
Assert.Equal(0, item.RunTimeTicks);
Assert.Equal("46639", item.ProviderIds[MetadataProvider.Tmdb.ToString()]);
Assert.Equal("253573", item.ProviderIds[MetadataProvider.Tvdb.ToString()]);
+ Assert.Equal("tt11111", item.ProviderIds[MetadataProvider.Imdb.ToString()]);
Assert.Equal(3, item.Genres.Length);
Assert.Contains("Drama", item.Genres);
@@ -54,6 +59,10 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers
Assert.Equal(new DateTime(2017, 4, 30), item.PremiereDate);
Assert.Single(item.Studios);
Assert.Contains("Starz", item.Studios);
+ Assert.Equal("9 PM", item.AirTime);
+ Assert.Single(item.AirDays);
+ Assert.Contains(DayOfWeek.Friday, item.AirDays);
+ Assert.Equal(SeriesStatus.Ended, item.Status);
Assert.Equal(6, result.People.Count);
@@ -69,6 +78,21 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers
Assert.Equal(new DateTime(2017, 10, 7, 14, 25, 47), item.DateCreated);
}
+ [Theory]
+ [InlineData("Test Data/Tvdb.nfo", "Tvdb", "121361")]
+ public void Parse_UrlFile_Success(string path, string provider, string id)
+ {
+ var result = new MetadataResult<Series>()
+ {
+ Item = new Series()
+ };
+
+ _parser.Fetch(result, path, CancellationToken.None);
+ var item = (Series)result.Item;
+
+ Assert.Equal(id, item.ProviderIds[provider]);
+ }
+
[Fact]
public void Fetch_WithNullItem_ThrowsArgumentException()
{
diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Test Data/American Gods.nfo b/tests/Jellyfin.XbmcMetadata.Tests/Test Data/American Gods.nfo
index b9f31f2f6..5bf7e08eb 100644
--- a/tests/Jellyfin.XbmcMetadata.Tests/Test Data/American Gods.nfo
+++ b/tests/Jellyfin.XbmcMetadata.Tests/Test Data/American Gods.nfo
@@ -126,7 +126,7 @@
<episodeguide>
<url cache="tmdb-46639-en.json">http://api.themoviedb.org/3/tv/46639?api_key=6a5be4999abf74eba1f9a8311294c267&amp;language=en</url>
</episodeguide>
- <id>46639</id>
+ <id IMDB="tt11111" TMDB="46639">253573</id>
<uniqueid type="tmdb" default="true">46639</uniqueid>
<uniqueid type="tvdb">253573</uniqueid>
<genre>Drama</genre>
@@ -134,7 +134,9 @@
<genre>Sci-Fi &amp; Fantasy</genre>
<premiered>2017-04-30</premiered>
<year>2017</year>
- <status></status>
+ <status>ended</status>
+ <airs_time>9 PM</airs_time>
+ <airs_dayofweek>Friday</airs_dayofweek>
<code></code>
<aired></aired>
<studio>Starz</studio>
diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Test Data/Dancing Queen.nfo b/tests/Jellyfin.XbmcMetadata.Tests/Test Data/Dancing Queen.nfo
new file mode 100644
index 000000000..29f19e1a0
--- /dev/null
+++ b/tests/Jellyfin.XbmcMetadata.Tests/Test Data/Dancing Queen.nfo
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<musicvideo>
+ <title>Dancing Queen</title>
+ <userrating>0</userrating>
+ <top250>0</top250>
+ <track>3</track>
+ <album>Arrival</album>
+ <outline></outline>
+ <plot>Dancing Queen est un des tubes emblématiques de l&apos;ère disco produits par le groupe suédois ABBA en 1976. Ce tube connaît un regain de popularité en 1994 lors de la sortie de Priscilla, folle du désert, et fait « presque » partie de la distribution du film Muriel.&#x0A;Le groupe a également enregistré une version espagnole de ce titre, La reina del baile, pour le marché d&apos;Amérique latine. On peut retrouver ces versions en espagnol des succès de ABBA sur l&apos;abum Oro. Le 18 juin 1976, ABBA a interprété cette chanson lors d&apos;un spectacle télévisé organisé en l&apos;honneur du roi Charles XVI Gustave de Suède, qui venait de se marier. Le titre sera repris en 2011 par Glee dans la saison 2, épisode 20.</plot>
+ <tagline></tagline>
+ <runtime>2</runtime>
+ <thumb preview="https://www.theaudiodb.com/images/media/album/thumb/arrival-4ee244732bbde.jpg/preview">https://www.theaudiodb.com/images/media/album/thumb/arrival-4ee244732bbde.jpg</thumb>
+ <thumb preview="https://assets.fanart.tv/preview/music/d87e52c5-bb8d-4da8-b941-9f4928627dc8/albumcover/arrival-548ab7a698b49.jpg">https://assets.fanart.tv/fanart/music/d87e52c5-bb8d-4da8-b941-9f4928627dc8/albumcover/arrival-548ab7a698b49.jpg</thumb>
+ <mpaa></mpaa>
+ <playcount>0</playcount>
+ <lastplayed></lastplayed>
+ <id></id>
+ <genre>Pop</genre>
+ <director>John Smith</director>
+ <premiered>1976-01-01</premiered>
+ <year>1976</year>
+ <status></status>
+ <code></code>
+ <aired></aired>
+ <studio>Studio 54</studio>
+ <trailer></trailer>
+ <fileinfo>
+ <streamdetails>
+ <video>
+ <codec>hevc</codec>
+ <aspect>1.792230</aspect>
+ <width>716</width>
+ <height>568</height>
+ <durationinseconds>143</durationinseconds>
+ <stereomode></stereomode>
+ </video>
+ <audio>
+ <codec>ac3</codec>
+ <language>eng</language>
+ <channels>2</channels>
+ </audio>
+ </streamdetails>
+ </fileinfo>
+ <artist>ABBA</artist>
+ <resume>
+ <position>0.000000</position>
+ <total>0.000000</total>
+ </resume>
+ <dateadded>2018-09-10 09:46:06</dateadded>
+</musicvideo>
diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Test Data/Imdb.nfo b/tests/Jellyfin.XbmcMetadata.Tests/Test Data/Imdb.nfo
new file mode 100644
index 000000000..e30a1c660
--- /dev/null
+++ b/tests/Jellyfin.XbmcMetadata.Tests/Test Data/Imdb.nfo
@@ -0,0 +1 @@
+https://www.imdb.com/title/tt0944947/
diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Test Data/Justice League.nfo b/tests/Jellyfin.XbmcMetadata.Tests/Test Data/Justice League.nfo
index 18b44d944..72e27fe50 100644
--- a/tests/Jellyfin.XbmcMetadata.Tests/Test Data/Justice League.nfo
+++ b/tests/Jellyfin.XbmcMetadata.Tests/Test Data/Justice League.nfo
@@ -24,12 +24,20 @@
<votes>119873</votes>
</rating>
</ratings>
+ <criticrating>7.6</criticrating>
+ <language>en</language>
+ <countrycode>us</countrycode>
+ <customrating>8.7</customrating>
+ <aspectratio>1.777778</aspectratio>
<userrating>0</userrating>
<top250>0</top250>
<outline>Fueled by his restored faith in humanity and inspired by Superman&apos;s selfless act, Bruce Wayne enlists the help of his new-found ally, Diana Prince, to face an even greater enemy.</outline>
<plot>Fueled by his restored faith in humanity and inspired by Superman&apos;s selfless act, Bruce Wayne enlists the help of his newfound ally, Diana Prince, to face an even greater enemy. Together, Batman and Wonder Woman work quickly to find and recruit a team of meta-humans to stand against this newly awakened threat. But despite the formation of this unprecedented league of heroes-Batman, Wonder Woman, Aquaman, Cyborg and The Flash-it may already be too late to save the planet from an assault of catastrophic proportions.</plot>
<tagline>Justice for all.</tagline>
<runtime>120</runtime>
+ <playcount>2</playcount>
+ <watched>true</watched>
+ <lastplayed>2021-02-11 07:47:23</lastplayed>
<tmdbId>141052</tmdbId>
<thumb aspect="set.poster" preview="https://assets.fanart.tv/preview/movies/468551/movieposter/justice-league-collection-5c24ea65591d3.jpg">https://assets.fanart.tv/fanart/movies/468551/movieposter/justice-league-collection-5c24ea65591d3.jpg</thumb>
<thumb aspect="set.poster" preview="https://assets.fanart.tv/preview/movies/468551/movieposter/justice-league-collection-5c24ea65591d3.jpg">https://assets.fanart.tv/fanart/movies/468551/movieposter/justice-league-collection-5c24ea65591d3.jpg</thumb>
@@ -82,8 +90,6 @@
<thumb preview="https://assets.fanart.tv/preview/movies/141052/moviebackground/justice-league-5a119394ea362.jpg">https://assets.fanart.tv/fanart/movies/141052/moviebackground/justice-league-5a119394ea362.jpg</thumb>
</fanart>
<mpaa>Australia:M</mpaa>
- <playcount>0</playcount>
- <lastplayed></lastplayed>
<id>tt0974015</id>
<uniqueid type="imdb" default="true">tt0974015</uniqueid>
<genre>Action</genre>
@@ -93,20 +99,22 @@
<country>USA</country>
<country>Canada</country>
<country>UK</country>
- <set>
+ <set tmdbcolid="702342">
<name>Justice League Collection</name>
<overview>Based on the DC Comics superhero team</overview>
</set>
<credits>Jerry Siegel</credits>
<credits>Joe Shuster</credits>
- <director>Zack Snyder</director>
+ <director>Zack Snyder,</director>
+ <writer>Test</writer>
<premiered>2017-11-15</premiered>
+ <enddate>2017-11-16</enddate>
<year>2017</year>
<status></status>
<code></code>
<aired></aired>
<studio>DC Comics</studio>
- <trailer></trailer>
+ <trailer>plugin://plugin.video.youtube/?action=play_video&amp;videoid=dQw4w9WgXcQ</trailer>
<fileinfo>
<streamdetails>
<video>
@@ -116,6 +124,7 @@
<height>1080</height>
<durationinseconds>6268</durationinseconds>
<stereomode></stereomode>
+ <format3d>HSBS</format3d>
</video>
<audio>
<codec>truehd</codec>
diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Test Data/The Bone Orchard.nfo b/tests/Jellyfin.XbmcMetadata.Tests/Test Data/The Bone Orchard.nfo
index 14feffcba..cd275e4cf 100644
--- a/tests/Jellyfin.XbmcMetadata.Tests/Test Data/The Bone Orchard.nfo
+++ b/tests/Jellyfin.XbmcMetadata.Tests/Test Data/The Bone Orchard.nfo
@@ -14,6 +14,10 @@
<episode>1</episode>
<displayseason>-1</displayseason>
<displayepisode>-1</displayepisode>
+ <episodenumberend>1</episodenumberend>
+ <airsbefore_episode>1</airsbefore_episode>
+ <airsafter_season>2</airsafter_season>
+ <airsbefore_season>3</airsbefore_season>
<outline></outline>
<plot>When Shadow Moon is released from prison early after the death of his wife, he meets Mr. Wednesday and is recruited as his bodyguard. Shadow discovers that this may be more than he bargained for.</plot>
<tagline></tagline>
diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Test Data/Tmdb.nfo b/tests/Jellyfin.XbmcMetadata.Tests/Test Data/Tmdb.nfo
new file mode 100644
index 000000000..15af71852
--- /dev/null
+++ b/tests/Jellyfin.XbmcMetadata.Tests/Test Data/Tmdb.nfo
@@ -0,0 +1 @@
+https://www.themoviedb.org/movie/30287-fallo
diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Test Data/Tvdb.nfo b/tests/Jellyfin.XbmcMetadata.Tests/Test Data/Tvdb.nfo
new file mode 100644
index 000000000..9de69f8e1
--- /dev/null
+++ b/tests/Jellyfin.XbmcMetadata.Tests/Test Data/Tvdb.nfo
@@ -0,0 +1 @@
+https://www.thetvdb.com/?tab=series&id=121361