aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/ISSUE_TEMPLATE/bug_report.md1
-rw-r--r--.github/stale.yml6
-rw-r--r--.github/workflows/automation.yml10
-rw-r--r--Emby.Naming/Common/NamingOptions.cs16
-rw-r--r--Emby.Notifications/NotificationEntryPoint.cs21
-rw-r--r--Emby.Server.Implementations/ApplicationHost.cs33
-rw-r--r--Emby.Server.Implementations/Emby.Server.Implementations.csproj4
-rw-r--r--Emby.Server.Implementations/Library/LibraryManager.cs5
-rw-r--r--Emby.Server.Implementations/Localization/Core/ca.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/cs.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/de.json8
-rw-r--r--Emby.Server.Implementations/Localization/Core/en-GB.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/es-AR.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/es_419.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/fi.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/fr.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/gl.json31
-rw-r--r--Emby.Server.Implementations/Localization/Core/hu.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/it.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/kk.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/ko.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/ml.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/ms.json12
-rw-r--r--Emby.Server.Implementations/Localization/Core/nl.json18
-rw-r--r--Emby.Server.Implementations/Localization/Core/pl.json3
-rw-r--r--Emby.Server.Implementations/Localization/Core/pt.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/ru.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/sk.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/sl-SI.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/sq.json31
-rw-r--r--Emby.Server.Implementations/Localization/Core/ta.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/vi.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/zh-CN.json4
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/CleanActivityLogTask.cs2
-rw-r--r--Jellyfin.Api/Controllers/InstantMixController.cs57
-rw-r--r--Jellyfin.Api/Controllers/MediaInfoController.cs22
-rw-r--r--Jellyfin.Api/Controllers/VideosController.cs12
-rw-r--r--Jellyfin.Api/Jellyfin.Api.csproj2
-rw-r--r--Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj4
-rw-r--r--Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj8
-rw-r--r--Jellyfin.Server/Jellyfin.Server.csproj10
-rw-r--r--MediaBrowser.Common/Net/IPHost.cs13
-rw-r--r--MediaBrowser.Controller/Entities/BaseItem.cs16
-rw-r--r--MediaBrowser.Controller/Entities/UserView.cs85
-rw-r--r--MediaBrowser.Controller/IServerApplicationHost.cs8
-rw-r--r--MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs46
-rw-r--r--MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs585
-rw-r--r--MediaBrowser.Model/System/SystemInfo.cs1
-rw-r--r--MediaBrowser.Providers/Manager/ItemImageProvider.cs25
-rw-r--r--MediaBrowser.Providers/Manager/MetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs7
-rw-r--r--MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs22
-rw-r--r--MediaBrowser.Providers/Plugins/MusicBrainz/Plugin.cs12
-rw-r--r--MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs3
-rw-r--r--MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs9
-rw-r--r--MediaBrowser.Providers/Plugins/Omdb/Plugin.cs12
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs8
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs2
-rw-r--r--MediaBrowser.Providers/Subtitles/SubtitleManager.cs6
-rw-r--r--deployment/Dockerfile.debian.amd642
-rw-r--r--deployment/Dockerfile.debian.arm642
-rw-r--r--deployment/Dockerfile.debian.armhf2
-rw-r--r--deployment/Dockerfile.linux.amd642
-rw-r--r--deployment/Dockerfile.linux.amd64-musl2
-rw-r--r--deployment/Dockerfile.linux.arm642
-rw-r--r--deployment/Dockerfile.linux.armhf2
-rw-r--r--deployment/Dockerfile.macos2
-rw-r--r--deployment/Dockerfile.portable2
-rw-r--r--deployment/Dockerfile.ubuntu.amd642
-rw-r--r--deployment/Dockerfile.ubuntu.arm642
-rw-r--r--deployment/Dockerfile.ubuntu.armhf2
-rw-r--r--deployment/Dockerfile.windows.amd642
-rw-r--r--jellyfin.ruleset6
-rw-r--r--src/Jellyfin.Extensions/DictionaryExtensions.cs65
-rw-r--r--tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj2
-rw-r--r--tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj2
-rw-r--r--tests/Jellyfin.MediaEncoding.Tests/FFprobeParserTests.cs5
-rw-r--r--tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs3
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/QuickConnect/QuickConnectManagerTests.cs73
-rw-r--r--tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj2
-rw-r--r--tests/Jellyfin.Server.Integration.Tests/JellyfinApplicationFactory.cs5
-rw-r--r--tests/Jellyfin.Server.Integration.Tests/Middleware/RobotsRedirectionMiddlewareTests.cs32
-rw-r--r--tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj2
85 files changed, 763 insertions, 716 deletions
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index 12f1f5ed5..5a525267a 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -17,6 +17,7 @@ assignees: ''
- Browser: [e.g. Firefox 72, Chrome 80, Safari 13]
- Jellyfin Version: [e.g. 10.4.3, nightly 20191231]
- Playback: [Direct Play, Remux, Direct Stream, Transcode]
+ - Hardware Acceleration: [e.g. none, VAAPI, NVENC, etc.]
- Installed Plugins: [e.g. none, Fanart, Anime, etc.]
- Reverse Proxy: [e.g. none, nginx, apache, etc.]
- Base URL: [e.g. none, yes: /example]
diff --git a/.github/stale.yml b/.github/stale.yml
index 05892c44d..cba9c33b2 100644
--- a/.github/stale.yml
+++ b/.github/stale.yml
@@ -17,9 +17,13 @@ staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has gone 120 days without comment. To avoid abandoned issues, it will be closed in 21 days if there are no new comments.
-
+
If you're the original submitter of this issue, please comment confirming if this issue still affects you in the latest release or nightlies, or close the issue if it has been fixed. If you're another user also affected by this bug, please comment confirming so. Either action will remove the stale label.
This bot exists to prevent issues from becoming stale and forgotten. Jellyfin is always moving forward, and bugs are often fixed as side effects of other changes. We therefore ask that bug report authors remain vigilant about their issues to ensure they are closed if fixed, or re-confirmed - perhaps with fresh logs or reproduction examples - regularly. If you have any questions you can reach us on [Matrix or Social Media](https://docs.jellyfin.org/general/getting-help.html).
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false
+
+# Disable automatic closing of pull requests
+pulls:
+ daysUntilClose: false
diff --git a/.github/workflows/automation.yml b/.github/workflows/automation.yml
index 38415f1c6..20294843d 100644
--- a/.github/workflows/automation.yml
+++ b/.github/workflows/automation.yml
@@ -26,7 +26,7 @@ jobs:
if: ${{ github.repository == 'jellyfin/jellyfin' }}
steps:
- name: Remove from 'Current Release' project
- uses: alex-page/github-project-automation-plus@v0.7.1
+ uses: alex-page/github-project-automation-plus@v0.8.1
if: (github.event.pull_request || github.event.issue.pull_request) && !contains(github.event.*.labels.*.name, 'stable backport')
continue-on-error: true
with:
@@ -35,7 +35,7 @@ jobs:
repo-token: ${{ secrets.JF_BOT_TOKEN }}
- name: Add to 'Release Next' project
- uses: alex-page/github-project-automation-plus@v0.7.1
+ uses: alex-page/github-project-automation-plus@v0.8.1
if: (github.event.pull_request || github.event.issue.pull_request) && github.event.action == 'opened'
continue-on-error: true
with:
@@ -44,7 +44,7 @@ jobs:
repo-token: ${{ secrets.JF_BOT_TOKEN }}
- name: Add to 'Current Release' project
- uses: alex-page/github-project-automation-plus@v0.7.1
+ uses: alex-page/github-project-automation-plus@v0.8.1
if: (github.event.pull_request || github.event.issue.pull_request) && !contains(github.event.*.labels.*.name, 'stable backport')
continue-on-error: true
with:
@@ -58,7 +58,7 @@ jobs:
run: echo "::set-output name=number::$(curl -s ${{ github.event.issue.comments_url }} | jq '.[] | select(.author_association == "MEMBER") | .author_association' | wc -l)"
- name: Move issue to needs triage
- uses: alex-page/github-project-automation-plus@v0.7.1
+ uses: alex-page/github-project-automation-plus@v0.8.1
if: github.event.issue.pull_request == '' && github.event.comment.author_association == 'MEMBER' && steps.member_comments.outputs.number <= 1
continue-on-error: true
with:
@@ -67,7 +67,7 @@ jobs:
repo-token: ${{ secrets.JF_BOT_TOKEN }}
- name: Add issue to triage project
- uses: alex-page/github-project-automation-plus@v0.7.1
+ uses: alex-page/github-project-automation-plus@v0.8.1
if: github.event.issue.pull_request == '' && github.event.action == 'opened'
continue-on-error: true
with:
diff --git a/Emby.Naming/Common/NamingOptions.cs b/Emby.Naming/Common/NamingOptions.cs
index 22a3e8bb4..5f125eb4f 100644
--- a/Emby.Naming/Common/NamingOptions.cs
+++ b/Emby.Naming/Common/NamingOptions.cs
@@ -137,7 +137,7 @@ namespace Emby.Naming.Common
CleanStrings = new[]
{
- @"[ _\,\.\(\)\[\]\-](3d|sbs|tab|hsbs|htab|mvc|HDR|HDC|UHD|UltraHD|4k|ac3|dts|custom|dc|divx|divx5|dsr|dsrip|dutch|dvd|dvdrip|dvdscr|dvdscreener|screener|dvdivx|cam|fragment|fs|hdtv|hdrip|hdtvrip|internal|limited|multisubs|ntsc|ogg|ogm|pal|pdtv|proper|repack|rerip|retail|cd[1-9]|r3|r5|bd5|bd|se|svcd|swedish|german|read.nfo|nfofix|unrated|ws|telesync|ts|telecine|tc|brrip|bdrip|480p|480i|576p|576i|720p|720i|1080p|1080i|2160p|hrhd|hrhdtv|hddvd|bluray|blu-ray|x264|x265|h264|xvid|xvidvd|xxx|www.www|AAC|DTS|\[.*\])([ _\,\.\(\)\[\]\-]|$)",
+ @"[ _\,\.\(\)\[\]\-](3d|sbs|tab|hsbs|htab|mvc|HDR|HDC|UHD|UltraHD|4k|ac3|dts|custom|dc|divx|divx5|dsr|dsrip|dutch|dvd|dvdrip|dvdscr|dvdscreener|screener|dvdivx|cam|fragment|fs|hdtv|hdrip|hdtvrip|internal|limited|multisubs|ntsc|ogg|ogm|pal|pdtv|proper|repack|rerip|retail|cd[1-9]|r3|r5|bd5|bd|se|svcd|swedish|german|read.nfo|nfofix|unrated|ws|telesync|ts|telecine|tc|brrip|bdrip|480p|480i|576p|576i|720p|720i|1080p|1080i|2160p|hrhd|hrhdtv|hddvd|bluray|blu-ray|x264|x265|h264|h265|xvid|xvidvd|xxx|www.www|AAC|DTS|\[.*\])([ _\,\.\(\)\[\]\-]|$)",
@"(\[.*\])"
};
@@ -277,7 +277,7 @@ namespace Emby.Naming.Common
IsNamed = true
},
- new EpisodeExpression("[\\\\/\\._ \\[\\(-]([0-9]+)x([0-9]+(?:(?:[a-i]|\\.[1-9])(?![0-9]))?)([^\\\\/]*)$")
+ new EpisodeExpression(@"[\\\/\._ \[\(-]([0-9]+)x([0-9]+(?:(?:[a-i]|\.[1-9])(?![0-9]))?)([^\\\/]*)$")
{
SupportsAbsoluteEpisodeNumbers = true
},
@@ -305,6 +305,12 @@ namespace Emby.Naming.Common
// *** End Kodi Standard Naming
+ // "Episode 16", "Episode 16 - Title"
+ new EpisodeExpression(@"[Ee]pisode (?<epnumber>[0-9]+)(-(?<endingepnumber>[0-9]+))?[^\\\/]*$")
+ {
+ IsNamed = true
+ },
+
new EpisodeExpression(@".*(\\|\/)[sS]?(?<seasonnumber>[0-9]+)[xX](?<epnumber>[0-9]+)[^\\\/]*$")
{
IsNamed = true
@@ -362,12 +368,6 @@ namespace Emby.Naming.Common
IsOptimistic = true,
IsNamed = true
},
- // "Episode 16", "Episode 16 - Title"
- new EpisodeExpression(@".*[\\\/][^\\\/]* (?<epnumber>[0-9]{1,3})(-(?<endingepnumber>[0-9]{2,3}))*[^\\\/]*$")
- {
- IsOptimistic = true,
- IsNamed = true
- }
};
EpisodeWithoutSeasonExpressions = new[]
diff --git a/Emby.Notifications/NotificationEntryPoint.cs b/Emby.Notifications/NotificationEntryPoint.cs
index 7433d3c8a..e8ae14ff2 100644
--- a/Emby.Notifications/NotificationEntryPoint.cs
+++ b/Emby.Notifications/NotificationEntryPoint.cs
@@ -77,7 +77,6 @@ namespace Emby.Notifications
{
_libraryManager.ItemAdded += OnLibraryManagerItemAdded;
_appHost.HasPendingRestartChanged += OnAppHostHasPendingRestartChanged;
- _appHost.HasUpdateAvailableChanged += OnAppHostHasUpdateAvailableChanged;
_activityManager.EntryCreated += OnActivityManagerEntryCreated;
return Task.CompletedTask;
@@ -132,25 +131,6 @@ namespace Emby.Notifications
return _config.GetConfiguration<NotificationOptions>("notifications");
}
- private async void OnAppHostHasUpdateAvailableChanged(object? sender, EventArgs e)
- {
- if (!_appHost.HasUpdateAvailable)
- {
- return;
- }
-
- var type = NotificationType.ApplicationUpdateAvailable.ToString();
-
- var notification = new NotificationRequest
- {
- Description = "Please see jellyfin.org for details.",
- NotificationType = type,
- Name = _localization.GetLocalizedString("NewVersionIsAvailable")
- };
-
- await SendNotification(notification, null).ConfigureAwait(false);
- }
-
private void OnLibraryManagerItemAdded(object? sender, ItemChangeEventArgs e)
{
if (!FilterItem(e.Item))
@@ -325,7 +305,6 @@ namespace Emby.Notifications
_libraryManager.ItemAdded -= OnLibraryManagerItemAdded;
_appHost.HasPendingRestartChanged -= OnAppHostHasPendingRestartChanged;
- _appHost.HasUpdateAvailableChanged -= OnAppHostHasUpdateAvailableChanged;
_activityManager.EntryCreated -= OnActivityManagerEntryCreated;
_disposed = true;
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index 82995deb3..38498ab13 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -1102,7 +1102,6 @@ namespace Emby.Server.Implementations
OperatingSystemDisplayName = OperatingSystem.Name,
CanSelfRestart = CanSelfRestart,
CanLaunchWebBrowser = CanLaunchWebBrowser,
- HasUpdateAvailable = HasUpdateAvailable,
TranscodingTempPath = ConfigurationManager.GetTranscodePath(),
ServerName = FriendlyName,
LocalAddress = GetSmartApiUrl(source),
@@ -1118,7 +1117,7 @@ namespace Emby.Server.Implementations
.Select(i => new WakeOnLanInfo(i))
.ToList();
- public PublicSystemInfo GetPublicSystemInfo(IPAddress source)
+ public PublicSystemInfo GetPublicSystemInfo(IPAddress address)
{
return new PublicSystemInfo
{
@@ -1127,7 +1126,7 @@ namespace Emby.Server.Implementations
Id = SystemId,
OperatingSystem = OperatingSystem.Id.ToString(),
ServerName = FriendlyName,
- LocalAddress = GetSmartApiUrl(source),
+ LocalAddress = GetSmartApiUrl(address),
StartupWizardCompleted = ConfigurationManager.CommonConfiguration.IsStartupWizardCompleted
};
}
@@ -1136,7 +1135,7 @@ namespace Emby.Server.Implementations
public bool ListenWithHttps => Certificate != null && ConfigurationManager.GetNetworkConfiguration().EnableHttps;
/// <inheritdoc/>
- public string GetSmartApiUrl(IPAddress ipAddress, int? port = null)
+ public string GetSmartApiUrl(IPAddress remoteAddr, int? port = null)
{
// Published server ends with a /
if (!string.IsNullOrEmpty(PublishedServerUrl))
@@ -1145,7 +1144,7 @@ namespace Emby.Server.Implementations
return PublishedServerUrl.Trim('/');
}
- string smart = NetManager.GetBindInterface(ipAddress, out port);
+ string smart = NetManager.GetBindInterface(remoteAddr, out port);
// If the smartAPI doesn't start with http then treat it as a host or ip.
if (smart.StartsWith("http", StringComparison.OrdinalIgnoreCase))
{
@@ -1208,14 +1207,14 @@ namespace Emby.Server.Implementations
}
/// <inheritdoc/>
- public string GetLocalApiUrl(string host, string scheme = null, int? port = null)
+ public string GetLocalApiUrl(string hostname, string scheme = null, int? port = null)
{
// NOTE: If no BaseUrl is set then UriBuilder appends a trailing slash, but if there is no BaseUrl it does
// not. For consistency, always trim the trailing slash.
return new UriBuilder
{
Scheme = scheme ?? (ListenWithHttps ? Uri.UriSchemeHttps : Uri.UriSchemeHttp),
- Host = host,
+ Host = hostname,
Port = port ?? (ListenWithHttps ? HttpsPort : HttpPort),
Path = ConfigurationManager.GetNetworkConfiguration().BaseUrl
}.ToString().TrimEnd('/');
@@ -1252,26 +1251,6 @@ namespace Emby.Server.Implementations
protected abstract void ShutdownInternal();
- public event EventHandler HasUpdateAvailableChanged;
-
- private bool _hasUpdateAvailable;
-
- public bool HasUpdateAvailable
- {
- get => _hasUpdateAvailable;
- set
- {
- var fireEvent = value && !_hasUpdateAvailable;
-
- _hasUpdateAvailable = value;
-
- if (fireEvent)
- {
- HasUpdateAvailableChanged?.Invoke(this, EventArgs.Empty);
- }
- }
- }
-
public IEnumerable<Assembly> GetApiPluginAssemblies()
{
var assemblies = _allConcreteTypes
diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
index 9c90de1ed..fe233df6c 100644
--- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj
+++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
@@ -24,11 +24,11 @@
<ItemGroup>
<PackageReference Include="Jellyfin.XmlTv" Version="10.6.2" />
- <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.1" />
+ <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.2" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="5.0.0" />
- <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="5.0.7" />
+ <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="5.0.8" />
<PackageReference Include="Mono.Nat" Version="3.0.1" />
<PackageReference Include="prometheus-net.DotNetRuntime" Version="4.1.0" />
<PackageReference Include="sharpcompress" Version="0.28.3" />
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index d80637332..13fb8b2fd 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -2540,9 +2540,10 @@ namespace Emby.Server.Implementations.Library
{
episodeInfo = resolver.Resolve(episode.Path, isFolder, null, null, isAbsoluteNaming);
// Resolve from parent folder if it's not the Season folder
- if (episodeInfo == null && episode.Parent.GetType() == typeof(Folder))
+ var parent = episode.GetParent();
+ if (episodeInfo == null && parent.GetType() == typeof(Folder))
{
- episodeInfo = resolver.Resolve(episode.Parent.Path, true, null, null, isAbsoluteNaming);
+ episodeInfo = resolver.Resolve(parent.Path, true, null, null, isAbsoluteNaming);
if (episodeInfo != null)
{
// add the container
diff --git a/Emby.Server.Implementations/Localization/Core/ca.json b/Emby.Server.Implementations/Localization/Core/ca.json
index fd8437b6d..1b612dc71 100644
--- a/Emby.Server.Implementations/Localization/Core/ca.json
+++ b/Emby.Server.Implementations/Localization/Core/ca.json
@@ -118,5 +118,7 @@
"TaskCleanActivityLog": "Buidar Registre d'Activitat",
"Undefined": "Indefinit",
"Forced": "Forçat",
- "Default": "Defecto"
+ "Default": "Defecto",
+ "TaskOptimizeDatabaseDescription": "Compacta la base de dades i trunca l'espai lliure. Executar aquesta tasca després d’escanejar la biblioteca o fer altres canvis que impliquin modificacions a la base de dades pot millorar el rendiment.",
+ "TaskOptimizeDatabase": "Optimitzar la base de dades"
}
diff --git a/Emby.Server.Implementations/Localization/Core/cs.json b/Emby.Server.Implementations/Localization/Core/cs.json
index ff14c1929..62b2b6328 100644
--- a/Emby.Server.Implementations/Localization/Core/cs.json
+++ b/Emby.Server.Implementations/Localization/Core/cs.json
@@ -118,5 +118,7 @@
"TaskCleanActivityLog": "Smazat záznam aktivity",
"Undefined": "Nedefinované",
"Forced": "Vynucené",
- "Default": "Výchozí"
+ "Default": "Výchozí",
+ "TaskOptimizeDatabaseDescription": "Zmenší databázi a odstraní prázdné místo. Spuštění této úlohy po skenování knihovny či jiných změnách databáze může zlepšit výkon.",
+ "TaskOptimizeDatabase": "Optimalizovat databázi"
}
diff --git a/Emby.Server.Implementations/Localization/Core/de.json b/Emby.Server.Implementations/Localization/Core/de.json
index 9d82b5878..c924e5c15 100644
--- a/Emby.Server.Implementations/Localization/Core/de.json
+++ b/Emby.Server.Implementations/Localization/Core/de.json
@@ -3,7 +3,7 @@
"AppDeviceValues": "App: {0}, Gerät: {1}",
"Application": "Anwendung",
"Artists": "Interpreten",
- "AuthenticationSucceededWithUserName": "{0} wurde angemeldet",
+ "AuthenticationSucceededWithUserName": "{0} erfolgreich authentifiziert",
"Books": "Bücher",
"CameraImageUploadedFrom": "Ein neues Kamerafoto wurde von {0} hochgeladen",
"Channels": "Kanäle",
@@ -16,7 +16,7 @@
"Folders": "Verzeichnisse",
"Genres": "Genres",
"HeaderAlbumArtists": "Album-Interpreten",
- "HeaderContinueWatching": "Fortsetzen",
+ "HeaderContinueWatching": "Weiterschauen",
"HeaderFavoriteAlbums": "Lieblingsalben",
"HeaderFavoriteArtists": "Lieblings-Interpreten",
"HeaderFavoriteEpisodes": "Lieblingsepisoden",
@@ -118,5 +118,7 @@
"TaskCleanActivityLog": "Aktivitätsprotokoll aufräumen",
"Undefined": "Undefiniert",
"Forced": "Erzwungen",
- "Default": "Standard"
+ "Default": "Standard",
+ "TaskOptimizeDatabaseDescription": "Komprimiert die Datenbank und trimmt den freien Speicherplatz. Die Ausführung dieser Aufgabe nach dem Scannen der Bibliothek oder nach anderen Änderungen, die Datenbankänderungen implizieren, kann die Leistung verbessern.",
+ "TaskOptimizeDatabase": "Datenbank optimieren"
}
diff --git a/Emby.Server.Implementations/Localization/Core/en-GB.json b/Emby.Server.Implementations/Localization/Core/en-GB.json
index 7667612b9..8b2e8b6b1 100644
--- a/Emby.Server.Implementations/Localization/Core/en-GB.json
+++ b/Emby.Server.Implementations/Localization/Core/en-GB.json
@@ -118,5 +118,7 @@
"TaskCleanActivityLog": "Clean Activity Log",
"Undefined": "Undefined",
"Forced": "Forced",
- "Default": "Default"
+ "Default": "Default",
+ "TaskOptimizeDatabaseDescription": "Compacts database and truncates free space. Running this task after scanning the library or doing other changes that imply database modifications might improve performance.",
+ "TaskOptimizeDatabase": "Optimise database"
}
diff --git a/Emby.Server.Implementations/Localization/Core/es-AR.json b/Emby.Server.Implementations/Localization/Core/es-AR.json
index 0d4a14be0..6321f695c 100644
--- a/Emby.Server.Implementations/Localization/Core/es-AR.json
+++ b/Emby.Server.Implementations/Localization/Core/es-AR.json
@@ -118,5 +118,7 @@
"TaskCleanActivityLog": "Borrar log de actividades",
"Undefined": "Indefinido",
"Forced": "Forzado",
- "Default": "Por Defecto"
+ "Default": "Por Defecto",
+ "TaskOptimizeDatabaseDescription": "Compacta la base de datos y restaura el espacio libre. Ejecutar esta tarea después de actualizar las librerías o realizar otros cambios que impliquen modificar las bases de datos puede mejorar la performance.",
+ "TaskOptimizeDatabase": "Optimización de base de datos"
}
diff --git a/Emby.Server.Implementations/Localization/Core/es_419.json b/Emby.Server.Implementations/Localization/Core/es_419.json
index 6d2a5c7ac..a968c6dab 100644
--- a/Emby.Server.Implementations/Localization/Core/es_419.json
+++ b/Emby.Server.Implementations/Localization/Core/es_419.json
@@ -117,5 +117,7 @@
"TaskCleanActivityLog": "Limpiar Registro de Actividades",
"Undefined": "Sin definir",
"Forced": "Forzado",
- "Default": "Por Defecto"
+ "Default": "Por Defecto",
+ "TaskOptimizeDatabaseDescription": "Compacta la base de datos y restaura el espacio libre. Ejecutar esta tarea después de actualizar las librerías o realizar otros cambios que impliquen modificar las bases de datos puede mejorar la performance.",
+ "TaskOptimizeDatabase": "Optimización de base de datos"
}
diff --git a/Emby.Server.Implementations/Localization/Core/fi.json b/Emby.Server.Implementations/Localization/Core/fi.json
index 633968d26..4a1f4f1d5 100644
--- a/Emby.Server.Implementations/Localization/Core/fi.json
+++ b/Emby.Server.Implementations/Localization/Core/fi.json
@@ -117,5 +117,7 @@
"Default": "Oletus",
"TaskCleanActivityLogDescription": "Poistaa määritettyä ikää vanhemmat tapahtumat toimintahistoriasta.",
"TaskCleanActivityLog": "Tyhjennä toimintahistoria",
- "Undefined": "Määrittelemätön"
+ "Undefined": "Määrittelemätön",
+ "TaskOptimizeDatabaseDescription": "Tiivistää ja puhdistaa tietokannan. Tämän toiminnon suorittaminen kirjastojen skannauksen tai muiden tietokantaan liittyvien muutoksien jälkeen voi parantaa suorituskykyä.",
+ "TaskOptimizeDatabase": "Optimoi tietokanta"
}
diff --git a/Emby.Server.Implementations/Localization/Core/fr.json b/Emby.Server.Implementations/Localization/Core/fr.json
index ce1493be8..0e4c38425 100644
--- a/Emby.Server.Implementations/Localization/Core/fr.json
+++ b/Emby.Server.Implementations/Localization/Core/fr.json
@@ -118,5 +118,7 @@
"TaskCleanActivityLog": "Nettoyer le journal d'activité",
"Undefined": "Non défini",
"Forced": "Forcé",
- "Default": "Par défaut"
+ "Default": "Par défaut",
+ "TaskOptimizeDatabaseDescription": "Réduit les espaces vides/inutiles et compacte la base de données. Utiliser cette fonction après une mise à jour de la bibliothèque ou toute autre modification de la base de données peut améliorer les performances du serveur.",
+ "TaskOptimizeDatabase": "Optimiser la base de données"
}
diff --git a/Emby.Server.Implementations/Localization/Core/gl.json b/Emby.Server.Implementations/Localization/Core/gl.json
index 0398e1c9e..afb22ab47 100644
--- a/Emby.Server.Implementations/Localization/Core/gl.json
+++ b/Emby.Server.Implementations/Localization/Core/gl.json
@@ -88,5 +88,34 @@
"NotificationOptionVideoPlaybackStopped": "Reproducción de vídeo parada",
"NotificationOptionVideoPlayback": "Reproducción de vídeo iniciada",
"NotificationOptionUserLockedOut": "Usuario bloqueado",
- "NotificationOptionTaskFailed": "Falla na tarefa axendada"
+ "NotificationOptionTaskFailed": "Falla na tarefa axendada",
+ "TaskCleanTranscodeDescription": "Borra os arquivos de transcode anteriores a un día.",
+ "TaskCleanTranscode": "Limpar Directorio de Transcode",
+ "UserStoppedPlayingItemWithValues": "{0} rematou de reproducir {1} en {2}",
+ "UserStartedPlayingItemWithValues": "{0} está reproducindo {1} en {2}",
+ "TaskDownloadMissingSubtitlesDescription": "Busca en internet por subtítulos que faltan baseado na configuración de metadatos.",
+ "TaskDownloadMissingSubtitles": "Descargar subtítulos que faltan",
+ "TaskRefreshChannelsDescription": "Refresca a información do canle de internet.",
+ "TaskRefreshChannels": "Refrescar Canles",
+ "TaskUpdatePluginsDescription": "Descarga e instala actualizacións para plugins que están configurados para actualizarse automáticamente.",
+ "TaskRefreshPeopleDescription": "Actualiza os metadatos dos actores e directores na túa libraría multimedia.",
+ "TaskRefreshPeople": "Refrescar Persoas",
+ "TaskCleanLogsDescription": "Borra arquivos de rexistro que son mais antigos que {0} días.",
+ "TaskRefreshLibraryDescription": "Escanea a tua libraría multimedia buscando novos arquivos e refrescando os metadatos.",
+ "TaskRefreshLibrary": "Escanear Libraría Multimedia",
+ "TaskRefreshChapterImagesDescription": "Crea previsualizacións para videos que teñen capítulos.",
+ "TaskRefreshChapterImages": "Extraer Imaxes dos Capítulos",
+ "TaskCleanCacheDescription": "Borra ficheiros da caché que xa non son necesarios para o sistema.",
+ "TaskCleanCache": "Limpa Directorio de Caché",
+ "TaskCleanActivityLogDescription": "Borra as entradas no rexistro de actividade anteriores á data configurada.",
+ "TasksApplicationCategory": "Aplicación",
+ "ValueSpecialEpisodeName": "Especial - {0}",
+ "ValueHasBeenAddedToLibrary": "{0} foi engadido a túa libraría multimedia",
+ "TasksLibraryCategory": "Libraría",
+ "TasksMaintenanceCategory": "Mantemento",
+ "VersionNumber": "Versión {0}",
+ "UserPolicyUpdatedWithName": "A política de usuario foi actualizada para {0}",
+ "UserPasswordChangedWithName": "Cambiouse o contrasinal para o usuario {0}",
+ "UserOnlineFromDevice": "{0} está en liña desde {1}",
+ "UserOfflineFromDevice": "{0} desconectouse desde {1}"
}
diff --git a/Emby.Server.Implementations/Localization/Core/hu.json b/Emby.Server.Implementations/Localization/Core/hu.json
index 85848fed6..255d5427a 100644
--- a/Emby.Server.Implementations/Localization/Core/hu.json
+++ b/Emby.Server.Implementations/Localization/Core/hu.json
@@ -118,5 +118,7 @@
"TaskCleanActivityLog": "Tevékenységnapló törlése",
"Undefined": "Meghatározatlan",
"Forced": "Kényszerített",
- "Default": "Alapértelmezett"
+ "Default": "Alapértelmezett",
+ "TaskOptimizeDatabaseDescription": "Tömöríti az adatbázist és csonkolja a szabad helyet. A feladat futtatása a könyvtár beolvasása után, vagy egyéb, adatbázis-módosítást igénylő változtatások végrehajtása javíthatja a teljesítményt.",
+ "TaskOptimizeDatabase": "Adatbázis optimalizálása"
}
diff --git a/Emby.Server.Implementations/Localization/Core/it.json b/Emby.Server.Implementations/Localization/Core/it.json
index bd06f0a25..8b753400e 100644
--- a/Emby.Server.Implementations/Localization/Core/it.json
+++ b/Emby.Server.Implementations/Localization/Core/it.json
@@ -118,5 +118,7 @@
"TaskCleanActivityLogDescription": "Elimina gli inserimenti nel registro delle attività più vecchie dell’età configurata.",
"Undefined": "Non Definito",
"Forced": "Forzato",
- "Default": "Predefinito"
+ "Default": "Predefinito",
+ "TaskOptimizeDatabaseDescription": "Compatta Database e tronca spazi liberi. Eseguire questa azione dopo la scansione o dopo aver fatto altri cambiamenti inerenti il database potrebbe aumentarne la performance.",
+ "TaskOptimizeDatabase": "Ottimizza Database"
}
diff --git a/Emby.Server.Implementations/Localization/Core/kk.json b/Emby.Server.Implementations/Localization/Core/kk.json
index 4eee36989..1b4a18deb 100644
--- a/Emby.Server.Implementations/Localization/Core/kk.json
+++ b/Emby.Server.Implementations/Localization/Core/kk.json
@@ -118,5 +118,7 @@
"TaskRefreshLibraryDescription": "Tasyğyşhanadağy jaña faildardy skanerleidі jäne metaderekterdı jañğyrtady.",
"TaskRefreshChapterImagesDescription": "Sahnalary bar beineler üşın nobailar jasaidy.",
"TaskCleanCacheDescription": "Jüiede qajet emes keştelgen faildardy joiady.",
- "TaskCleanActivityLogDescription": "Äreket jūrnalyndağy teñşelgen jasynan asqan jazbalary joiady."
+ "TaskCleanActivityLogDescription": "Äreket jūrnalyndağy teñşelgen jasynan asqan jazbalary joiady.",
+ "TaskOptimizeDatabaseDescription": "Derekqordy qysyp, bos oryndy qysqartady. Būl tapsyrmany tasyğyşhanany skanerlegennen keiın nemese derekqorğa meñzeitın basqa özgertuler ıstelgennen keiın oryndau önımdılıktı damytuy mümkın.",
+ "TaskOptimizeDatabase": "Derekqordy oñtailandyru"
}
diff --git a/Emby.Server.Implementations/Localization/Core/ko.json b/Emby.Server.Implementations/Localization/Core/ko.json
index 9179bbc8d..409b4d26b 100644
--- a/Emby.Server.Implementations/Localization/Core/ko.json
+++ b/Emby.Server.Implementations/Localization/Core/ko.json
@@ -118,5 +118,7 @@
"TaskCleanActivityLog": "활동내역청소",
"Undefined": "일치하지 않음",
"Forced": "강제하기",
- "Default": "기본 설정"
+ "Default": "기본 설정",
+ "TaskOptimizeDatabaseDescription": "데이터베이스를 압축하고 사용 가능한 공간을 늘립니다. 라이브러리를 검색한 후 이 작업을 실행하거나 데이터베이스 수정같은 비슷한 작업을 수행하면 성능이 향상될 수 있습니다.",
+ "TaskOptimizeDatabase": "데이터베이스 최적화"
}
diff --git a/Emby.Server.Implementations/Localization/Core/ml.json b/Emby.Server.Implementations/Localization/Core/ml.json
index e764963cf..435f9b630 100644
--- a/Emby.Server.Implementations/Localization/Core/ml.json
+++ b/Emby.Server.Implementations/Localization/Core/ml.json
@@ -117,5 +117,7 @@
"Favorites": "പ്രിയങ്കരങ്ങൾ",
"Books": "പുസ്തകങ്ങൾ",
"Genres": "വിഭാഗങ്ങൾ",
- "Channels": "ചാനലുകൾ"
+ "Channels": "ചാനലുകൾ",
+ "TaskOptimizeDatabaseDescription": "ഡാറ്റാബേസ് ചുരുക്കുകയും സ്വതന്ത്ര ഇടം വെട്ടിച്ചുരുക്കുകയും ചെയ്യുന്നു. ലൈബ്രറി സ്‌കാൻ ചെയ്‌തതിനുശേഷം അല്ലെങ്കിൽ ഡാറ്റാബേസ് പരിഷ്‌ക്കരണങ്ങളെ സൂചിപ്പിക്കുന്ന മറ്റ് മാറ്റങ്ങൾ ചെയ്‌തതിന് ശേഷം ഈ ടാസ്‌ക് പ്രവർത്തിപ്പിക്കുന്നത് പ്രകടനം മെച്ചപ്പെടുത്തും.",
+ "TaskOptimizeDatabase": "ഡാറ്റാബേസ് ഒപ്റ്റിമൈസ് ചെയ്യുക"
}
diff --git a/Emby.Server.Implementations/Localization/Core/ms.json b/Emby.Server.Implementations/Localization/Core/ms.json
index 5b4c8ae10..b2dcf270c 100644
--- a/Emby.Server.Implementations/Localization/Core/ms.json
+++ b/Emby.Server.Implementations/Localization/Core/ms.json
@@ -5,7 +5,7 @@
"Artists": "Artis",
"AuthenticationSucceededWithUserName": "{0} berjaya disahkan",
"Books": "Buku-buku",
- "CameraImageUploadedFrom": "Ada gambar dari kamera yang baru dimuat naik melalui {0}",
+ "CameraImageUploadedFrom": "Gambar baharu telah dimuat naik melalui {0}",
"Channels": "Saluran",
"ChapterNameValue": "Bab {0}",
"Collections": "Koleksi",
@@ -101,5 +101,13 @@
"Forced": "Paksa",
"Default": "Asal",
"TaskCleanCache": "Bersihkan Direktori Cache",
- "TaskCleanActivityLogDescription": "Padamkan entri log aktiviti yang lebih tua daripada usia yang dikonfigurasi."
+ "TaskCleanActivityLogDescription": "Padamkan entri log aktiviti yang lebih tua daripada usia yang dikonfigurasi.",
+ "TaskRefreshPeople": "Segarkan Orang",
+ "TaskCleanLogsDescription": "Padamkan fail log yang berumur lebih dari {0} hari.",
+ "TaskCleanLogs": "Bersihkan Direktotri Log",
+ "TaskRefreshLibraryDescription": "Imbas perpustakaan media untuk mencari fail-fail baru dan menyegarkan metadata.",
+ "TaskRefreshLibrary": "Imbas Perpustakaan Media",
+ "TaskRefreshChapterImagesDescription": "Membuat gambaran kecil untuk video yang mempunyai bab.",
+ "TaskRefreshChapterImages": "Ekstrak Gambar-gambar Bab",
+ "TaskCleanCacheDescription": "Menghapuskan fail cache yang tidak lagi diperlukan oleh sistem."
}
diff --git a/Emby.Server.Implementations/Localization/Core/nl.json b/Emby.Server.Implementations/Localization/Core/nl.json
index 2973c8c6e..f79840c78 100644
--- a/Emby.Server.Implementations/Localization/Core/nl.json
+++ b/Emby.Server.Implementations/Localization/Core/nl.json
@@ -1,7 +1,7 @@
{
"Albums": "Albums",
"AppDeviceValues": "App: {0}, Apparaat: {1}",
- "Application": "Applicatie",
+ "Application": "Toepassing",
"Artists": "Artiesten",
"AuthenticationSucceededWithUserName": "{0} is succesvol geauthenticeerd",
"Books": "Boeken",
@@ -25,7 +25,7 @@
"HeaderLiveTV": "Live TV",
"HeaderNextUp": "Volgende",
"HeaderRecordingGroups": "Opnamegroepen",
- "HomeVideos": "Home video's",
+ "HomeVideos": "Thuis video's",
"Inherit": "Erven",
"ItemAddedWithName": "{0} is toegevoegd aan de bibliotheek",
"ItemRemovedWithName": "{0} is verwijderd uit de bibliotheek",
@@ -92,11 +92,11 @@
"ValueHasBeenAddedToLibrary": "{0} is toegevoegd aan je mediabibliotheek",
"ValueSpecialEpisodeName": "Speciaal - {0}",
"VersionNumber": "Versie {0}",
- "TaskDownloadMissingSubtitlesDescription": "Zoekt op het internet naar missende ondertitels gebaseerd op metadata configuratie.",
- "TaskDownloadMissingSubtitles": "Download missende ondertitels",
+ "TaskDownloadMissingSubtitlesDescription": "Zoekt op het internet naar missende ondertiteling gebaseerd op metadata configuratie.",
+ "TaskDownloadMissingSubtitles": "Download missende ondertiteling",
"TaskRefreshChannelsDescription": "Vernieuwt informatie van internet kanalen.",
"TaskRefreshChannels": "Vernieuw Kanalen",
- "TaskCleanTranscodeDescription": "Verwijder transcode bestanden ouder dan 1 dag.",
+ "TaskCleanTranscodeDescription": "Verwijdert transcode bestanden ouder dan 1 dag.",
"TaskCleanLogs": "Log Folder Opschonen",
"TaskCleanTranscode": "Transcode Folder Opschonen",
"TaskUpdatePluginsDescription": "Download en installeert updates voor plugins waar automatisch updaten aan staat.",
@@ -108,15 +108,17 @@
"TaskRefreshLibrary": "Scan Media Bibliotheek",
"TaskRefreshChapterImagesDescription": "Maakt thumbnails aan voor videos met hoofdstukken.",
"TaskRefreshChapterImages": "Hoofdstukafbeeldingen Uitpakken",
- "TaskCleanCacheDescription": "Verwijder gecachte bestanden die het systeem niet langer nodig heeft.",
+ "TaskCleanCacheDescription": "Verwijdert gecachte bestanden die het systeem niet langer nodig heeft.",
"TaskCleanCache": "Cache Folder Opschonen",
"TasksChannelsCategory": "Internet Kanalen",
"TasksApplicationCategory": "Applicatie",
"TasksLibraryCategory": "Bibliotheek",
"TasksMaintenanceCategory": "Onderhoud",
- "TaskCleanActivityLogDescription": "Verwijder activiteiten logs ouder dan de ingestelde tijd.",
+ "TaskCleanActivityLogDescription": "Verwijdert activiteiten logs ouder dan de ingestelde tijd.",
"TaskCleanActivityLog": "Leeg activiteiten logboek",
"Undefined": "Niet gedefinieerd",
"Forced": "Geforceerd",
- "Default": "Standaard"
+ "Default": "Standaard",
+ "TaskOptimizeDatabaseDescription": "Comprimeert de database en trimt vrije ruimte. Het uitvoeren van deze taak kan de prestaties verbeteren, na het scannen van de bibliotheek of andere aanpassingen die invloed hebben op de database.",
+ "TaskOptimizeDatabase": "Database optimaliseren"
}
diff --git a/Emby.Server.Implementations/Localization/Core/pl.json b/Emby.Server.Implementations/Localization/Core/pl.json
index e3da96a85..275bdec6e 100644
--- a/Emby.Server.Implementations/Localization/Core/pl.json
+++ b/Emby.Server.Implementations/Localization/Core/pl.json
@@ -118,5 +118,6 @@
"TaskCleanActivityLog": "Czyść dziennik aktywności",
"Undefined": "Nieustalony",
"Forced": "Wymuszony",
- "Default": "Domyślne"
+ "Default": "Domyślne",
+ "TaskOptimizeDatabase": "Optymalizuj bazę danych"
}
diff --git a/Emby.Server.Implementations/Localization/Core/pt.json b/Emby.Server.Implementations/Localization/Core/pt.json
index f1a78b2d3..b435672ad 100644
--- a/Emby.Server.Implementations/Localization/Core/pt.json
+++ b/Emby.Server.Implementations/Localization/Core/pt.json
@@ -61,7 +61,7 @@
"NameSeasonUnknown": "Temporada Desconhecida",
"NameSeasonNumber": "Temporada {0}",
"NameInstallFailed": "Falha na instalação de {0}",
- "MusicVideos": "Videoclips",
+ "MusicVideos": "Videoclipes",
"Music": "Música",
"MixedContent": "Conteúdo Misto",
"MessageServerConfigurationUpdated": "A configuração do servidor foi actualizada",
diff --git a/Emby.Server.Implementations/Localization/Core/ru.json b/Emby.Server.Implementations/Localization/Core/ru.json
index e58f8c39d..248f06c4b 100644
--- a/Emby.Server.Implementations/Localization/Core/ru.json
+++ b/Emby.Server.Implementations/Localization/Core/ru.json
@@ -118,5 +118,7 @@
"TaskCleanActivityLog": "Очистить журнал активности",
"Undefined": "Не определено",
"Forced": "Форсир-ые",
- "Default": "По умолчанию"
+ "Default": "По умолчанию",
+ "TaskOptimizeDatabaseDescription": "Сжимает базу данных и обрезает свободное место. Выполнение этой задачи после сканирования библиотеки или внесения других изменений, предполагающих модификации базы данных, может повысить производительность.",
+ "TaskOptimizeDatabase": "Оптимизировать базу данных"
}
diff --git a/Emby.Server.Implementations/Localization/Core/sk.json b/Emby.Server.Implementations/Localization/Core/sk.json
index 99fbd3954..37da7d5ab 100644
--- a/Emby.Server.Implementations/Localization/Core/sk.json
+++ b/Emby.Server.Implementations/Localization/Core/sk.json
@@ -118,5 +118,7 @@
"TaskCleanActivityLog": "Vyčistiť log aktivít",
"Undefined": "Nedefinované",
"Forced": "Vynútené",
- "Default": "Predvolené"
+ "Default": "Predvolené",
+ "TaskOptimizeDatabaseDescription": "Zmenší databázu a odstráni prázdne miesto. Spustenie tejto úlohy po skenovaní knižnice alebo po iných zmenách zahŕňajúcich úpravy databáze môže zlepšiť výkon.",
+ "TaskOptimizeDatabase": "Optimalizovať databázu"
}
diff --git a/Emby.Server.Implementations/Localization/Core/sl-SI.json b/Emby.Server.Implementations/Localization/Core/sl-SI.json
index 343e067b7..1852dc89e 100644
--- a/Emby.Server.Implementations/Localization/Core/sl-SI.json
+++ b/Emby.Server.Implementations/Localization/Core/sl-SI.json
@@ -118,5 +118,7 @@
"TaskCleanActivityLog": "Počisti dnevnik aktivnosti",
"Undefined": "Nedoločen",
"Forced": "Prisilno",
- "Default": "Privzeto"
+ "Default": "Privzeto",
+ "TaskOptimizeDatabaseDescription": "Stisne bazo podatkov in uredi prazen prostor. Zagon tega opravila po iskanju predstavnosti ali drugih spremembah ki vplivajo na bazo podatkov lahko izboljša hitrost delovanja.",
+ "TaskOptimizeDatabase": "Optimiziraj bazo podatkov"
}
diff --git a/Emby.Server.Implementations/Localization/Core/sq.json b/Emby.Server.Implementations/Localization/Core/sq.json
index 0d909b06e..e36fdc43d 100644
--- a/Emby.Server.Implementations/Localization/Core/sq.json
+++ b/Emby.Server.Implementations/Localization/Core/sq.json
@@ -42,8 +42,8 @@
"Sync": "Sinkronizo",
"SubtitleDownloadFailureFromForItem": "Titrat deshtuan të shkarkohen nga {0} për {1}",
"StartupEmbyServerIsLoading": "Serveri Jellyfin po ngarkohet. Ju lutemi provoni përseri pas pak.",
- "Songs": "Këngë",
- "Shows": "Seriale",
+ "Songs": "Këngët",
+ "Shows": "Serialet",
"ServerNameNeedsToBeRestarted": "{0} duhet të ristartoj",
"ScheduledTaskStartedWithName": "{0} filloi",
"ScheduledTaskFailedWithName": "{0} dështoi",
@@ -74,9 +74,9 @@
"NameSeasonUnknown": "Sezon i panjohur",
"NameSeasonNumber": "Sezoni {0}",
"NameInstallFailed": "Instalimi i {0} dështoi",
- "MusicVideos": "Video muzikore",
+ "MusicVideos": "Videot muzikore",
"Music": "Muzikë",
- "Movies": "Filma",
+ "Movies": "Filmat",
"MixedContent": "Përmbajtje e përzier",
"MessageServerConfigurationUpdated": "Konfigurimet e serverit u përditësuan",
"MessageNamedServerConfigurationUpdatedWithValue": "Seksioni i konfigurimit të serverit {0} u përditësua",
@@ -97,20 +97,27 @@
"HeaderFavoriteAlbums": "Albumet e preferuar",
"HeaderContinueWatching": "Vazhdo të shikosh",
"HeaderAlbumArtists": "Artistët e albumeve",
- "Genres": "Zhanre",
- "Folders": "Dosje",
- "Favorites": "Të preferuara",
+ "Genres": "Zhanret",
+ "Folders": "Skedarët",
+ "Favorites": "Të preferuarat",
"FailedLoginAttemptWithUserName": "Përpjekja për hyrje dështoi nga {0}",
"DeviceOnlineWithName": "{0} u lidh",
"DeviceOfflineWithName": "{0} u shkëput",
- "Collections": "Koleksione",
+ "Collections": "Koleksionet",
"ChapterNameValue": "Kapituj",
- "Channels": "Kanale",
+ "Channels": "Kanalet",
"CameraImageUploadedFrom": "Një foto e re nga kamera u ngarkua nga {0}",
- "Books": "Libra",
+ "Books": "Librat",
"AuthenticationSucceededWithUserName": "{0} u identifikua me sukses",
- "Artists": "Artistë",
+ "Artists": "Artistët",
"Application": "Aplikacioni",
"AppDeviceValues": "Aplikacioni: {0}, Pajisja: {1}",
- "Albums": "Albume"
+ "Albums": "Albumet",
+ "TaskCleanActivityLogDescription": "Pastro të dhënat mbi aktivitetin më të vjetra sesa koha e përcaktuar.",
+ "TaskCleanActivityLog": "Pastro të dhënat mbi aktivitetin",
+ "Undefined": "I papërcaktuar",
+ "Forced": "I detyruar",
+ "Default": "Parazgjedhur",
+ "TaskOptimizeDatabaseDescription": "Kompakton bazën e të dhënave dhe shkurton hapësirën e lirë. Drejtimi i kësaj detyre pasi skanoni bibliotekën ose bëni ndryshime të tjera që nënkuptojnë modifikime të bazës së të dhënave mund të përmirësojë performancën.",
+ "TaskOptimizeDatabase": "Optimizo databazën"
}
diff --git a/Emby.Server.Implementations/Localization/Core/ta.json b/Emby.Server.Implementations/Localization/Core/ta.json
index 129986ed0..d6e9aa8e5 100644
--- a/Emby.Server.Implementations/Localization/Core/ta.json
+++ b/Emby.Server.Implementations/Localization/Core/ta.json
@@ -117,5 +117,7 @@
"TaskCleanActivityLog": "செயல்பாட்டு பதிவை அழி",
"Undefined": "வரையறுக்கப்படாத",
"Forced": "கட்டாயப்படுத்தப்பட்டது",
- "Default": "இயல்புநிலை"
+ "Default": "இயல்புநிலை",
+ "TaskOptimizeDatabaseDescription": "தரவுத்தளத்தை சுருக்கி, இலவச இடத்தை குறைக்கிறது. நூலகத்தை ஸ்கேன் செய்தபின் அல்லது தரவுத்தள மாற்றங்களைக் குறிக்கும் பிற மாற்றங்களைச் செய்தபின் இந்த பணியை இயக்குவது செயல்திறனை மேம்படுத்தக்கூடும்.",
+ "TaskOptimizeDatabase": "தரவுத்தளத்தை மேம்படுத்தவும்"
}
diff --git a/Emby.Server.Implementations/Localization/Core/vi.json b/Emby.Server.Implementations/Localization/Core/vi.json
index 58652c469..20ab1dd7d 100644
--- a/Emby.Server.Implementations/Localization/Core/vi.json
+++ b/Emby.Server.Implementations/Localization/Core/vi.json
@@ -117,5 +117,7 @@
"TaskCleanActivityLog": "Xóa Nhật Ký Hoạt Động",
"Undefined": "Không Xác Định",
"Forced": "Bắt Buộc",
- "Default": "Mặc Định"
+ "Default": "Mặc Định",
+ "TaskOptimizeDatabaseDescription": "Thu gọn cơ sở dữ liệu và cắt bớt dung lượng trống. Chạy tác vụ này sau khi quét thư viện hoặc thực hiện các thay đổi khác ngụ ý sửa đổi cơ sở dữ liệu có thể cải thiện hiệu suất.",
+ "TaskOptimizeDatabase": "Tối ưu hóa cơ sở dữ liệu"
}
diff --git a/Emby.Server.Implementations/Localization/Core/zh-CN.json b/Emby.Server.Implementations/Localization/Core/zh-CN.json
index 12803456e..faa9c40e2 100644
--- a/Emby.Server.Implementations/Localization/Core/zh-CN.json
+++ b/Emby.Server.Implementations/Localization/Core/zh-CN.json
@@ -118,5 +118,7 @@
"TaskCleanActivityLogDescription": "删除早于设置时间的活动日志条目。",
"Undefined": "未定义",
"Forced": "强制的",
- "Default": "默认"
+ "Default": "默认",
+ "TaskOptimizeDatabaseDescription": "压缩数据库并优化可用空间,在扫描库或执行其他数据库修改后运行此任务可能会提高性能。",
+ "TaskOptimizeDatabase": "优化数据库"
}
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanActivityLogTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanActivityLogTask.cs
index 50ba9bc89..19600b1e6 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanActivityLogTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanActivityLogTask.cs
@@ -65,7 +65,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
throw new Exception($"Activity Log Retention days must be at least 0. Currently: {retentionDays}");
}
- var startDate = DateTime.UtcNow.AddDays(retentionDays.Value * -1);
+ var startDate = DateTime.UtcNow.AddDays(-retentionDays.Value);
return _activityManager.CleanAsync(startDate);
}
diff --git a/Jellyfin.Api/Controllers/InstantMixController.cs b/Jellyfin.Api/Controllers/InstantMixController.cs
index f232dffaa..4774ed4ef 100644
--- a/Jellyfin.Api/Controllers/InstantMixController.cs
+++ b/Jellyfin.Api/Controllers/InstantMixController.cs
@@ -229,42 +229,6 @@ namespace Jellyfin.Api.Controllers
}
/// <summary>
- /// Creates an instant playlist based on a given genre.
- /// </summary>
- /// <param name="id">The item id.</param>
- /// <param name="userId">Optional. Filter by user id, and attach user data.</param>
- /// <param name="limit">Optional. The maximum number of records to return.</param>
- /// <param name="fields">Optional. Specify additional fields of information to return in the output.</param>
- /// <param name="enableImages">Optional. Include image information in output.</param>
- /// <param name="enableUserData">Optional. Include user data.</param>
- /// <param name="imageTypeLimit">Optional. The max number of images to return, per image type.</param>
- /// <param name="enableImageTypes">Optional. The image types to include in the output.</param>
- /// <response code="200">Instant playlist returned.</response>
- /// <returns>A <see cref="QueryResult{BaseItemDto}"/> with the playlist items.</returns>
- [HttpGet("MusicGenres/{id}/InstantMix")]
- [ProducesResponseType(StatusCodes.Status200OK)]
- public ActionResult<QueryResult<BaseItemDto>> GetInstantMixFromMusicGenreById(
- [FromRoute, Required] Guid id,
- [FromQuery] Guid? userId,
- [FromQuery] int? limit,
- [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
- [FromQuery] bool? enableImages,
- [FromQuery] bool? enableUserData,
- [FromQuery] int? imageTypeLimit,
- [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes)
- {
- var item = _libraryManager.GetItemById(id);
- var user = userId.HasValue && !userId.Equals(Guid.Empty)
- ? _userManager.GetUserById(userId.Value)
- : null;
- var dtoOptions = new DtoOptions { Fields = fields }
- .AddClientFields(Request)
- .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes!);
- var items = _musicManager.GetInstantMixFromItem(item, user, dtoOptions);
- return GetResult(items, user, limit, dtoOptions);
- }
-
- /// <summary>
/// Creates an instant playlist based on a given item.
/// </summary>
/// <param name="id">The item id.</param>
@@ -352,8 +316,7 @@ namespace Jellyfin.Api.Controllers
/// <returns>A <see cref="QueryResult{BaseItemDto}"/> with the playlist items.</returns>
[HttpGet("MusicGenres/InstantMix")]
[ProducesResponseType(StatusCodes.Status200OK)]
- [Obsolete("Use GetInstantMixFromMusicGenres instead")]
- public ActionResult<QueryResult<BaseItemDto>> GetInstantMixFromMusicGenreById2(
+ public ActionResult<QueryResult<BaseItemDto>> GetInstantMixFromMusicGenreById(
[FromQuery, Required] Guid id,
[FromQuery] Guid? userId,
[FromQuery] int? limit,
@@ -363,15 +326,15 @@ namespace Jellyfin.Api.Controllers
[FromQuery] int? imageTypeLimit,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes)
{
- return GetInstantMixFromMusicGenreById(
- id,
- userId,
- limit,
- fields,
- enableImages,
- enableUserData,
- imageTypeLimit,
- enableImageTypes);
+ var item = _libraryManager.GetItemById(id);
+ var user = userId.HasValue && !userId.Equals(Guid.Empty)
+ ? _userManager.GetUserById(userId.Value)
+ : null;
+ var dtoOptions = new DtoOptions { Fields = fields }
+ .AddClientFields(Request)
+ .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes!);
+ var items = _musicManager.GetInstantMixFromItem(item, user, dtoOptions);
+ return GetResult(items, user, limit, dtoOptions);
}
private QueryResult<BaseItemDto> GetResult(List<BaseItem> items, User? user, int? limit, DtoOptions dtoOptions)
diff --git a/Jellyfin.Api/Controllers/MediaInfoController.cs b/Jellyfin.Api/Controllers/MediaInfoController.cs
index e330f02b6..8903e0ce8 100644
--- a/Jellyfin.Api/Controllers/MediaInfoController.cs
+++ b/Jellyfin.Api/Controllers/MediaInfoController.cs
@@ -161,6 +161,11 @@ namespace Jellyfin.Api.Controllers
liveStreamId)
.ConfigureAwait(false);
+ if (info.ErrorCode != null)
+ {
+ return info;
+ }
+
if (profile != null)
{
// set device specific data
@@ -302,27 +307,12 @@ namespace Jellyfin.Api.Controllers
/// </summary>
/// <param name="size">The bitrate. Defaults to 102400.</param>
/// <response code="200">Test buffer returned.</response>
- /// <response code="400">Size has to be a numer between 0 and 10,000,000.</response>
/// <returns>A <see cref="FileResult"/> with specified bitrate.</returns>
[HttpGet("Playback/BitrateTest")]
[ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status400BadRequest)]
- [Produces(MediaTypeNames.Application.Octet)]
[ProducesFile(MediaTypeNames.Application.Octet)]
- public ActionResult GetBitrateTestBytes([FromQuery] int size = 102400)
+ public ActionResult GetBitrateTestBytes([FromQuery][Range(1, 100_000_000, ErrorMessage = "The requested size must be greater than or equal to {1} and less than or equal to {2}")] int size = 102400)
{
- const int MaxSize = 10_000_000;
-
- if (size <= 0)
- {
- return BadRequest($"The requested size ({size}) is equal to or smaller than 0.");
- }
-
- if (size > MaxSize)
- {
- return BadRequest($"The requested size ({size}) is larger than the max allowed value ({MaxSize}).");
- }
-
byte[] buffer = ArrayPool<byte>.Shared.Rent(size);
try
{
diff --git a/Jellyfin.Api/Controllers/VideosController.cs b/Jellyfin.Api/Controllers/VideosController.cs
index dc64a0f1b..29a25fa6a 100644
--- a/Jellyfin.Api/Controllers/VideosController.cs
+++ b/Jellyfin.Api/Controllers/VideosController.cs
@@ -296,6 +296,8 @@ namespace Jellyfin.Api.Controllers
/// <param name="startTimeTicks">Optional. Specify a starting offset, in ticks. 1 tick = 10000 ms.</param>
/// <param name="width">Optional. The fixed horizontal resolution of the encoded video.</param>
/// <param name="height">Optional. The fixed vertical resolution of the encoded video.</param>
+ /// <param name="maxWidth">Optional. The maximum horizontal resolution of the encoded video.</param>
+ /// <param name="maxHeight">Optional. The maximum vertical resolution of the encoded video.</param>
/// <param name="videoBitRate">Optional. Specify a video bitrate to encode to, e.g. 500000. If omitted this will be left to encoder defaults.</param>
/// <param name="subtitleStreamIndex">Optional. The index of the subtitle stream to use. If omitted no subtitles will be used.</param>
/// <param name="subtitleMethod">Optional. Specify the subtitle delivery method.</param>
@@ -352,6 +354,8 @@ namespace Jellyfin.Api.Controllers
[FromQuery] long? startTimeTicks,
[FromQuery] int? width,
[FromQuery] int? height,
+ [FromQuery] int? maxWidth,
+ [FromQuery] int? maxHeight,
[FromQuery] int? videoBitRate,
[FromQuery] int? subtitleStreamIndex,
[FromQuery] SubtitleDeliveryMethod? subtitleMethod,
@@ -407,6 +411,8 @@ namespace Jellyfin.Api.Controllers
StartTimeTicks = startTimeTicks,
Width = width,
Height = height,
+ MaxWidth = maxWidth,
+ MaxHeight = maxHeight,
VideoBitRate = videoBitRate,
SubtitleStreamIndex = subtitleStreamIndex,
SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode,
@@ -550,6 +556,8 @@ namespace Jellyfin.Api.Controllers
/// <param name="startTimeTicks">Optional. Specify a starting offset, in ticks. 1 tick = 10000 ms.</param>
/// <param name="width">Optional. The fixed horizontal resolution of the encoded video.</param>
/// <param name="height">Optional. The fixed vertical resolution of the encoded video.</param>
+ /// <param name="maxWidth">Optional. The maximum horizontal resolution of the encoded video.</param>
+ /// <param name="maxHeight">Optional. The maximum vertical resolution of the encoded video.</param>
/// <param name="videoBitRate">Optional. Specify a video bitrate to encode to, e.g. 500000. If omitted this will be left to encoder defaults.</param>
/// <param name="subtitleStreamIndex">Optional. The index of the subtitle stream to use. If omitted no subtitles will be used.</param>
/// <param name="subtitleMethod">Optional. Specify the subtitle delivery method.</param>
@@ -606,6 +614,8 @@ namespace Jellyfin.Api.Controllers
[FromQuery] long? startTimeTicks,
[FromQuery] int? width,
[FromQuery] int? height,
+ [FromQuery] int? maxWidth,
+ [FromQuery] int? maxHeight,
[FromQuery] int? videoBitRate,
[FromQuery] int? subtitleStreamIndex,
[FromQuery] SubtitleDeliveryMethod? subtitleMethod,
@@ -657,6 +667,8 @@ namespace Jellyfin.Api.Controllers
startTimeTicks,
width,
height,
+ maxWidth,
+ maxHeight,
videoBitRate,
subtitleStreamIndex,
subtitleMethod,
diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj
index bd7da9b06..d1d0ac708 100644
--- a/Jellyfin.Api/Jellyfin.Api.csproj
+++ b/Jellyfin.Api/Jellyfin.Api.csproj
@@ -15,7 +15,7 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.AspNetCore.Authorization" Version="5.0.7" />
+ <PackageReference Include="Microsoft.AspNetCore.Authorization" Version="5.0.8" />
<PackageReference Include="Microsoft.Extensions.Http" Version="5.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.1.4" />
<PackageReference Include="Swashbuckle.AspNetCore.ReDoc" Version="6.1.4" />
diff --git a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj
index ee43c2159..96fe00384 100644
--- a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj
+++ b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj
@@ -22,8 +22,8 @@
<ItemGroup>
<PackageReference Include="BlurHashSharp" Version="1.2.0" />
<PackageReference Include="BlurHashSharp.SkiaSharp" Version="1.2.0" />
- <PackageReference Include="SkiaSharp" Version="2.80.2" />
- <PackageReference Include="SkiaSharp.NativeAssets.Linux" Version="2.80.2" />
+ <PackageReference Include="SkiaSharp" Version="2.80.3" />
+ <PackageReference Include="SkiaSharp.NativeAssets.Linux" Version="2.80.3" />
</ItemGroup>
<ItemGroup>
diff --git a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj
index eeeb1d19b..f73492b7c 100644
--- a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj
+++ b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj
@@ -27,13 +27,13 @@
<ItemGroup>
<PackageReference Include="System.Linq.Async" Version="5.0.0" />
- <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.7" />
- <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="5.0.7" />
- <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.7">
+ <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.8" />
+ <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="5.0.8" />
+ <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.8">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
- <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="5.0.7">
+ <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="5.0.8">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj
index 9a197e037..958a44fda 100644
--- a/Jellyfin.Server/Jellyfin.Server.csproj
+++ b/Jellyfin.Server/Jellyfin.Server.csproj
@@ -38,15 +38,15 @@
<PackageReference Include="CommandLineParser" Version="2.8.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="5.0.0" />
- <PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="5.0.7" />
- <PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="5.0.7" />
- <PackageReference Include="prometheus-net" Version="4.1.1" />
- <PackageReference Include="prometheus-net.AspNetCore" Version="4.1.1" />
+ <PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="5.0.8" />
+ <PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="5.0.8" />
+ <PackageReference Include="prometheus-net" Version="4.2.0" />
+ <PackageReference Include="prometheus-net.AspNetCore" Version="4.2.0" />
<PackageReference Include="Serilog.AspNetCore" Version="4.1.0" />
<PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="3.1.0" />
<PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />
- <PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
+ <PackageReference Include="Serilog.Sinks.Console" Version="4.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="Serilog.Sinks.Graylog" Version="2.2.2" />
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.0.4" />
diff --git a/MediaBrowser.Common/Net/IPHost.cs b/MediaBrowser.Common/Net/IPHost.cs
index 5db8817ee..d78d7def2 100644
--- a/MediaBrowser.Common/Net/IPHost.cs
+++ b/MediaBrowser.Common/Net/IPHost.cs
@@ -79,16 +79,11 @@ namespace MediaBrowser.Common.Net
/// </summary>
public override byte PrefixLength
{
- get
- {
- return (byte)(ResolveHost() ? 128 : 32);
- }
+ get => (byte)(ResolveHost() ? 128 : 32);
- set
- {
- // Not implemented, as a host object can only have a prefix length of 128 (IPv6) or 32 (IPv4) prefix length,
- // which is automatically determined by it's IP type. Anything else is meaningless.
- }
+ // Not implemented, as a host object can only have a prefix length of 128 (IPv6) or 32 (IPv4) prefix length,
+ // which is automatically determined by it's IP type. Anything else is meaningless.
+ set => throw new NotImplementedException();
}
/// <summary>
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs
index e5be5421a..23b97f70c 100644
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ b/MediaBrowser.Controller/Entities/BaseItem.cs
@@ -771,19 +771,6 @@ namespace MediaBrowser.Controller.Entities
[JsonIgnore]
public Guid ParentId { get; set; }
- /// <summary>
- /// Gets or sets the parent.
- /// </summary>
- /// <value>The parent.</value>
- [JsonIgnore]
- public Folder Parent
- {
- get => GetParent() as Folder;
- set
- {
- }
- }
-
public void SetParent(Folder parent)
{
ParentId = parent == null ? Guid.Empty : parent.Id;
@@ -822,8 +809,7 @@ namespace MediaBrowser.Controller.Entities
{
foreach (var parent in GetParents())
{
- var item = parent as T;
- if (item != null)
+ if (parent is T item)
{
return item;
}
diff --git a/MediaBrowser.Controller/Entities/UserView.cs b/MediaBrowser.Controller/Entities/UserView.cs
index 57dc9b59b..62f3c4b55 100644
--- a/MediaBrowser.Controller/Entities/UserView.cs
+++ b/MediaBrowser.Controller/Entities/UserView.cs
@@ -15,6 +15,25 @@ namespace MediaBrowser.Controller.Entities
{
public class UserView : Folder, IHasCollectionType
{
+ private static readonly string[] _viewTypesEligibleForGrouping = new string[]
+ {
+ Model.Entities.CollectionType.Movies,
+ Model.Entities.CollectionType.TvShows,
+ string.Empty
+ };
+
+ private static readonly string[] _originalFolderViewTypes = new string[]
+ {
+ Model.Entities.CollectionType.Books,
+ Model.Entities.CollectionType.MusicVideos,
+ Model.Entities.CollectionType.HomeVideos,
+ Model.Entities.CollectionType.Photos,
+ Model.Entities.CollectionType.Music,
+ Model.Entities.CollectionType.BoxSets
+ };
+
+ public static ITVSeriesManager TVSeriesManager { get; set; }
+
/// <summary>
/// Gets or sets the view type.
/// </summary>
@@ -30,13 +49,23 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
public Guid? UserId { get; set; }
- public static ITVSeriesManager TVSeriesManager;
-
/// <inheritdoc />
[JsonIgnore]
public string CollectionType => ViewType;
/// <inheritdoc />
+ [JsonIgnore]
+ public override bool SupportsInheritedParentImages => false;
+
+ /// <inheritdoc />
+ [JsonIgnore]
+ public override bool SupportsPlayedStatus => false;
+
+ /// <inheritdoc />
+ [JsonIgnore]
+ public override bool SupportsPeople => false;
+
+ /// <inheritdoc />
public override IEnumerable<Guid> GetIdsForAncestorQuery()
{
if (!DisplayParentId.Equals(Guid.Empty))
@@ -53,17 +82,13 @@ namespace MediaBrowser.Controller.Entities
}
}
- [JsonIgnore]
- public override bool SupportsInheritedParentImages => false;
-
- [JsonIgnore]
- public override bool SupportsPlayedStatus => false;
-
+ /// <inheritdoc />
public override int GetChildCount(User user)
{
return GetChildren(user, true).Count;
}
+ /// <inheritdoc />
protected override QueryResult<BaseItem> GetItemsInternal(InternalItemsQuery query)
{
var parent = this as Folder;
@@ -81,6 +106,7 @@ namespace MediaBrowser.Controller.Entities
.GetUserItems(parent, this, CollectionType, query);
}
+ /// <inheritdoc />
public override List<BaseItem> GetChildren(User user, bool includeLinkedChildren, InternalItemsQuery query)
{
query ??= new InternalItemsQuery(user);
@@ -91,16 +117,19 @@ namespace MediaBrowser.Controller.Entities
return result.ToList();
}
+ /// <inheritdoc />
public override bool CanDelete()
{
return false;
}
+ /// <inheritdoc />
public override bool IsSaveLocalMetadataEnabled()
{
return true;
}
+ /// <inheritdoc />
public override IEnumerable<BaseItem> GetRecursiveChildren(User user, InternalItemsQuery query)
{
query.SetUser(user);
@@ -111,32 +140,26 @@ namespace MediaBrowser.Controller.Entities
return GetItemList(query);
}
+ /// <inheritdoc />
protected override IEnumerable<BaseItem> GetEligibleChildrenForRecursiveChildren(User user)
{
return GetChildren(user, false);
}
- private static readonly string[] UserSpecificViewTypes = new string[]
- {
- Model.Entities.CollectionType.Playlists
- };
-
public static bool IsUserSpecific(Folder folder)
{
- var collectionFolder = folder as ICollectionFolder;
-
- if (collectionFolder == null)
+ if (folder is not ICollectionFolder collectionFolder)
{
return false;
}
- var supportsUserSpecific = folder as ISupportsUserSpecificView;
- if (supportsUserSpecific != null && supportsUserSpecific.EnableUserSpecificView)
+ if (folder is ISupportsUserSpecificView supportsUserSpecific
+ && supportsUserSpecific.EnableUserSpecificView)
{
return true;
}
- return UserSpecificViewTypes.Contains(collectionFolder.CollectionType ?? string.Empty, StringComparer.OrdinalIgnoreCase);
+ return string.Equals(Model.Entities.CollectionType.Playlists, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase);
}
public static bool IsEligibleForGrouping(Folder folder)
@@ -145,39 +168,19 @@ namespace MediaBrowser.Controller.Entities
&& IsEligibleForGrouping(collectionFolder.CollectionType);
}
- private static string[] ViewTypesEligibleForGrouping = new string[]
- {
- Model.Entities.CollectionType.Movies,
- Model.Entities.CollectionType.TvShows,
- string.Empty
- };
-
public static bool IsEligibleForGrouping(string viewType)
{
- return ViewTypesEligibleForGrouping.Contains(viewType ?? string.Empty, StringComparer.OrdinalIgnoreCase);
+ return _viewTypesEligibleForGrouping.Contains(viewType ?? string.Empty, StringComparer.OrdinalIgnoreCase);
}
- private static string[] OriginalFolderViewTypes = new string[]
- {
- Model.Entities.CollectionType.Books,
- Model.Entities.CollectionType.MusicVideos,
- Model.Entities.CollectionType.HomeVideos,
- Model.Entities.CollectionType.Photos,
- Model.Entities.CollectionType.Music,
- Model.Entities.CollectionType.BoxSets
- };
-
public static bool EnableOriginalFolder(string viewType)
{
- return OriginalFolderViewTypes.Contains(viewType ?? string.Empty, StringComparer.OrdinalIgnoreCase);
+ return _originalFolderViewTypes.Contains(viewType ?? string.Empty, StringComparer.OrdinalIgnoreCase);
}
protected override Task ValidateChildrenInternal(IProgress<double> progress, bool recursive, bool refreshChildMetadata, Providers.MetadataRefreshOptions refreshOptions, Providers.IDirectoryService directoryService, System.Threading.CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
-
- [JsonIgnore]
- public override bool SupportsPeople => false;
}
}
diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs
index 094923842..753c18bc7 100644
--- a/MediaBrowser.Controller/IServerApplicationHost.cs
+++ b/MediaBrowser.Controller/IServerApplicationHost.cs
@@ -16,8 +16,6 @@ namespace MediaBrowser.Controller
/// </summary>
public interface IServerApplicationHost : IApplicationHost
{
- event EventHandler HasUpdateAvailableChanged;
-
bool CoreStartupHasCompleted { get; }
bool CanLaunchWebBrowser { get; }
@@ -40,12 +38,6 @@ namespace MediaBrowser.Controller
bool ListenWithHttps { get; }
/// <summary>
- /// Gets a value indicating whether this instance has update available.
- /// </summary>
- /// <value><c>true</c> if this instance has update available; otherwise, <c>false</c>.</value>
- bool HasUpdateAvailable { get; }
-
- /// <summary>
/// Gets the name of the friendly.
/// </summary>
/// <value>The name of the friendly.</value>
diff --git a/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs b/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs
index 1fa90bb21..d0a76c4ca 100644
--- a/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs
+++ b/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -22,7 +20,7 @@ namespace MediaBrowser.MediaEncoding.Probing
throw new ArgumentNullException(nameof(result));
}
- if (result.Format != null && result.Format.Tags != null)
+ if (result.Format?.Tags != null)
{
result.Format.Tags = ConvertDictionaryToCaseInsensitive(result.Format.Tags);
}
@@ -41,38 +39,16 @@ namespace MediaBrowser.MediaEncoding.Probing
}
/// <summary>
- /// Gets a string from an FFProbeResult tags dictionary.
- /// </summary>
- /// <param name="tags">The tags.</param>
- /// <param name="key">The key.</param>
- /// <returns>System.String.</returns>
- public static string GetDictionaryValue(IReadOnlyDictionary<string, string> tags, string key)
- {
- if (tags == null)
- {
- return null;
- }
-
- tags.TryGetValue(key, out var val);
- return val;
- }
-
- /// <summary>
/// Gets an int from an FFProbeResult tags dictionary.
/// </summary>
/// <param name="tags">The tags.</param>
/// <param name="key">The key.</param>
/// <returns>System.Nullable{System.Int32}.</returns>
- public static int? GetDictionaryNumericValue(Dictionary<string, string> tags, string key)
+ public static int? GetDictionaryNumericValue(IReadOnlyDictionary<string, string> tags, string key)
{
- var val = GetDictionaryValue(tags, key);
-
- if (!string.IsNullOrEmpty(val))
+ if (tags.TryGetValue(key, out var val) && int.TryParse(val, out var i))
{
- if (int.TryParse(val, out var i))
- {
- return i;
- }
+ return i;
}
return null;
@@ -84,18 +60,12 @@ namespace MediaBrowser.MediaEncoding.Probing
/// <param name="tags">The tags.</param>
/// <param name="key">The key.</param>
/// <returns>System.Nullable{DateTime}.</returns>
- public static DateTime? GetDictionaryDateTime(Dictionary<string, string> tags, string key)
+ public static DateTime? GetDictionaryDateTime(IReadOnlyDictionary<string, string> tags, string key)
{
- var val = GetDictionaryValue(tags, key);
-
- if (string.IsNullOrEmpty(val))
- {
- return null;
- }
-
- if (DateTime.TryParse(val, DateTimeFormatInfo.CurrentInfo, DateTimeStyles.AssumeUniversal, out var i))
+ if (tags.TryGetValue(key, out var val)
+ && DateTime.TryParse(val, DateTimeFormatInfo.CurrentInfo, DateTimeStyles.AssumeUniversal, out var dateTime))
{
- return i.ToUniversalTime();
+ return dateTime.ToUniversalTime();
}
return null;
diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
index bbff5daca..c9ad3c41e 100644
--- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
+++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
@@ -8,6 +8,7 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
+using Jellyfin.Extensions;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
@@ -38,7 +39,16 @@ namespace MediaBrowser.MediaEncoding.Probing
_localization = localization;
}
- private IReadOnlyList<string> SplitWhitelist => _splitWhiteList ??= new string[] { "AC/DC" };
+ private IReadOnlyList<string> SplitWhitelist => _splitWhiteList ??= new string[]
+ {
+ "AC/DC",
+ "Au/Ra",
+ "이달의 소녀 1/3",
+ "LOONA 1/3",
+ "LOONA / yyxy",
+ "LOONA / ODD EYE CIRCLE",
+ "K/DA"
+ };
public MediaInfo GetMediaInfo(InternalMediaInfoResult data, VideoType? videoType, bool isAudio, string path, MediaProtocol protocol)
{
@@ -80,68 +90,33 @@ namespace MediaBrowser.MediaEncoding.Probing
var tags = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
var tagStreamType = isAudio ? "audio" : "video";
- if (data.Streams != null)
- {
- var tagStream = data.Streams.FirstOrDefault(i => string.Equals(i.CodecType, tagStreamType, StringComparison.OrdinalIgnoreCase));
+ var tagStream = data.Streams?.FirstOrDefault(i => string.Equals(i.CodecType, tagStreamType, StringComparison.OrdinalIgnoreCase));
- if (tagStream != null && tagStream.Tags != null)
+ if (tagStream?.Tags != null)
+ {
+ foreach (var (key, value) in tagStream.Tags)
{
- foreach (var pair in tagStream.Tags)
- {
- tags[pair.Key] = pair.Value;
- }
+ tags[key] = value;
}
}
- if (data.Format != null && data.Format.Tags != null)
+ if (data.Format?.Tags != null)
{
- foreach (var pair in data.Format.Tags)
+ foreach (var (key, value) in data.Format.Tags)
{
- tags[pair.Key] = pair.Value;
+ tags[key] = value;
}
}
FetchGenres(info, tags);
- var overview = FFProbeHelpers.GetDictionaryValue(tags, "synopsis");
-
- if (string.IsNullOrWhiteSpace(overview))
- {
- overview = FFProbeHelpers.GetDictionaryValue(tags, "description");
- }
-
- if (string.IsNullOrWhiteSpace(overview))
- {
- overview = FFProbeHelpers.GetDictionaryValue(tags, "desc");
- }
-
- if (!string.IsNullOrWhiteSpace(overview))
- {
- info.Overview = overview;
- }
-
- var title = FFProbeHelpers.GetDictionaryValue(tags, "title");
- if (!string.IsNullOrWhiteSpace(title))
- {
- info.Name = title;
- }
- else
- {
- title = FFProbeHelpers.GetDictionaryValue(tags, "title-eng");
- if (!string.IsNullOrWhiteSpace(title))
- {
- info.Name = title;
- }
- }
- var titleSort = FFProbeHelpers.GetDictionaryValue(tags, "titlesort");
- if (!string.IsNullOrWhiteSpace(titleSort))
- {
- info.ForcedSortName = titleSort;
- }
+ info.Name = tags.GetFirstNotNullNorWhiteSpaceValue("title", "title-eng");
+ info.ForcedSortName = tags.GetFirstNotNullNorWhiteSpaceValue("sort_name", "title-sort", "titlesort");
+ info.Overview = tags.GetFirstNotNullNorWhiteSpaceValue("synopsis", "description", "desc");
info.IndexNumber = FFProbeHelpers.GetDictionaryNumericValue(tags, "episode_sort");
info.ParentIndexNumber = FFProbeHelpers.GetDictionaryNumericValue(tags, "season_number");
- info.ShowName = FFProbeHelpers.GetDictionaryValue(tags, "show_name");
+ info.ShowName = tags.GetValueOrDefault("show_name");
info.ProductionYear = FFProbeHelpers.GetDictionaryNumericValue(tags, "date");
// Several different forms of retail/premiere date
@@ -153,32 +128,21 @@ namespace MediaBrowser.MediaEncoding.Probing
FFProbeHelpers.GetDictionaryDateTime(tags, "date");
// Set common metadata for music (audio) and music videos (video)
- info.Album = FFProbeHelpers.GetDictionaryValue(tags, "album");
-
- var artists = FFProbeHelpers.GetDictionaryValue(tags, "artists");
+ info.Album = tags.GetValueOrDefault("album");
- if (!string.IsNullOrWhiteSpace(artists))
+ if (tags.TryGetValue("artists", out var artists) && !string.IsNullOrWhiteSpace(artists))
{
- info.Artists = SplitArtists(artists, new[] { '/', ';' }, false)
- .DistinctNames()
- .ToArray();
+ info.Artists = SplitDistinctArtists(artists, new[] { '/', ';' }, false).ToArray();
}
else
{
- var artist = FFProbeHelpers.GetDictionaryValue(tags, "artist");
- if (string.IsNullOrWhiteSpace(artist))
- {
- info.Artists = Array.Empty<string>();
- }
- else
- {
- info.Artists = SplitArtists(artist, _nameDelimiters, true)
- .DistinctNames()
- .ToArray();
- }
+ var artist = tags.GetFirstNotNullNorWhiteSpaceValue("artist");
+ info.Artists = artist == null
+ ? Array.Empty<string>()
+ : SplitDistinctArtists(artist, _nameDelimiters, true).ToArray();
}
- // If we don't have a ProductionYear try and get it from PremiereDate
+ // Guess ProductionYear from PremiereDate if missing
if (!info.ProductionYear.HasValue && info.PremiereDate.HasValue)
{
info.ProductionYear = info.PremiereDate.Value.Year;
@@ -198,10 +162,10 @@ namespace MediaBrowser.MediaEncoding.Probing
{
FetchStudios(info, tags, "copyright");
- var iTunEXTC = FFProbeHelpers.GetDictionaryValue(tags, "iTunEXTC");
- if (!string.IsNullOrWhiteSpace(iTunEXTC))
+ var iTunExtc = tags.GetFirstNotNullNorWhiteSpaceValue("iTunEXTC");
+ if (iTunExtc != null)
{
- var parts = iTunEXTC.Split('|', StringSplitOptions.RemoveEmptyEntries);
+ var parts = iTunExtc.Split('|', StringSplitOptions.RemoveEmptyEntries);
// Example
// mpaa|G|100|For crude humor
if (parts.Length > 1)
@@ -215,10 +179,10 @@ namespace MediaBrowser.MediaEncoding.Probing
}
}
- var itunesXml = FFProbeHelpers.GetDictionaryValue(tags, "iTunMOVI");
- if (!string.IsNullOrWhiteSpace(itunesXml))
+ var iTunXml = tags.GetFirstNotNullNorWhiteSpaceValue("iTunMOVI");
+ if (iTunXml != null)
{
- FetchFromItunesInfo(itunesXml, info);
+ FetchFromItunesInfo(iTunXml, info);
}
if (data.Format != null && !string.IsNullOrEmpty(data.Format.Duration))
@@ -235,8 +199,7 @@ namespace MediaBrowser.MediaEncoding.Probing
ExtractTimestamp(info);
- var stereoMode = GetDictionaryValue(tags, "stereo_mode");
- if (string.Equals(stereoMode, "left_right", StringComparison.OrdinalIgnoreCase))
+ if (tags.TryGetValue("stereo_mode", out var stereoMode) && string.Equals(stereoMode, "left_right", StringComparison.OrdinalIgnoreCase))
{
info.Video3DFormat = Video3DFormat.FullSideBySide;
}
@@ -289,42 +252,36 @@ namespace MediaBrowser.MediaEncoding.Probing
if (string.Equals(codec, "aac", StringComparison.OrdinalIgnoreCase)
|| string.Equals(codec, "mp3", StringComparison.OrdinalIgnoreCase))
{
- if (channelsValue <= 2)
- {
- return 192000;
- }
-
- if (channelsValue >= 5)
+ switch (channelsValue)
{
- return 320000;
+ case <= 2:
+ return 192000;
+ case >= 5:
+ return 320000;
}
}
if (string.Equals(codec, "ac3", StringComparison.OrdinalIgnoreCase)
|| string.Equals(codec, "eac3", StringComparison.OrdinalIgnoreCase))
{
- if (channelsValue <= 2)
- {
- return 192000;
- }
-
- if (channelsValue >= 5)
+ switch (channelsValue)
{
- return 640000;
+ case <= 2:
+ return 192000;
+ case >= 5:
+ return 640000;
}
}
if (string.Equals(codec, "flac", StringComparison.OrdinalIgnoreCase)
|| string.Equals(codec, "alac", StringComparison.OrdinalIgnoreCase))
{
- if (channelsValue <= 2)
+ switch (channelsValue)
{
- return 960000;
- }
-
- if (channelsValue >= 5)
- {
- return 2880000;
+ case <= 2:
+ return 960000;
+ case >= 5:
+ return 2880000;
}
}
@@ -854,7 +811,7 @@ namespace MediaBrowser.MediaEncoding.Probing
|| string.Equals(streamInfo.CodecType, "video", StringComparison.OrdinalIgnoreCase)))
{
var bps = GetBPSFromTags(streamInfo);
- if (bps != null && bps > 0)
+ if (bps > 0)
{
stream.BitRate = bps;
}
@@ -923,6 +880,7 @@ namespace MediaBrowser.MediaEncoding.Probing
}
tags.TryGetValue(key, out var val);
+
return val;
}
@@ -930,7 +888,7 @@ namespace MediaBrowser.MediaEncoding.Probing
{
if (string.IsNullOrEmpty(input))
{
- return input;
+ return null;
}
return input.Split('(').FirstOrDefault();
@@ -1018,64 +976,64 @@ namespace MediaBrowser.MediaEncoding.Probing
/// <returns>System.Nullable{System.Single}.</returns>
private float? GetFrameRate(string value)
{
- if (!string.IsNullOrEmpty(value))
+ if (string.IsNullOrEmpty(value))
{
- var parts = value.Split('/');
+ return null;
+ }
- float result;
+ var parts = value.Split('/');
- if (parts.Length == 2)
- {
- result = float.Parse(parts[0], _usCulture) / float.Parse(parts[1], _usCulture);
- }
- else
- {
- result = float.Parse(parts[0], _usCulture);
- }
+ float result;
- return float.IsNaN(result) ? (float?)null : result;
+ if (parts.Length == 2)
+ {
+ result = float.Parse(parts[0], _usCulture) / float.Parse(parts[1], _usCulture);
+ }
+ else
+ {
+ result = float.Parse(parts[0], _usCulture);
}
- return null;
+ return float.IsNaN(result) ? null : result;
}
private void SetAudioRuntimeTicks(InternalMediaInfoResult result, MediaInfo data)
{
- if (result.Streams != null)
+ // Get the first info stream
+ var stream = result.Streams?.FirstOrDefault(s => string.Equals(s.CodecType, "audio", StringComparison.OrdinalIgnoreCase));
+ if (stream == null)
{
- // Get the first info stream
- var stream = result.Streams.FirstOrDefault(s => string.Equals(s.CodecType, "audio", StringComparison.OrdinalIgnoreCase));
+ return;
+ }
- if (stream != null)
- {
- // Get duration from stream properties
- var duration = stream.Duration;
+ // Get duration from stream properties
+ var duration = stream.Duration;
- // If it's not there go into format properties
- if (string.IsNullOrEmpty(duration))
- {
- duration = result.Format.Duration;
- }
+ // If it's not there go into format properties
+ if (string.IsNullOrEmpty(duration))
+ {
+ duration = result.Format.Duration;
+ }
- // If we got something, parse it
- if (!string.IsNullOrEmpty(duration))
- {
- data.RunTimeTicks = TimeSpan.FromSeconds(double.Parse(duration, _usCulture)).Ticks;
- }
- }
+ // If we got something, parse it
+ if (!string.IsNullOrEmpty(duration))
+ {
+ data.RunTimeTicks = TimeSpan.FromSeconds(double.Parse(duration, _usCulture)).Ticks;
}
}
private int? GetBPSFromTags(MediaStreamInfo streamInfo)
{
- if (streamInfo != null && streamInfo.Tags != null)
+ if (streamInfo?.Tags == null)
{
- var bps = GetDictionaryValue(streamInfo.Tags, "BPS-eng") ?? GetDictionaryValue(streamInfo.Tags, "BPS");
- if (!string.IsNullOrEmpty(bps)
- && int.TryParse(bps, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedBps))
- {
- return parsedBps;
- }
+ return null;
+ }
+
+ var bps = GetDictionaryValue(streamInfo.Tags, "BPS-eng") ?? GetDictionaryValue(streamInfo.Tags, "BPS");
+ if (!string.IsNullOrEmpty(bps)
+ && int.TryParse(bps, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedBps))
+ {
+ return parsedBps;
}
return null;
@@ -1083,13 +1041,15 @@ namespace MediaBrowser.MediaEncoding.Probing
private double? GetRuntimeSecondsFromTags(MediaStreamInfo streamInfo)
{
- if (streamInfo != null && streamInfo.Tags != null)
+ if (streamInfo?.Tags == null)
{
- var duration = GetDictionaryValue(streamInfo.Tags, "DURATION-eng") ?? GetDictionaryValue(streamInfo.Tags, "DURATION");
- if (!string.IsNullOrEmpty(duration) && TimeSpan.TryParse(duration, out var parsedDuration))
- {
- return parsedDuration.TotalSeconds;
- }
+ return null;
+ }
+
+ var duration = GetDictionaryValue(streamInfo.Tags, "DURATION-eng") ?? GetDictionaryValue(streamInfo.Tags, "DURATION");
+ if (!string.IsNullOrEmpty(duration) && TimeSpan.TryParse(duration, out var parsedDuration))
+ {
+ return parsedDuration.TotalSeconds;
}
return null;
@@ -1097,14 +1057,17 @@ namespace MediaBrowser.MediaEncoding.Probing
private long? GetNumberOfBytesFromTags(MediaStreamInfo streamInfo)
{
- if (streamInfo != null && streamInfo.Tags != null)
+ if (streamInfo?.Tags == null)
{
- var numberOfBytes = GetDictionaryValue(streamInfo.Tags, "NUMBER_OF_BYTES-eng") ?? GetDictionaryValue(streamInfo.Tags, "NUMBER_OF_BYTES");
- if (!string.IsNullOrEmpty(numberOfBytes)
- && long.TryParse(numberOfBytes, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedBytes))
- {
- return parsedBytes;
- }
+ return null;
+ }
+
+ var numberOfBytes = GetDictionaryValue(streamInfo.Tags, "NUMBER_OF_BYTES-eng")
+ ?? GetDictionaryValue(streamInfo.Tags, "NUMBER_OF_BYTES");
+ if (!string.IsNullOrEmpty(numberOfBytes)
+ && long.TryParse(numberOfBytes, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedBytes))
+ {
+ return parsedBytes;
}
return null;
@@ -1112,24 +1075,18 @@ namespace MediaBrowser.MediaEncoding.Probing
private void SetSize(InternalMediaInfoResult data, MediaInfo info)
{
- if (data.Format != null)
+ if (data.Format == null)
{
- if (!string.IsNullOrEmpty(data.Format.Size))
- {
- info.Size = long.Parse(data.Format.Size, _usCulture);
- }
- else
- {
- info.Size = null;
- }
+ return;
}
+
+ info.Size = string.IsNullOrEmpty(data.Format.Size) ? null : long.Parse(data.Format.Size, _usCulture);
}
- private void SetAudioInfoFromTags(MediaInfo audio, Dictionary<string, string> tags)
+ private void SetAudioInfoFromTags(MediaInfo audio, IReadOnlyDictionary<string, string> tags)
{
var people = new List<BaseItemPerson>();
- var composer = FFProbeHelpers.GetDictionaryValue(tags, "composer");
- if (!string.IsNullOrWhiteSpace(composer))
+ if (tags.TryGetValue("composer", out var composer) && !string.IsNullOrWhiteSpace(composer))
{
foreach (var person in Split(composer, false))
{
@@ -1137,8 +1094,7 @@ namespace MediaBrowser.MediaEncoding.Probing
}
}
- var conductor = FFProbeHelpers.GetDictionaryValue(tags, "conductor");
- if (!string.IsNullOrWhiteSpace(conductor))
+ if (tags.TryGetValue("conductor", out var conductor) && !string.IsNullOrWhiteSpace(conductor))
{
foreach (var person in Split(conductor, false))
{
@@ -1146,8 +1102,7 @@ namespace MediaBrowser.MediaEncoding.Probing
}
}
- var lyricist = FFProbeHelpers.GetDictionaryValue(tags, "lyricist");
- if (!string.IsNullOrWhiteSpace(lyricist))
+ if (tags.TryGetValue("lyricist", out var lyricist) && !string.IsNullOrWhiteSpace(lyricist))
{
foreach (var person in Split(lyricist, false))
{
@@ -1156,8 +1111,7 @@ namespace MediaBrowser.MediaEncoding.Probing
}
// Check for writer some music is tagged that way as alternative to composer/lyricist
- var writer = FFProbeHelpers.GetDictionaryValue(tags, "writer");
- if (!string.IsNullOrWhiteSpace(writer))
+ if (tags.TryGetValue("writer", out var writer) && !string.IsNullOrWhiteSpace(writer))
{
foreach (var person in Split(writer, false))
{
@@ -1167,38 +1121,23 @@ namespace MediaBrowser.MediaEncoding.Probing
audio.People = people.ToArray();
- var albumArtist = FFProbeHelpers.GetDictionaryValue(tags, "albumartist");
- if (string.IsNullOrWhiteSpace(albumArtist))
- {
- albumArtist = FFProbeHelpers.GetDictionaryValue(tags, "album artist");
- }
-
- if (string.IsNullOrWhiteSpace(albumArtist))
- {
- albumArtist = FFProbeHelpers.GetDictionaryValue(tags, "album_artist");
- }
-
- if (string.IsNullOrWhiteSpace(albumArtist))
- {
- audio.AlbumArtists = Array.Empty<string>();
- }
- else
- {
- audio.AlbumArtists = SplitArtists(albumArtist, _nameDelimiters, true)
- .DistinctNames()
- .ToArray();
- }
+ // Set album artist
+ var albumArtist = tags.GetFirstNotNullNorWhiteSpaceValue("albumartist", "album artist", "album_artist");
+ audio.AlbumArtists = albumArtist != null
+ ? SplitDistinctArtists(albumArtist, _nameDelimiters, true).ToArray()
+ : Array.Empty<string>();
+ // Set album artist to artist if empty
if (audio.AlbumArtists.Length == 0)
{
audio.AlbumArtists = audio.Artists;
}
// Track number
- audio.IndexNumber = GetDictionaryDiscValue(tags, "track");
+ audio.IndexNumber = GetDictionaryTrackOrDiscNumber(tags, "track");
// Disc number
- audio.ParentIndexNumber = GetDictionaryDiscValue(tags, "disc");
+ audio.ParentIndexNumber = GetDictionaryTrackOrDiscNumber(tags, "disc");
// There's several values in tags may or may not be present
FetchStudios(audio, tags, "organization");
@@ -1206,30 +1145,25 @@ namespace MediaBrowser.MediaEncoding.Probing
FetchStudios(audio, tags, "publisher");
FetchStudios(audio, tags, "label");
- // These support mulitple values, but for now we only store the first.
- var mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Album Artist Id"))
- ?? GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ALBUMARTISTID"));
-
+ // These support multiple values, but for now we only store the first.
+ var mb = GetMultipleMusicBrainzId(tags.GetValueOrDefault("MusicBrainz Album Artist Id"))
+ ?? GetMultipleMusicBrainzId(tags.GetValueOrDefault("MUSICBRAINZ_ALBUMARTISTID"));
audio.SetProviderId(MetadataProvider.MusicBrainzAlbumArtist, mb);
- mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Artist Id"))
- ?? GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ARTISTID"));
-
+ mb = GetMultipleMusicBrainzId(tags.GetValueOrDefault("MusicBrainz Artist Id"))
+ ?? GetMultipleMusicBrainzId(tags.GetValueOrDefault("MUSICBRAINZ_ARTISTID"));
audio.SetProviderId(MetadataProvider.MusicBrainzArtist, mb);
- mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Album Id"))
- ?? GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ALBUMID"));
-
+ mb = GetMultipleMusicBrainzId(tags.GetValueOrDefault("MusicBrainz Album Id"))
+ ?? GetMultipleMusicBrainzId(tags.GetValueOrDefault("MUSICBRAINZ_ALBUMID"));
audio.SetProviderId(MetadataProvider.MusicBrainzAlbum, mb);
- mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Release Group Id"))
- ?? GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_RELEASEGROUPID"));
-
+ mb = GetMultipleMusicBrainzId(tags.GetValueOrDefault("MusicBrainz Release Group Id"))
+ ?? GetMultipleMusicBrainzId(tags.GetValueOrDefault("MUSICBRAINZ_RELEASEGROUPID"));
audio.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, mb);
- mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Release Track Id"))
- ?? GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_RELEASETRACKID"));
-
+ mb = GetMultipleMusicBrainzId(tags.GetValueOrDefault("MusicBrainz Release Track Id"))
+ ?? GetMultipleMusicBrainzId(tags.GetValueOrDefault("MUSICBRAINZ_RELEASETRACKID"));
audio.SetProviderId(MetadataProvider.MusicBrainzTrack, mb);
}
@@ -1253,18 +1187,18 @@ namespace MediaBrowser.MediaEncoding.Probing
/// <returns>System.String[][].</returns>
private IEnumerable<string> Split(string val, bool allowCommaDelimiter)
{
- // Only use the comma as a delimeter if there are no slashes or pipes.
+ // Only use the comma as a delimiter if there are no slashes or pipes.
// We want to be careful not to split names that have commas in them
- var delimeter = !allowCommaDelimiter || _nameDelimiters.Any(i => val.IndexOf(i, StringComparison.Ordinal) != -1) ?
+ var delimiter = !allowCommaDelimiter || _nameDelimiters.Any(i => val.IndexOf(i, StringComparison.Ordinal) != -1) ?
_nameDelimiters :
new[] { ',' };
- return val.Split(delimeter, StringSplitOptions.RemoveEmptyEntries)
+ return val.Split(delimiter, StringSplitOptions.RemoveEmptyEntries)
.Where(i => !string.IsNullOrWhiteSpace(i))
.Select(i => i.Trim());
}
- private IEnumerable<string> SplitArtists(string val, char[] delimiters, bool splitFeaturing)
+ private IEnumerable<string> SplitDistinctArtists(string val, char[] delimiters, bool splitFeaturing)
{
if (splitFeaturing)
{
@@ -1290,7 +1224,7 @@ namespace MediaBrowser.MediaEncoding.Probing
.Select(i => i.Trim());
artistsFound.AddRange(artists);
- return artistsFound;
+ return artistsFound.DistinctNames();
}
/// <summary>
@@ -1299,36 +1233,38 @@ namespace MediaBrowser.MediaEncoding.Probing
/// <param name="info">The info.</param>
/// <param name="tags">The tags.</param>
/// <param name="tagName">Name of the tag.</param>
- private void FetchStudios(MediaInfo info, Dictionary<string, string> tags, string tagName)
+ private void FetchStudios(MediaInfo info, IReadOnlyDictionary<string, string> tags, string tagName)
{
- var val = FFProbeHelpers.GetDictionaryValue(tags, tagName);
+ var val = tags.GetValueOrDefault(tagName);
- if (!string.IsNullOrEmpty(val))
+ if (string.IsNullOrEmpty(val))
{
- var studios = Split(val, true);
- var studioList = new List<string>();
+ return;
+ }
- foreach (var studio in studios)
- {
- // Sometimes the artist name is listed here, account for that
- if (info.Artists.Contains(studio, StringComparer.OrdinalIgnoreCase))
- {
- continue;
- }
+ var studios = Split(val, true);
+ var studioList = new List<string>();
- if (info.AlbumArtists.Contains(studio, StringComparer.OrdinalIgnoreCase))
- {
- continue;
- }
+ foreach (var studio in studios)
+ {
+ if (string.IsNullOrWhiteSpace(studio))
+ {
+ continue;
+ }
- studioList.Add(studio);
+ // Don't add artist/album artist name to studios, even if it's listed there
+ if (info.Artists.Contains(studio, StringComparer.OrdinalIgnoreCase)
+ || info.AlbumArtists.Contains(studio, StringComparer.OrdinalIgnoreCase))
+ {
+ continue;
}
- info.Studios = studioList
- .Where(i => !string.IsNullOrWhiteSpace(i))
- .Distinct(StringComparer.OrdinalIgnoreCase)
- .ToArray();
+ studioList.Add(studio);
}
+
+ info.Studios = studioList
+ .Distinct(StringComparer.OrdinalIgnoreCase)
+ .ToArray();
}
/// <summary>
@@ -1336,58 +1272,55 @@ namespace MediaBrowser.MediaEncoding.Probing
/// </summary>
/// <param name="info">The information.</param>
/// <param name="tags">The tags.</param>
- private void FetchGenres(MediaInfo info, Dictionary<string, string> tags)
+ private void FetchGenres(MediaInfo info, IReadOnlyDictionary<string, string> tags)
{
- var val = FFProbeHelpers.GetDictionaryValue(tags, "genre");
+ var genreVal = tags.GetValueOrDefault("genre");
+ if (string.IsNullOrEmpty(genreVal))
+ {
+ return;
+ }
- if (!string.IsNullOrEmpty(val))
+ var genres = new List<string>(info.Genres);
+ foreach (var genre in Split(genreVal, true))
{
- var genres = new List<string>(info.Genres);
- foreach (var genre in Split(val, true))
+ if (string.IsNullOrWhiteSpace(genre))
{
- genres.Add(genre);
+ continue;
}
- info.Genres = genres
- .Where(i => !string.IsNullOrWhiteSpace(i))
- .Distinct(StringComparer.OrdinalIgnoreCase)
- .ToArray();
+ genres.Add(genre);
}
+
+ info.Genres = genres
+ .Distinct(StringComparer.OrdinalIgnoreCase)
+ .ToArray();
}
/// <summary>
- /// Gets the disc number, which is sometimes can be in the form of '1', or '1/3'.
+ /// Gets the track or disc number, which can be in the form of '1', or '1/3'.
/// </summary>
/// <param name="tags">The tags.</param>
/// <param name="tagName">Name of the tag.</param>
- /// <returns>System.Nullable{System.Int32}.</returns>
- private int? GetDictionaryDiscValue(Dictionary<string, string> tags, string tagName)
+ /// <returns>The track or disc number, or null, if missing or not parseable.</returns>
+ private static int? GetDictionaryTrackOrDiscNumber(IReadOnlyDictionary<string, string> tags, string tagName)
{
- var disc = FFProbeHelpers.GetDictionaryValue(tags, tagName);
+ var disc = tags.GetValueOrDefault(tagName);
- if (!string.IsNullOrEmpty(disc))
+ if (!string.IsNullOrEmpty(disc) && int.TryParse(disc.Split('/')[0], out var discNum))
{
- disc = disc.Split('/')[0];
-
- if (int.TryParse(disc, out var num))
- {
- return num;
- }
+ return discNum;
}
return null;
}
- private ChapterInfo GetChapterInfo(MediaChapter chapter)
+ private static ChapterInfo GetChapterInfo(MediaChapter chapter)
{
var info = new ChapterInfo();
- if (chapter.Tags != null)
+ if (chapter.Tags != null && chapter.Tags.TryGetValue("title", out string name))
{
- if (chapter.Tags.TryGetValue("title", out string name))
- {
- info.Name = name;
- }
+ info.Name = name;
}
// Limit accuracy to milliseconds to match xml saving
@@ -1404,14 +1337,14 @@ namespace MediaBrowser.MediaEncoding.Probing
private void FetchWtvInfo(MediaInfo video, InternalMediaInfoResult data)
{
- if (data.Format == null || data.Format.Tags == null)
+ var tags = data.Format?.Tags;
+
+ if (tags == null)
{
return;
}
- var genres = FFProbeHelpers.GetDictionaryValue(data.Format.Tags, "WM/Genre");
-
- if (!string.IsNullOrWhiteSpace(genres))
+ if (tags.TryGetValue("WM/Genre", out var genres) && !string.IsNullOrWhiteSpace(genres))
{
var genreList = genres.Split(new[] { ';', '/', ',' }, StringSplitOptions.RemoveEmptyEntries)
.Where(i => !string.IsNullOrWhiteSpace(i))
@@ -1425,16 +1358,12 @@ namespace MediaBrowser.MediaEncoding.Probing
}
}
- var officialRating = FFProbeHelpers.GetDictionaryValue(data.Format.Tags, "WM/ParentalRating");
-
- if (!string.IsNullOrWhiteSpace(officialRating))
+ if (tags.TryGetValue("WM/ParentalRating", out var officialRating) && !string.IsNullOrWhiteSpace(officialRating))
{
video.OfficialRating = officialRating;
}
- var people = FFProbeHelpers.GetDictionaryValue(data.Format.Tags, "WM/MediaCredits");
-
- if (!string.IsNullOrEmpty(people))
+ if (tags.TryGetValue("WM/MediaCredits", out var people) && !string.IsNullOrEmpty(people))
{
video.People = people.Split(new[] { ';', '/' }, StringSplitOptions.RemoveEmptyEntries)
.Where(i => !string.IsNullOrWhiteSpace(i))
@@ -1442,29 +1371,21 @@ namespace MediaBrowser.MediaEncoding.Probing
.ToArray();
}
- var year = FFProbeHelpers.GetDictionaryValue(data.Format.Tags, "WM/OriginalReleaseTime");
- if (!string.IsNullOrWhiteSpace(year))
+ if (tags.TryGetValue("WM/OriginalReleaseTime", out var year) && int.TryParse(year, NumberStyles.Integer, _usCulture, out var parsedYear))
{
- if (int.TryParse(year, NumberStyles.Integer, _usCulture, out var val))
- {
- video.ProductionYear = val;
- }
+ video.ProductionYear = parsedYear;
}
- var premiereDateString = FFProbeHelpers.GetDictionaryValue(data.Format.Tags, "WM/MediaOriginalBroadcastDateTime");
- if (!string.IsNullOrWhiteSpace(premiereDateString))
+ // Credit to MCEBuddy: https://mcebuddy2x.codeplex.com/
+ // DateTime is reported along with timezone info (typically Z i.e. UTC hence assume None)
+ if (tags.TryGetValue("WM/MediaOriginalBroadcastDateTime", out var premiereDateString) && DateTime.TryParse(year, null, DateTimeStyles.None, out var parsedDate))
{
- // Credit to MCEBuddy: https://mcebuddy2x.codeplex.com/
- // DateTime is reported along with timezone info (typically Z i.e. UTC hence assume None)
- if (DateTime.TryParse(year, null, DateTimeStyles.None, out var val))
- {
- video.PremiereDate = val.ToUniversalTime();
- }
+ video.PremiereDate = parsedDate.ToUniversalTime();
}
- var description = FFProbeHelpers.GetDictionaryValue(data.Format.Tags, "WM/SubTitleDescription");
+ var description = tags.GetValueOrDefault("WM/SubTitleDescription");
- var subTitle = FFProbeHelpers.GetDictionaryValue(data.Format.Tags, "WM/SubTitle");
+ var subTitle = tags.GetValueOrDefault("WM/SubTitle");
// For below code, credit to MCEBuddy: https://mcebuddy2x.codeplex.com/
@@ -1475,49 +1396,48 @@ namespace MediaBrowser.MediaEncoding.Probing
// e.g. -> CBeebies Bedtime Hour. The Mystery: Animated adventures of two friends who live on an island in the middle of the big city. Some of Abney and Teal's favourite objects are missing. [S]
if (string.IsNullOrWhiteSpace(subTitle)
&& !string.IsNullOrWhiteSpace(description)
- && description.AsSpan().Slice(0, Math.Min(description.Length, MaxSubtitleDescriptionExtractionLength)).IndexOf(':') != -1) // Check within the Subtitle size limit, otherwise from description it can get too long creating an invalid filename
+ && description.AsSpan()[0..Math.Min(description.Length, MaxSubtitleDescriptionExtractionLength)].IndexOf(':') != -1) // Check within the Subtitle size limit, otherwise from description it can get too long creating an invalid filename
{
- string[] parts = description.Split(':');
- if (parts.Length > 0)
+ string[] descriptionParts = description.Split(':');
+ if (descriptionParts.Length > 0)
{
- string subtitle = parts[0];
+ string subtitle = descriptionParts[0];
try
{
- if (subtitle.Contains('/', StringComparison.Ordinal)) // It contains a episode number and season number
+ // Check if it contains a episode number and season number
+ if (subtitle.Contains('/', StringComparison.Ordinal))
{
- string[] numbers = subtitle.Split(' ');
- video.IndexNumber = int.Parse(numbers[0].Replace(".", string.Empty, StringComparison.Ordinal).Split('/')[0], CultureInfo.InvariantCulture);
- int totalEpisodesInSeason = int.Parse(numbers[0].Replace(".", string.Empty, StringComparison.Ordinal).Split('/')[1], CultureInfo.InvariantCulture);
+ string[] subtitleParts = subtitle.Split(' ');
+ string[] numbers = subtitleParts[0].Replace(".", string.Empty, StringComparison.Ordinal).Split('/');
+ video.IndexNumber = int.Parse(numbers[0], CultureInfo.InvariantCulture);
+ // int totalEpisodesInSeason = int.Parse(numbers[1], CultureInfo.InvariantCulture);
- description = string.Join(' ', numbers, 1, numbers.Length - 1).Trim(); // Skip the first, concatenate the rest, clean up spaces and save it
+ // Skip the numbers, concatenate the rest, trim and set as new description
+ description = string.Join(' ', subtitleParts, 1, subtitleParts.Length - 1).Trim();
+ }
+ else if (subtitle.Contains('.', StringComparison.Ordinal))
+ {
+ var subtitleParts = subtitle.Split('.');
+ description = string.Join('.', subtitleParts, 1, subtitleParts.Length - 1).Trim();
}
else
{
- // Switch to default parsing
- if (subtitle.Contains('.', StringComparison.Ordinal))
- {
- // skip the comment, keep the subtitle
- description = string.Join('.', subtitle.Split('.'), 1, subtitle.Split('.').Length - 1).Trim(); // skip the first
- }
- else
- {
- description = subtitle.Trim(); // Clean up whitespaces and save it
- }
+ description = subtitle.Trim();
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Error while parsing subtitle field");
- // Default parsing
+ // Fallback to default parsing
if (subtitle.Contains('.', StringComparison.Ordinal))
{
- // skip the comment, keep the subtitle
- description = string.Join('.', subtitle.Split('.'), 1, subtitle.Split('.').Length - 1).Trim(); // skip the first
+ var subtitleParts = subtitle.Split('.');
+ description = string.Join('.', subtitleParts, 1, subtitleParts.Length - 1).Trim();
}
else
{
- description = subtitle.Trim(); // Clean up whitespaces and save it
+ description = subtitle.Trim();
}
}
}
@@ -1531,24 +1451,27 @@ namespace MediaBrowser.MediaEncoding.Probing
private void ExtractTimestamp(MediaInfo video)
{
- if (video.VideoType == VideoType.VideoFile)
+ if (video.VideoType != VideoType.VideoFile)
{
- if (string.Equals(video.Container, "mpeg2ts", StringComparison.OrdinalIgnoreCase) ||
- string.Equals(video.Container, "m2ts", StringComparison.OrdinalIgnoreCase) ||
- string.Equals(video.Container, "ts", StringComparison.OrdinalIgnoreCase))
- {
- try
- {
- video.Timestamp = GetMpegTimestamp(video.Path);
+ return;
+ }
- _logger.LogDebug("Video has {Timestamp} timestamp", video.Timestamp);
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error extracting timestamp info from {Path}", video.Path);
- video.Timestamp = null;
- }
- }
+ if (!string.Equals(video.Container, "mpeg2ts", StringComparison.OrdinalIgnoreCase)
+ && !string.Equals(video.Container, "m2ts", StringComparison.OrdinalIgnoreCase)
+ && !string.Equals(video.Container, "ts", StringComparison.OrdinalIgnoreCase))
+ {
+ return;
+ }
+
+ try
+ {
+ video.Timestamp = GetMpegTimestamp(video.Path);
+ _logger.LogDebug("Video has {Timestamp} timestamp", video.Timestamp);
+ }
+ catch (Exception ex)
+ {
+ video.Timestamp = null;
+ _logger.LogError(ex, "Error extracting timestamp info from {Path}", video.Path);
}
}
@@ -1567,17 +1490,17 @@ namespace MediaBrowser.MediaEncoding.Probing
return TransportStreamTimestamp.None;
}
- if ((packetBuffer[4] == 71) && (packetBuffer[196] == 71))
+ if ((packetBuffer[4] != 71) || (packetBuffer[196] != 71))
{
- if ((packetBuffer[0] == 0) && (packetBuffer[1] == 0) && (packetBuffer[2] == 0) && (packetBuffer[3] == 0))
- {
- return TransportStreamTimestamp.Zero;
- }
+ return TransportStreamTimestamp.None;
+ }
- return TransportStreamTimestamp.Valid;
+ if ((packetBuffer[0] == 0) && (packetBuffer[1] == 0) && (packetBuffer[2] == 0) && (packetBuffer[3] == 0))
+ {
+ return TransportStreamTimestamp.Zero;
}
- return TransportStreamTimestamp.None;
+ return TransportStreamTimestamp.Valid;
}
}
}
diff --git a/MediaBrowser.Model/System/SystemInfo.cs b/MediaBrowser.Model/System/SystemInfo.cs
index d75ae91c0..e45b2f33a 100644
--- a/MediaBrowser.Model/System/SystemInfo.cs
+++ b/MediaBrowser.Model/System/SystemInfo.cs
@@ -130,6 +130,7 @@ namespace MediaBrowser.Model.System
/// Gets or sets a value indicating whether this instance has update available.
/// </summary>
/// <value><c>true</c> if this instance has update available; otherwise, <c>false</c>.</value>
+ [Obsolete("This should be handled by the package manager")]
public bool HasUpdateAvailable { get; set; }
public FFmpegLocation EncoderLocation { get; set; }
diff --git a/MediaBrowser.Providers/Manager/ItemImageProvider.cs b/MediaBrowser.Providers/Manager/ItemImageProvider.cs
index 966a3d822..416723d49 100644
--- a/MediaBrowser.Providers/Manager/ItemImageProvider.cs
+++ b/MediaBrowser.Providers/Manager/ItemImageProvider.cs
@@ -102,7 +102,7 @@ namespace MediaBrowser.Providers.Manager
{
if (provider is IRemoteImageProvider remoteProvider)
{
- await RefreshFromProvider(item, libraryOptions, remoteProvider, refreshOptions, typeOptions, backdropLimit, screenshotLimit, downloadedImages, result, cancellationToken).ConfigureAwait(false);
+ await RefreshFromProvider(item, remoteProvider, refreshOptions, typeOptions, backdropLimit, screenshotLimit, downloadedImages, result, cancellationToken).ConfigureAwait(false);
continue;
}
@@ -235,7 +235,6 @@ namespace MediaBrowser.Providers.Manager
/// Refreshes from provider.
/// </summary>
/// <param name="item">The item.</param>
- /// <param name="libraryOptions">The library options.</param>
/// <param name="provider">The provider.</param>
/// <param name="refreshOptions">The refresh options.</param>
/// <param name="savedOptions">The saved options.</param>
@@ -247,7 +246,6 @@ namespace MediaBrowser.Providers.Manager
/// <returns>Task.</returns>
private async Task RefreshFromProvider(
BaseItem item,
- LibraryOptions libraryOptions,
IRemoteImageProvider provider,
ImageRefreshOptions refreshOptions,
TypeOptions savedOptions,
@@ -295,7 +293,7 @@ namespace MediaBrowser.Providers.Manager
if (!HasImage(item, imageType) || (refreshOptions.IsReplacingImage(imageType) && !downloadedImages.Contains(imageType)))
{
minWidth = savedOptions.GetMinWidth(imageType);
- var downloaded = await DownloadImage(item, libraryOptions, provider, result, list, minWidth, imageType, cancellationToken).ConfigureAwait(false);
+ var downloaded = await DownloadImage(item, provider, result, list, minWidth, imageType, cancellationToken).ConfigureAwait(false);
if (downloaded)
{
@@ -305,12 +303,12 @@ namespace MediaBrowser.Providers.Manager
}
minWidth = savedOptions.GetMinWidth(ImageType.Backdrop);
- await DownloadBackdrops(item, libraryOptions, ImageType.Backdrop, backdropLimit, provider, result, list, minWidth, cancellationToken).ConfigureAwait(false);
+ await DownloadBackdrops(item, ImageType.Backdrop, backdropLimit, provider, result, list, minWidth, cancellationToken).ConfigureAwait(false);
if (item is IHasScreenshots hasScreenshots)
{
minWidth = savedOptions.GetMinWidth(ImageType.Screenshot);
- await DownloadBackdrops(item, libraryOptions, ImageType.Screenshot, screenshotLimit, provider, result, list, minWidth, cancellationToken).ConfigureAwait(false);
+ await DownloadBackdrops(item, ImageType.Screenshot, screenshotLimit, provider, result, list, minWidth, cancellationToken).ConfigureAwait(false);
}
}
catch (OperationCanceledException)
@@ -360,7 +358,7 @@ namespace MediaBrowser.Providers.Manager
}
}
- public bool MergeImages(BaseItem item, List<LocalImageInfo> images)
+ public bool MergeImages(BaseItem item, IReadOnlyList<LocalImageInfo> images)
{
var changed = false;
@@ -444,12 +442,12 @@ namespace MediaBrowser.Providers.Manager
return null;
}
- private bool UpdateMultiImages(BaseItem item, List<LocalImageInfo> images, ImageType type)
+ private bool UpdateMultiImages(BaseItem item, IReadOnlyList<LocalImageInfo> images, ImageType type)
{
var changed = false;
var newImageFileInfos = images
- .FindAll(i => i.Type == type)
+ .Where(i => i.Type == type)
.Select(i => i.FileInfo)
.ToList();
@@ -463,7 +461,6 @@ namespace MediaBrowser.Providers.Manager
private async Task<bool> DownloadImage(
BaseItem item,
- LibraryOptions libraryOptions,
IRemoteImageProvider provider,
RefreshResult result,
IEnumerable<RemoteImageInfo> images,
@@ -475,7 +472,7 @@ namespace MediaBrowser.Providers.Manager
.Where(i => i.Type == type && !(i.Width.HasValue && i.Width.Value < minWidth))
.ToList();
- if (EnableImageStub(item, libraryOptions) && eligibleImages.Count > 0)
+ if (EnableImageStub(item) && eligibleImages.Count > 0)
{
SaveImageStub(item, type, eligibleImages.Select(i => i.Url));
result.UpdateType |= ItemUpdateType.ImageUpdate;
@@ -519,7 +516,7 @@ namespace MediaBrowser.Providers.Manager
return false;
}
- private bool EnableImageStub(BaseItem item, LibraryOptions libraryOptions)
+ private bool EnableImageStub(BaseItem item)
{
if (item is LiveTvProgram)
{
@@ -563,7 +560,7 @@ namespace MediaBrowser.Providers.Manager
newIndex);
}
- private async Task DownloadBackdrops(BaseItem item, LibraryOptions libraryOptions, ImageType imageType, int limit, IRemoteImageProvider provider, RefreshResult result, IEnumerable<RemoteImageInfo> images, int minWidth, CancellationToken cancellationToken)
+ private async Task DownloadBackdrops(BaseItem item, ImageType imageType, int limit, IRemoteImageProvider provider, RefreshResult result, IEnumerable<RemoteImageInfo> images, int minWidth, CancellationToken cancellationToken)
{
foreach (var image in images.Where(i => i.Type == imageType))
{
@@ -579,7 +576,7 @@ namespace MediaBrowser.Providers.Manager
var url = image.Url;
- if (EnableImageStub(item, libraryOptions))
+ if (EnableImageStub(item))
{
SaveImageStub(item, imageType, new[] { url });
result.UpdateType |= ItemUpdateType.ImageUpdate;
diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs
index 827cb69b9..333f47f87 100644
--- a/MediaBrowser.Providers/Manager/MetadataService.cs
+++ b/MediaBrowser.Providers/Manager/MetadataService.cs
@@ -617,7 +617,7 @@ namespace MediaBrowser.Providers.Manager
MetadataResult<TItemType> metadata,
TIdType id,
MetadataRefreshOptions options,
- List<IMetadataProvider> providers,
+ ICollection<IMetadataProvider> providers,
ItemImageProvider imageService,
CancellationToken cancellationToken)
{
diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs
index 0cae7768a..7a9379af7 100644
--- a/MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs
+++ b/MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs
@@ -215,18 +215,19 @@ namespace MediaBrowser.Providers.Music
return result;
}
- public async Task<MetadataResult<MusicArtist>> GetMetadata(ArtistInfo id, CancellationToken cancellationToken)
+ /// <inheritdoc />
+ public async Task<MetadataResult<MusicArtist>> GetMetadata(ArtistInfo info, CancellationToken cancellationToken)
{
var result = new MetadataResult<MusicArtist>
{
Item = new MusicArtist()
};
- var musicBrainzId = id.GetMusicBrainzArtistId();
+ var musicBrainzId = info.GetMusicBrainzArtistId();
if (string.IsNullOrWhiteSpace(musicBrainzId))
{
- var searchResults = await GetSearchResults(id, cancellationToken).ConfigureAwait(false);
+ var searchResults = await GetSearchResults(info, cancellationToken).ConfigureAwait(false);
var singleResult = searchResults.FirstOrDefault();
diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs
index 0023d5959..8db3c391e 100644
--- a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs
+++ b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs
@@ -41,7 +41,6 @@ namespace MediaBrowser.Providers.Music
private readonly long _musicBrainzQueryIntervalMs;
private readonly IHttpClientFactory _httpClientFactory;
- private readonly IApplicationHost _appHost;
private readonly ILogger<MusicBrainzAlbumProvider> _logger;
private readonly string _musicBrainzBaseUrl;
@@ -51,11 +50,9 @@ namespace MediaBrowser.Providers.Music
public MusicBrainzAlbumProvider(
IHttpClientFactory httpClientFactory,
- IApplicationHost appHost,
ILogger<MusicBrainzAlbumProvider> logger)
{
_httpClientFactory = httpClientFactory;
- _appHost = appHost;
_logger = logger;
_musicBrainzBaseUrl = Plugin.Instance.Configuration.Server;
@@ -174,10 +171,10 @@ namespace MediaBrowser.Providers.Music
}
/// <inheritdoc />
- public async Task<MetadataResult<MusicAlbum>> GetMetadata(AlbumInfo id, CancellationToken cancellationToken)
+ public async Task<MetadataResult<MusicAlbum>> GetMetadata(AlbumInfo info, CancellationToken cancellationToken)
{
- var releaseId = id.GetReleaseId();
- var releaseGroupId = id.GetReleaseGroupId();
+ var releaseId = info.GetReleaseId();
+ var releaseGroupId = info.GetReleaseGroupId();
var result = new MetadataResult<MusicAlbum>
{
@@ -193,9 +190,9 @@ namespace MediaBrowser.Providers.Music
if (string.IsNullOrWhiteSpace(releaseId))
{
- var artistMusicBrainzId = id.GetMusicBrainzArtistId();
+ var artistMusicBrainzId = info.GetMusicBrainzArtistId();
- var releaseResult = await GetReleaseResult(artistMusicBrainzId, id.GetAlbumArtist(), id.Name, cancellationToken).ConfigureAwait(false);
+ var releaseResult = await GetReleaseResult(artistMusicBrainzId, info.GetAlbumArtist(), info.Name, cancellationToken).ConfigureAwait(false);
if (releaseResult != null)
{
@@ -499,12 +496,11 @@ namespace MediaBrowser.Providers.Music
using var subReader = reader.ReadSubtree();
return ParseArtistNameCredit(subReader);
}
-
default:
- {
- reader.Skip();
- break;
- }
+ {
+ reader.Skip();
+ break;
+ }
}
}
else
diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/Plugin.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/Plugin.cs
index 43bd3a472..9eeb4750b 100644
--- a/MediaBrowser.Providers/Plugins/MusicBrainz/Plugin.cs
+++ b/MediaBrowser.Providers/Plugins/MusicBrainz/Plugin.cs
@@ -11,6 +11,12 @@ namespace MediaBrowser.Providers.Plugins.MusicBrainz
{
public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
{
+ public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer)
+ : base(applicationPaths, xmlSerializer)
+ {
+ Instance = this;
+ }
+
public static Plugin Instance { get; private set; }
public override Guid Id => new Guid("8c95c4d2-e50c-4fb0-a4f3-6c06ff0f9a1a");
@@ -26,12 +32,6 @@ namespace MediaBrowser.Providers.Plugins.MusicBrainz
// TODO remove when plugin removed from server.
public override string ConfigurationFileName => "Jellyfin.Plugin.MusicBrainz.xml";
- public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer)
- : base(applicationPaths, xmlSerializer)
- {
- Instance = this;
- }
-
public IEnumerable<PluginPageInfo> GetPages()
{
yield return new PluginPageInfo
diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs
index 78eea02e0..d9b0600c3 100644
--- a/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs
@@ -9,9 +9,8 @@ using System.Net.Http;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
-using MediaBrowser.Common;
using Jellyfin.Extensions.Json;
-using Jellyfin.Extensions.Json.Converters;
+using MediaBrowser.Common;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs
index 5d9fd36d3..eafcae4ac 100644
--- a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs
@@ -9,9 +9,8 @@ using System.Net.Http;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
-using MediaBrowser.Common;
using Jellyfin.Extensions.Json;
-using Jellyfin.Extensions.Json.Converters;
+using MediaBrowser.Common;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
@@ -213,19 +212,19 @@ namespace MediaBrowser.Providers.Plugins.Omdb
{
var path = await EnsureItemInfo(imdbId, cancellationToken).ConfigureAwait(false);
await using var stream = File.OpenRead(path);
- return await JsonSerializer.DeserializeAsync<RootObject>(stream, _jsonOptions, cancellationToken);
+ return await JsonSerializer.DeserializeAsync<RootObject>(stream, _jsonOptions, cancellationToken).ConfigureAwait(false);
}
internal async Task<SeasonRootObject> GetSeasonRootObject(string imdbId, int seasonId, CancellationToken cancellationToken)
{
var path = await EnsureSeasonInfo(imdbId, seasonId, cancellationToken).ConfigureAwait(false);
await using var stream = File.OpenRead(path);
- return await JsonSerializer.DeserializeAsync<SeasonRootObject>(stream, _jsonOptions, cancellationToken);
+ return await JsonSerializer.DeserializeAsync<SeasonRootObject>(stream, _jsonOptions, cancellationToken).ConfigureAwait(false);
}
internal static bool IsValidSeries(Dictionary<string, string> seriesProviderIds)
{
- if (seriesProviderIds.TryGetValue(MetadataProvider.Imdb.ToString(), out string id) && !string.IsNullOrEmpty(id))
+ if (seriesProviderIds.TryGetValue(MetadataProvider.Imdb.ToString(), out string id))
{
// This check should ideally never be necessary but we're seeing some cases of this and haven't tracked them down yet.
if (!string.IsNullOrWhiteSpace(id))
diff --git a/MediaBrowser.Providers/Plugins/Omdb/Plugin.cs b/MediaBrowser.Providers/Plugins/Omdb/Plugin.cs
index d7f6781e5..047df4f33 100644
--- a/MediaBrowser.Providers/Plugins/Omdb/Plugin.cs
+++ b/MediaBrowser.Providers/Plugins/Omdb/Plugin.cs
@@ -11,6 +11,12 @@ namespace MediaBrowser.Providers.Plugins.Omdb
{
public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
{
+ public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer)
+ : base(applicationPaths, xmlSerializer)
+ {
+ Instance = this;
+ }
+
public static Plugin Instance { get; private set; }
public override Guid Id => new Guid("a628c0da-fac5-4c7e-9d1a-7134223f14c8");
@@ -22,12 +28,6 @@ namespace MediaBrowser.Providers.Plugins.Omdb
// TODO remove when plugin removed from server.
public override string ConfigurationFileName => "Jellyfin.Plugin.Omdb.xml";
- public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer)
- : base(applicationPaths, xmlSerializer)
- {
- Instance = this;
- }
-
public IEnumerable<PluginPageInfo> GetPages()
{
yield return new PluginPageInfo
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs
index ca1af6c49..5dd1f0b73 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs
@@ -79,16 +79,16 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets
return collections;
}
- public async Task<MetadataResult<BoxSet>> GetMetadata(BoxSetInfo id, CancellationToken cancellationToken)
+ public async Task<MetadataResult<BoxSet>> GetMetadata(BoxSetInfo info, CancellationToken cancellationToken)
{
- var tmdbId = Convert.ToInt32(id.GetProviderId(MetadataProvider.Tmdb), CultureInfo.InvariantCulture);
- var language = id.MetadataLanguage;
+ var tmdbId = Convert.ToInt32(info.GetProviderId(MetadataProvider.Tmdb), CultureInfo.InvariantCulture);
+ var language = info.MetadataLanguage;
// We don't already have an Id, need to fetch it
if (tmdbId <= 0)
{
// ParseName is required here.
// Caller provides the filename with extension stripped and NOT the parsed filename
- var parsedName = _libraryManager.ParseName(id.Name);
+ var parsedName = _libraryManager.ParseName(info.Name);
var cleanedName = TmdbUtils.CleanName(parsedName.Name);
var searchResults = await _tmdbClientManager.SearchCollectionAsync(cleanedName, language, cancellationToken).ConfigureAwait(false);
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs
index 4a0884c07..54f8d450a 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs
@@ -154,7 +154,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
var movieResultFromImdbId = await _tmdbClientManager.FindByExternalIdAsync(imdbId, FindExternalSource.Imdb, info.MetadataLanguage, cancellationToken).ConfigureAwait(false);
if (movieResultFromImdbId?.MovieResults.Count > 0)
{
- tmdbId = movieResultFromImdbId.MovieResults[0].Id.ToString();
+ tmdbId = movieResultFromImdbId.MovieResults[0].Id.ToString(CultureInfo.InvariantCulture);
}
}
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs
index 4c1f69763..66e30115d 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs
@@ -55,7 +55,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
result.Item = new Season
{
IndexNumber = seasonNumber,
- Overview = seasonResult?.Overview
+ Overview = seasonResult.Overview
};
if (!string.IsNullOrEmpty(seasonResult.ExternalIds?.TvdbId))
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs b/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs
index 79ec6139d..3980b7da0 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs
@@ -242,7 +242,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb
await EnsureClientConfigAsync().ConfigureAwait(false);
- var group = await GetSeriesGroupAsync(tvShowId, displayOrder, language, imageLanguages, cancellationToken);
+ var group = await GetSeriesGroupAsync(tvShowId, displayOrder, language, imageLanguages, cancellationToken).ConfigureAwait(false);
if (group != null)
{
var season = group.Groups.Find(s => s.Order == seasonNumber);
diff --git a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs
index 6aacaa15d..13f15b173 100644
--- a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs
+++ b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs
@@ -370,15 +370,15 @@ namespace MediaBrowser.Providers.Subtitles
}
/// <inheritdoc />
- public SubtitleProviderInfo[] GetSupportedProviders(BaseItem video)
+ public SubtitleProviderInfo[] GetSupportedProviders(BaseItem item)
{
VideoContentType mediaType;
- if (video is Episode)
+ if (item is Episode)
{
mediaType = VideoContentType.Episode;
}
- else if (video is Movie)
+ else if (item is Movie)
{
mediaType = VideoContentType.Movie;
}
diff --git a/deployment/Dockerfile.debian.amd64 b/deployment/Dockerfile.debian.amd64
index 4c426b6d5..67a5c9c99 100644
--- a/deployment/Dockerfile.debian.amd64
+++ b/deployment/Dockerfile.debian.amd64
@@ -16,7 +16,7 @@ RUN apt-get update \
# Install dotnet repository
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
-RUN wget https://download.visualstudio.microsoft.com/download/pr/e1c236ec-c392-4eaa-a846-c600c82bb7f6/b13bd8b69f875f87cf83fc6f5457bcdf/dotnet-sdk-5.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/8468e541-a99a-4191-8470-654fa0747a9a/cb32548d2fd3d60ef3fe8fc80cd735ef/dotnet-sdk-5.0.302-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
&& mkdir -p dotnet-sdk \
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
diff --git a/deployment/Dockerfile.debian.arm64 b/deployment/Dockerfile.debian.arm64
index 7ed6d52bc..c341068f6 100644
--- a/deployment/Dockerfile.debian.arm64
+++ b/deployment/Dockerfile.debian.arm64
@@ -16,7 +16,7 @@ RUN apt-get update \
# Install dotnet repository
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
-RUN wget https://download.visualstudio.microsoft.com/download/pr/e1c236ec-c392-4eaa-a846-c600c82bb7f6/b13bd8b69f875f87cf83fc6f5457bcdf/dotnet-sdk-5.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/8468e541-a99a-4191-8470-654fa0747a9a/cb32548d2fd3d60ef3fe8fc80cd735ef/dotnet-sdk-5.0.302-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
&& mkdir -p dotnet-sdk \
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
diff --git a/deployment/Dockerfile.debian.armhf b/deployment/Dockerfile.debian.armhf
index b46cceaa4..19be363b6 100644
--- a/deployment/Dockerfile.debian.armhf
+++ b/deployment/Dockerfile.debian.armhf
@@ -16,7 +16,7 @@ RUN apt-get update \
# Install dotnet repository
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
-RUN wget https://download.visualstudio.microsoft.com/download/pr/e1c236ec-c392-4eaa-a846-c600c82bb7f6/b13bd8b69f875f87cf83fc6f5457bcdf/dotnet-sdk-5.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/8468e541-a99a-4191-8470-654fa0747a9a/cb32548d2fd3d60ef3fe8fc80cd735ef/dotnet-sdk-5.0.302-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
&& mkdir -p dotnet-sdk \
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
diff --git a/deployment/Dockerfile.linux.amd64 b/deployment/Dockerfile.linux.amd64
index a0e23557a..a89fe9289 100644
--- a/deployment/Dockerfile.linux.amd64
+++ b/deployment/Dockerfile.linux.amd64
@@ -16,7 +16,7 @@ RUN apt-get update \
# Install dotnet repository
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
-RUN wget https://download.visualstudio.microsoft.com/download/pr/e1c236ec-c392-4eaa-a846-c600c82bb7f6/b13bd8b69f875f87cf83fc6f5457bcdf/dotnet-sdk-5.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/8468e541-a99a-4191-8470-654fa0747a9a/cb32548d2fd3d60ef3fe8fc80cd735ef/dotnet-sdk-5.0.302-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
&& mkdir -p dotnet-sdk \
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
diff --git a/deployment/Dockerfile.linux.amd64-musl b/deployment/Dockerfile.linux.amd64-musl
index af0f55f8e..f7fb722f2 100644
--- a/deployment/Dockerfile.linux.amd64-musl
+++ b/deployment/Dockerfile.linux.amd64-musl
@@ -16,7 +16,7 @@ RUN apt-get update \
# Install dotnet repository
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
-RUN wget https://download.visualstudio.microsoft.com/download/pr/e1c236ec-c392-4eaa-a846-c600c82bb7f6/b13bd8b69f875f87cf83fc6f5457bcdf/dotnet-sdk-5.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/8468e541-a99a-4191-8470-654fa0747a9a/cb32548d2fd3d60ef3fe8fc80cd735ef/dotnet-sdk-5.0.302-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
&& mkdir -p dotnet-sdk \
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
diff --git a/deployment/Dockerfile.linux.arm64 b/deployment/Dockerfile.linux.arm64
index ba004bb6a..1b57441a0 100644
--- a/deployment/Dockerfile.linux.arm64
+++ b/deployment/Dockerfile.linux.arm64
@@ -16,7 +16,7 @@ RUN apt-get update \
# Install dotnet repository
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
-RUN wget https://download.visualstudio.microsoft.com/download/pr/e1c236ec-c392-4eaa-a846-c600c82bb7f6/b13bd8b69f875f87cf83fc6f5457bcdf/dotnet-sdk-5.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/8468e541-a99a-4191-8470-654fa0747a9a/cb32548d2fd3d60ef3fe8fc80cd735ef/dotnet-sdk-5.0.302-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
&& mkdir -p dotnet-sdk \
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
diff --git a/deployment/Dockerfile.linux.armhf b/deployment/Dockerfile.linux.armhf
index 0d1114c01..20cf33e13 100644
--- a/deployment/Dockerfile.linux.armhf
+++ b/deployment/Dockerfile.linux.armhf
@@ -16,7 +16,7 @@ RUN apt-get update \
# Install dotnet repository
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
-RUN wget https://download.visualstudio.microsoft.com/download/pr/e1c236ec-c392-4eaa-a846-c600c82bb7f6/b13bd8b69f875f87cf83fc6f5457bcdf/dotnet-sdk-5.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/8468e541-a99a-4191-8470-654fa0747a9a/cb32548d2fd3d60ef3fe8fc80cd735ef/dotnet-sdk-5.0.302-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
&& mkdir -p dotnet-sdk \
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
diff --git a/deployment/Dockerfile.macos b/deployment/Dockerfile.macos
index b57dc53f5..4ddd106bb 100644
--- a/deployment/Dockerfile.macos
+++ b/deployment/Dockerfile.macos
@@ -16,7 +16,7 @@ RUN apt-get update \
# Install dotnet repository
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
-RUN wget https://download.visualstudio.microsoft.com/download/pr/e1c236ec-c392-4eaa-a846-c600c82bb7f6/b13bd8b69f875f87cf83fc6f5457bcdf/dotnet-sdk-5.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/8468e541-a99a-4191-8470-654fa0747a9a/cb32548d2fd3d60ef3fe8fc80cd735ef/dotnet-sdk-5.0.302-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
&& mkdir -p dotnet-sdk \
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
diff --git a/deployment/Dockerfile.portable b/deployment/Dockerfile.portable
index 3783dfacf..e56a480c6 100644
--- a/deployment/Dockerfile.portable
+++ b/deployment/Dockerfile.portable
@@ -15,7 +15,7 @@ RUN apt-get update \
# Install dotnet repository
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
-RUN wget https://download.visualstudio.microsoft.com/download/pr/e1c236ec-c392-4eaa-a846-c600c82bb7f6/b13bd8b69f875f87cf83fc6f5457bcdf/dotnet-sdk-5.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/8468e541-a99a-4191-8470-654fa0747a9a/cb32548d2fd3d60ef3fe8fc80cd735ef/dotnet-sdk-5.0.302-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
&& mkdir -p dotnet-sdk \
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
diff --git a/deployment/Dockerfile.ubuntu.amd64 b/deployment/Dockerfile.ubuntu.amd64
index 663a7af9e..03d4c185c 100644
--- a/deployment/Dockerfile.ubuntu.amd64
+++ b/deployment/Dockerfile.ubuntu.amd64
@@ -16,7 +16,7 @@ RUN apt-get update \
# Install dotnet repository
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
-RUN wget https://download.visualstudio.microsoft.com/download/pr/e1c236ec-c392-4eaa-a846-c600c82bb7f6/b13bd8b69f875f87cf83fc6f5457bcdf/dotnet-sdk-5.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/8468e541-a99a-4191-8470-654fa0747a9a/cb32548d2fd3d60ef3fe8fc80cd735ef/dotnet-sdk-5.0.302-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
&& mkdir -p dotnet-sdk \
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
diff --git a/deployment/Dockerfile.ubuntu.arm64 b/deployment/Dockerfile.ubuntu.arm64
index 83eb24e42..9e0b60e0b 100644
--- a/deployment/Dockerfile.ubuntu.arm64
+++ b/deployment/Dockerfile.ubuntu.arm64
@@ -16,7 +16,7 @@ RUN apt-get update \
# Install dotnet repository
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
-RUN wget https://download.visualstudio.microsoft.com/download/pr/e1c236ec-c392-4eaa-a846-c600c82bb7f6/b13bd8b69f875f87cf83fc6f5457bcdf/dotnet-sdk-5.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/8468e541-a99a-4191-8470-654fa0747a9a/cb32548d2fd3d60ef3fe8fc80cd735ef/dotnet-sdk-5.0.302-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
&& mkdir -p dotnet-sdk \
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
diff --git a/deployment/Dockerfile.ubuntu.armhf b/deployment/Dockerfile.ubuntu.armhf
index 1187f37b9..0392f7b2f 100644
--- a/deployment/Dockerfile.ubuntu.armhf
+++ b/deployment/Dockerfile.ubuntu.armhf
@@ -16,7 +16,7 @@ RUN apt-get update \
# Install dotnet repository
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
-RUN wget https://download.visualstudio.microsoft.com/download/pr/e1c236ec-c392-4eaa-a846-c600c82bb7f6/b13bd8b69f875f87cf83fc6f5457bcdf/dotnet-sdk-5.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/8468e541-a99a-4191-8470-654fa0747a9a/cb32548d2fd3d60ef3fe8fc80cd735ef/dotnet-sdk-5.0.302-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
&& mkdir -p dotnet-sdk \
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
diff --git a/deployment/Dockerfile.windows.amd64 b/deployment/Dockerfile.windows.amd64
index 8b2361f0b..9c78897a4 100644
--- a/deployment/Dockerfile.windows.amd64
+++ b/deployment/Dockerfile.windows.amd64
@@ -15,7 +15,7 @@ RUN apt-get update \
# Install dotnet repository
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
-RUN wget https://download.visualstudio.microsoft.com/download/pr/e1c236ec-c392-4eaa-a846-c600c82bb7f6/b13bd8b69f875f87cf83fc6f5457bcdf/dotnet-sdk-5.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/8468e541-a99a-4191-8470-654fa0747a9a/cb32548d2fd3d60ef3fe8fc80cd735ef/dotnet-sdk-5.0.302-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
&& mkdir -p dotnet-sdk \
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
diff --git a/jellyfin.ruleset b/jellyfin.ruleset
index 44bc34369..a2fc7bc8d 100644
--- a/jellyfin.ruleset
+++ b/jellyfin.ruleset
@@ -43,6 +43,8 @@
or pass in 'CancellationToken.None' explicitly to indicate intentionally not propagating the token -->
<Rule Id="CA2016" Action="Error" />
+ <!-- disable warning CA1014: Mark assemblies with CLSCompliantAttribute -->
+ <Rule Id="CA1014" Action="Info" />
<!-- disable warning CA1024: Use properties where appropriate -->
<Rule Id="CA1024" Action="Info" />
<!-- disable warning CA1031: Do not catch general exception types -->
@@ -68,8 +70,6 @@
<!-- disable warning CA5394: Do not use insecure randomness -->
<Rule Id="CA5394" Action="Info" />
- <!-- disable warning CA1014: Mark assemblies with CLSCompliantAttribute -->
- <Rule Id="CA1014" Action="Info" />
<!-- disable warning CA1054: Change the type of parameter url from string to System.Uri -->
<Rule Id="CA1054" Action="None" />
<!-- disable warning CA1055: URI return values should not be strings -->
@@ -82,5 +82,7 @@
<Rule Id="CA1308" Action="None" />
<!-- disable warning CA2101: Specify marshaling for P/Invoke string arguments -->
<Rule Id="CA2101" Action="None" />
+ <!-- disable warning CA2234: Pass System.Uri objects instead of strings -->
+ <Rule Id="CA2234" Action="None" />
</Rules>
</RuleSet>
diff --git a/src/Jellyfin.Extensions/DictionaryExtensions.cs b/src/Jellyfin.Extensions/DictionaryExtensions.cs
new file mode 100644
index 000000000..43ed41ab1
--- /dev/null
+++ b/src/Jellyfin.Extensions/DictionaryExtensions.cs
@@ -0,0 +1,65 @@
+using System;
+using System.Collections.Generic;
+
+namespace Jellyfin.Extensions
+{
+ /// <summary>
+ /// Static extensions for the <see cref="IReadOnlyDictionary{TKey,TValue}"/> interface.
+ /// </summary>
+ public static class DictionaryExtensions
+ {
+ /// <summary>
+ /// Gets a string from a string dictionary, checking all keys sequentially,
+ /// stopping at the first key that returns a result that's neither null nor blank.
+ /// </summary>
+ /// <param name="dictionary">The dictionary.</param>
+ /// <param name="key1">The first checked key.</param>
+ /// <returns>System.String.</returns>
+ public static string? GetFirstNotNullNorWhiteSpaceValue(this IReadOnlyDictionary<string, string> dictionary, string key1)
+ {
+ return dictionary.GetFirstNotNullNorWhiteSpaceValue(key1, string.Empty, string.Empty);
+ }
+
+ /// <summary>
+ /// Gets a string from a string dictionary, checking all keys sequentially,
+ /// stopping at the first key that returns a result that's neither null nor blank.
+ /// </summary>
+ /// <param name="dictionary">The dictionary.</param>
+ /// <param name="key1">The first checked key.</param>
+ /// <param name="key2">The second checked key.</param>
+ /// <returns>System.String.</returns>
+ public static string? GetFirstNotNullNorWhiteSpaceValue(this IReadOnlyDictionary<string, string> dictionary, string key1, string key2)
+ {
+ return dictionary.GetFirstNotNullNorWhiteSpaceValue(key1, key2, string.Empty);
+ }
+
+ /// <summary>
+ /// Gets a string from a string dictionary, checking all keys sequentially,
+ /// stopping at the first key that returns a result that's neither null nor blank.
+ /// </summary>
+ /// <param name="dictionary">The dictionary.</param>
+ /// <param name="key1">The first checked key.</param>
+ /// <param name="key2">The second checked key.</param>
+ /// <param name="key3">The third checked key.</param>
+ /// <returns>System.String.</returns>
+ public static string? GetFirstNotNullNorWhiteSpaceValue(this IReadOnlyDictionary<string, string> dictionary, string key1, string key2, string key3)
+ {
+ if (dictionary.TryGetValue(key1, out var val) && !string.IsNullOrWhiteSpace(val))
+ {
+ return val;
+ }
+
+ if (!string.IsNullOrEmpty(key2) && dictionary.TryGetValue(key2, out val) && !string.IsNullOrWhiteSpace(val))
+ {
+ return val;
+ }
+
+ if (!string.IsNullOrEmpty(key3) && dictionary.TryGetValue(key3, out val) && !string.IsNullOrWhiteSpace(val))
+ {
+ return val;
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
index d4ea91872..07538b38b 100644
--- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
+++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
@@ -18,7 +18,7 @@
<PackageReference Include="AutoFixture" Version="4.17.0" />
<PackageReference Include="AutoFixture.AutoMoq" Version="4.17.0" />
<PackageReference Include="AutoFixture.Xunit2" Version="4.17.0" />
- <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="5.0.7" />
+ <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="5.0.8" />
<PackageReference Include="Microsoft.Extensions.Options" Version="5.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
<PackageReference Include="xunit" Version="2.4.1" />
diff --git a/tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj b/tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj
index 4b6dca377..f87e63be2 100644
--- a/tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj
+++ b/tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj
@@ -10,7 +10,7 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
+ <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
diff --git a/tests/Jellyfin.MediaEncoding.Tests/FFprobeParserTests.cs b/tests/Jellyfin.MediaEncoding.Tests/FFprobeParserTests.cs
index 45808375f..2955104a2 100644
--- a/tests/Jellyfin.MediaEncoding.Tests/FFprobeParserTests.cs
+++ b/tests/Jellyfin.MediaEncoding.Tests/FFprobeParserTests.cs
@@ -14,9 +14,10 @@ namespace Jellyfin.MediaEncoding.Tests
public async Task Test(string fileName)
{
var path = Path.Join("Test Data", fileName);
- using (var stream = File.OpenRead(path))
+ await using (var stream = File.OpenRead(path))
{
- await JsonSerializer.DeserializeAsync<InternalMediaInfoResult>(stream, JsonDefaults.Options).ConfigureAwait(false);
+ var res = await JsonSerializer.DeserializeAsync<InternalMediaInfoResult>(stream, JsonDefaults.Options).ConfigureAwait(false);
+ Assert.NotNull(res);
}
}
}
diff --git a/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs b/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs
index 921c2b1f5..2873f6161 100644
--- a/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs
+++ b/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs
@@ -70,7 +70,8 @@ namespace Jellyfin.Naming.Tests.TV
[InlineData("Log Horizon 2/[HorribleSubs] Log Horizon 2 - 03 [720p].mkv", 3)] // digit in series name
[InlineData("Season 1/seriesname 05.mkv", 5)] // no hyphen between series name and episode number
[InlineData("[BBT-RMX] Ranma ½ - 154 [50AC421A].mkv", 154)] // hyphens in the pre-name info, triple digit episode number
- // TODO: [InlineData("Case Closed (1996-2007)/Case Closed - 317.mkv", 317)] // triple digit episode number
+ [InlineData("Season 2/Episode 21 - 94 Meetings.mp4", 21)] // Title starts with a number
+ // [InlineData("Case Closed (1996-2007)/Case Closed - 317.mkv", 317)] // triple digit episode number
// TODO: [InlineData("Season 2/16 12 Some Title.avi", 16)]
// TODO: [InlineData("/The.Legend.of.Condor.Heroes.2017.V2.web-dl.1080p.h264.aac-hdctv/The.Legend.of.Condor.Heroes.2017.E07.V2.web-dl.1080p.h264.aac-hdctv.mkv", 7)]
// TODO: [InlineData("Season 4/Uchuu.Senkan.Yamato.2199.E03.avi", 3)]
diff --git a/tests/Jellyfin.Server.Implementations.Tests/QuickConnect/QuickConnectManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/QuickConnect/QuickConnectManagerTests.cs
new file mode 100644
index 000000000..365acfa34
--- /dev/null
+++ b/tests/Jellyfin.Server.Implementations.Tests/QuickConnect/QuickConnectManagerTests.cs
@@ -0,0 +1,73 @@
+using System;
+using AutoFixture;
+using AutoFixture.AutoMoq;
+using Emby.Server.Implementations.QuickConnect;
+using MediaBrowser.Controller.Authentication;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Model.Configuration;
+using Moq;
+using Xunit;
+
+namespace Jellyfin.Server.Implementations.Tests.LiveTv
+{
+ public class QuickConnectManagerTests
+ {
+ private readonly Fixture _fixture;
+ private readonly ServerConfiguration _config;
+ private readonly QuickConnectManager _quickConnectManager;
+
+ public QuickConnectManagerTests()
+ {
+ _config = new ServerConfiguration();
+ var configManager = new Mock<IServerConfigurationManager>();
+ configManager.Setup(x => x.Configuration).Returns(_config);
+
+ _fixture = new Fixture();
+ _fixture.Customize(new AutoMoqCustomization
+ {
+ ConfigureMembers = true
+ }).Inject(configManager.Object);
+ _quickConnectManager = _fixture.Create<QuickConnectManager>();
+ }
+
+ [Fact]
+ public void IsEnabled_QuickConnectUnavailable_False()
+ => Assert.False(_quickConnectManager.IsEnabled);
+
+ [Fact]
+ public void TryConnect_QuickConnectUnavailable_ThrowsAuthenticationException()
+ => Assert.Throws<AuthenticationException>(_quickConnectManager.TryConnect);
+
+ [Fact]
+ public void CheckRequestStatus_QuickConnectUnavailable_ThrowsAuthenticationException()
+ => Assert.Throws<AuthenticationException>(() => _quickConnectManager.CheckRequestStatus(string.Empty));
+
+ [Fact]
+ public void AuthorizeRequest_QuickConnectUnavailable_ThrowsAuthenticationException()
+ => Assert.Throws<AuthenticationException>(() => _quickConnectManager.AuthorizeRequest(Guid.Empty, string.Empty));
+
+ [Fact]
+ public void IsEnabled_QuickConnectAvailable_True()
+ {
+ _config.QuickConnectAvailable = true;
+ Assert.True(_quickConnectManager.IsEnabled);
+ }
+
+ [Fact]
+ public void CheckRequestStatus_QuickConnectAvailable_Success()
+ {
+ _config.QuickConnectAvailable = true;
+ var res1 = _quickConnectManager.TryConnect();
+ var res2 = _quickConnectManager.CheckRequestStatus(res1.Secret);
+ Assert.Equal(res1, res2);
+ }
+
+ [Fact]
+ public void AuthorizeRequest_QuickConnectAvailable_Success()
+ {
+ _config.QuickConnectAvailable = true;
+ var res = _quickConnectManager.TryConnect();
+ Assert.True(_quickConnectManager.AuthorizeRequest(Guid.Empty, res.Code));
+ }
+ }
+}
diff --git a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj
index 59f125cd0..8bbe58387 100644
--- a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj
+++ b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj
@@ -12,7 +12,7 @@
<PackageReference Include="AutoFixture" Version="4.17.0" />
<PackageReference Include="AutoFixture.AutoMoq" Version="4.17.0" />
<PackageReference Include="AutoFixture.Xunit2" Version="4.17.0" />
- <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="5.0.7" />
+ <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="5.0.8" />
<PackageReference Include="Microsoft.Extensions.Options" Version="5.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
<PackageReference Include="xunit" Version="2.4.1" />
diff --git a/tests/Jellyfin.Server.Integration.Tests/JellyfinApplicationFactory.cs b/tests/Jellyfin.Server.Integration.Tests/JellyfinApplicationFactory.cs
index d9ec81a27..976e19d46 100644
--- a/tests/Jellyfin.Server.Integration.Tests/JellyfinApplicationFactory.cs
+++ b/tests/Jellyfin.Server.Integration.Tests/JellyfinApplicationFactory.cs
@@ -44,10 +44,7 @@ namespace Jellyfin.Server.Integration.Tests
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
// Specify the startup command line options
- var commandLineOpts = new StartupOptions
- {
- NoWebClient = true
- };
+ var commandLineOpts = new StartupOptions();
// Use a temporary directory for the application paths
var webHostPathRoot = Path.Combine(_testPathRoot, "test-host-" + Path.GetFileNameWithoutExtension(Path.GetRandomFileName()));
diff --git a/tests/Jellyfin.Server.Integration.Tests/Middleware/RobotsRedirectionMiddlewareTests.cs b/tests/Jellyfin.Server.Integration.Tests/Middleware/RobotsRedirectionMiddlewareTests.cs
new file mode 100644
index 000000000..8c49a2e2b
--- /dev/null
+++ b/tests/Jellyfin.Server.Integration.Tests/Middleware/RobotsRedirectionMiddlewareTests.cs
@@ -0,0 +1,32 @@
+using System.Net;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Mvc.Testing;
+using Xunit;
+
+namespace Jellyfin.Server.Integration.Tests.Middleware
+{
+ public sealed class RobotsRedirectionMiddlewareTests : IClassFixture<JellyfinApplicationFactory>
+ {
+ private readonly JellyfinApplicationFactory _factory;
+
+ public RobotsRedirectionMiddlewareTests(JellyfinApplicationFactory factory)
+ {
+ _factory = factory;
+ }
+
+ [Fact]
+ public async Task RobotsDotTxtRedirects()
+ {
+ var client = _factory.CreateClient(
+ new WebApplicationFactoryClientOptions()
+ {
+ AllowAutoRedirect = false
+ });
+
+ var response = await client.GetAsync("robots.txt").ConfigureAwait(false);
+
+ Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
+ Assert.Equal("web/robots.txt", response.Headers.Location?.ToString());
+ }
+ }
+}
diff --git a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj
index c8e72c10d..0bd48e8ab 100644
--- a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj
+++ b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj
@@ -13,7 +13,7 @@
<PackageReference Include="AutoFixture" Version="4.17.0" />
<PackageReference Include="AutoFixture.AutoMoq" Version="4.17.0" />
<PackageReference Include="AutoFixture.Xunit2" Version="4.17.0" />
- <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="5.0.7" />
+ <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="5.0.8" />
<PackageReference Include="Microsoft.Extensions.Options" Version="5.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
<PackageReference Include="xunit" Version="2.4.1" />