aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.ci/azure-pipelines-package.yml18
-rw-r--r--.drone.yml30
-rw-r--r--.github/workflows/codeql-analysis.yml2
-rw-r--r--Emby.Naming/AudioBook/AudioBookInfo.cs8
-rw-r--r--Emby.Naming/Video/VideoListResolver.cs13
-rw-r--r--Emby.Naming/Video/VideoResolver.cs4
-rw-r--r--Emby.Server.Implementations/Dto/DtoService.cs4
-rw-r--r--Emby.Server.Implementations/IO/ManagedFileSystem.cs4
-rw-r--r--Emby.Server.Implementations/Localization/Core/fi.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/hi.json27
-rw-r--r--Emby.Server.Implementations/Localization/Core/kk.json220
-rw-r--r--Emby.Server.Implementations/Localization/Core/ru.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/sv.json2
-rw-r--r--Emby.Server.Implementations/Plugins/PluginManager.cs26
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs9
-rw-r--r--Jellyfin.Api/Attributes/ParameterObsoleteAttribute.cs12
-rw-r--r--Jellyfin.Api/Controllers/ChannelsController.cs7
-rw-r--r--Jellyfin.Api/Controllers/ItemsController.cs8
-rw-r--r--Jellyfin.Api/Controllers/LiveTvController.cs4
-rw-r--r--Jellyfin.Api/Controllers/MediaInfoController.cs29
-rw-r--r--Jellyfin.Api/Controllers/PackageController.cs2
-rw-r--r--Jellyfin.Api/Controllers/PlaylistsController.cs10
-rw-r--r--Jellyfin.Api/Controllers/TrailersController.cs5
-rw-r--r--Jellyfin.Api/Controllers/UniversalAudioController.cs37
-rw-r--r--Jellyfin.Api/Controllers/UserController.cs2
-rw-r--r--Jellyfin.Api/Controllers/YearsController.cs5
-rw-r--r--Jellyfin.Api/Helpers/DynamicHlsHelper.cs2
-rw-r--r--Jellyfin.Api/Helpers/RequestHelpers.cs53
-rw-r--r--Jellyfin.Api/Jellyfin.Api.csproj2
-rw-r--r--Jellyfin.Api/Models/LiveTvDtos/GetProgramsDto.cs7
-rw-r--r--Jellyfin.Data/Jellyfin.Data.csproj4
-rw-r--r--Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj4
-rw-r--r--Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs8
-rw-r--r--Jellyfin.Server/Filters/ParameterObsoleteFilter.cs37
-rw-r--r--Jellyfin.Server/Jellyfin.Server.csproj4
-rw-r--r--MediaBrowser.Common/Extensions/ShuffleExtensions.cs3
-rw-r--r--MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.nuget.targets6
-rw-r--r--MediaBrowser.Model/MediaBrowser.Model.csproj2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs1
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs15
-rw-r--r--MediaBrowser.sln.GhostDoc.xml35
-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--hooks/pre_build6
-rw-r--r--tests/Jellyfin.Api.Tests/Helpers/RequestHelpersTests.cs59
-rw-r--r--tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj4
-rw-r--r--tests/Jellyfin.Common.Tests/Extensions/ShuffleExtensionsTests.cs22
-rw-r--r--tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj2
-rw-r--r--tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj2
-rw-r--r--tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj2
-rw-r--r--tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj2
-rw-r--r--tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj2
-rw-r--r--tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs79
-rw-r--r--tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs9
-rw-r--r--tests/Jellyfin.Networking.Tests/NetworkTesting/Jellyfin.Networking.Tests.csproj2
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj2
-rw-r--r--tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj2
68 files changed, 476 insertions, 420 deletions
diff --git a/.ci/azure-pipelines-package.yml b/.ci/azure-pipelines-package.yml
index 0a63b329b..20f4dfe33 100644
--- a/.ci/azure-pipelines-package.yml
+++ b/.ci/azure-pipelines-package.yml
@@ -193,6 +193,10 @@ jobs:
pool:
vmImage: 'ubuntu-latest'
+ variables:
+ - name: JellyfinVersion
+ value: $[replace(variables['Build.SourceBranch'],'refs/tags/v','')]
+
steps:
- task: UseDotNet@2
displayName: 'Use .NET 5.0 sdk'
@@ -204,9 +208,15 @@ jobs:
displayName: 'Build Stable Nuget packages'
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags/v')
inputs:
- command: 'pack'
- packagesToPack: 'Jellyfin.Data/Jellyfin.Data.csproj;MediaBrowser.Common/MediaBrowser.Common.csproj;MediaBrowser.Controller/MediaBrowser.Controller.csproj;MediaBrowser.Model/MediaBrowser.Model.csproj;Emby.Naming/Emby.Naming.csproj'
- versioningScheme: 'off'
+ command: 'custom'
+ projects: |
+ Jellyfin.Data/Jellyfin.Data.csproj
+ MediaBrowser.Common/MediaBrowser.Common.csproj
+ MediaBrowser.Controller/MediaBrowser.Controller.csproj
+ MediaBrowser.Model/MediaBrowser.Model.csproj
+ Emby.Naming/Emby.Naming.csproj
+ custom: 'pack'
+ arguments: -o $(Build.ArtifactStagingDirectory) -p:Version=$(JellyfinVersion)
- task: DotNetCoreCLI@2
displayName: 'Build Unstable Nuget packages'
@@ -233,7 +243,7 @@ jobs:
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags/v')
inputs:
command: 'push'
- packagesToPush: '$(Build.ArtifactStagingDirectory)/**/*.nupkg;$(Build.ArtifactStagingDirectory)/**/*.snupkg'
+ packagesToPush: '$(Build.ArtifactStagingDirectory)/**/*.nupkg'
nuGetFeedType: 'external'
publishFeedCredentials: 'NugetOrg'
allowPackageConflicts: true # This ignores an error if the version already exists
diff --git a/.drone.yml b/.drone.yml
deleted file mode 100644
index 87c8e414e..000000000
--- a/.drone.yml
+++ /dev/null
@@ -1,30 +0,0 @@
----
-kind: pipeline
-name: build-debug
-
-steps:
-- name: submodules
- image: docker:git
- commands:
- - git submodule update --init --recursive
-
-- name: build
- image: microsoft/dotnet:2-sdk
- commands:
- - dotnet publish "Jellyfin.Server" --configuration Debug --output "../ci/ci-debug"
-
----
-kind: pipeline
-name: build-release
-
-steps:
-- name: submodules
- image: docker:git
- commands:
- - git submodule update --init --recursive
-
-- name: build
- image: microsoft/dotnet:2-sdk
- commands:
- - dotnet publish "Jellyfin.Server" --configuration Release --output "../ci/ci-release"
-
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 538894818..3e456f909 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -24,7 +24,7 @@ jobs:
- name: Setup .NET Core
uses: actions/setup-dotnet@v1
with:
- dotnet-version: '5.0.100'
+ dotnet-version: '5.0.x'
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
diff --git a/Emby.Naming/AudioBook/AudioBookInfo.cs b/Emby.Naming/AudioBook/AudioBookInfo.cs
index adf403ab6..15702ff2c 100644
--- a/Emby.Naming/AudioBook/AudioBookInfo.cs
+++ b/Emby.Naming/AudioBook/AudioBookInfo.cs
@@ -15,13 +15,13 @@ namespace Emby.Naming.AudioBook
/// <param name="files">List of files composing the actual audiobook.</param>
/// <param name="extras">List of extra files.</param>
/// <param name="alternateVersions">Alternative version of files.</param>
- public AudioBookInfo(string name, int? year, List<AudioBookFileInfo>? files, List<AudioBookFileInfo>? extras, List<AudioBookFileInfo>? alternateVersions)
+ public AudioBookInfo(string name, int? year, List<AudioBookFileInfo> files, List<AudioBookFileInfo> extras, List<AudioBookFileInfo> alternateVersions)
{
Name = name;
Year = year;
- Files = files ?? new List<AudioBookFileInfo>();
- Extras = extras ?? new List<AudioBookFileInfo>();
- AlternateVersions = alternateVersions ?? new List<AudioBookFileInfo>();
+ Files = files;
+ Extras = extras;
+ AlternateVersions = alternateVersions;
}
/// <summary>
diff --git a/Emby.Naming/Video/VideoListResolver.cs b/Emby.Naming/Video/VideoListResolver.cs
index fd1677473..09a030d2d 100644
--- a/Emby.Naming/Video/VideoListResolver.cs
+++ b/Emby.Naming/Video/VideoListResolver.cs
@@ -185,8 +185,8 @@ namespace Emby.Naming.Video
if (!string.IsNullOrEmpty(folderName)
&& folderName.Length > 1
&& videos.All(i => i.Files.Count == 1
- && IsEligibleForMultiVersion(folderName, i.Files[0].Path))
- && HaveSameYear(videos))
+ && IsEligibleForMultiVersion(folderName, i.Files[0].Path))
+ && HaveSameYear(videos))
{
var ordered = videos.OrderBy(i => i.Name).ToList();
@@ -216,10 +216,9 @@ namespace Emby.Naming.Video
return videos.Select(i => i.Year ?? -1).Distinct().Count() < 2;
}
- private bool IsEligibleForMultiVersion(string folderName, string? testFilename)
+ private bool IsEligibleForMultiVersion(string folderName, string testFilePath)
{
- testFilename = Path.GetFileNameWithoutExtension(testFilename) ?? string.Empty;
-
+ string testFilename = Path.GetFileNameWithoutExtension(testFilePath);
if (testFilename.StartsWith(folderName, StringComparison.OrdinalIgnoreCase))
{
if (CleanStringParser.TryClean(testFilename, _options.CleanStringRegexes, out var cleanName))
@@ -233,8 +232,8 @@ namespace Emby.Naming.Video
}
return string.IsNullOrEmpty(testFilename)
- || testFilename[0].Equals('-')
- || testFilename[0].Equals('_')
+ || testFilename[0] == '-'
+ || testFilename[0] == '_'
|| string.IsNullOrWhiteSpace(Regex.Replace(testFilename, @"\[([^]]*)\]", string.Empty));
}
diff --git a/Emby.Naming/Video/VideoResolver.cs b/Emby.Naming/Video/VideoResolver.cs
index d7165d8d7..619d1520e 100644
--- a/Emby.Naming/Video/VideoResolver.cs
+++ b/Emby.Naming/Video/VideoResolver.cs
@@ -125,7 +125,7 @@ namespace Emby.Naming.Video
/// <returns>True if is video file.</returns>
public bool IsVideoFile(string path)
{
- var extension = Path.GetExtension(path) ?? string.Empty;
+ var extension = Path.GetExtension(path);
return _options.VideoFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase);
}
@@ -136,7 +136,7 @@ namespace Emby.Naming.Video
/// <returns>True if is video file stub.</returns>
public bool IsStubFile(string path)
{
- var extension = Path.GetExtension(path) ?? string.Empty;
+ var extension = Path.GetExtension(path);
return _options.StubFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase);
}
diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs
index 8a901516c..e3ab0d6ea 100644
--- a/Emby.Server.Implementations/Dto/DtoService.cs
+++ b/Emby.Server.Implementations/Dto/DtoService.cs
@@ -1157,7 +1157,7 @@ namespace Emby.Server.Implementations.Dto
if (episodeSeries != null)
{
dto.SeriesPrimaryImageTag = GetTagAndFillBlurhash(dto, episodeSeries, ImageType.Primary);
- if (!dto.ImageTags.ContainsKey(ImageType.Primary))
+ if (dto.ImageTags == null || !dto.ImageTags.ContainsKey(ImageType.Primary))
{
AttachPrimaryImageAspectRatio(dto, episodeSeries);
}
@@ -1207,7 +1207,7 @@ namespace Emby.Server.Implementations.Dto
if (series != null)
{
dto.SeriesPrimaryImageTag = GetTagAndFillBlurhash(dto, series, ImageType.Primary);
- if (!dto.ImageTags.ContainsKey(ImageType.Primary))
+ if (dto.ImageTags == null || !dto.ImageTags.ContainsKey(ImageType.Primary))
{
AttachPrimaryImageAspectRatio(dto, series);
}
diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs
index 5ebc9b61b..c0e757543 100644
--- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs
+++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs
@@ -684,7 +684,9 @@ namespace Emby.Server.Implementations.IO
return new EnumerationOptions
{
RecurseSubdirectories = recursive,
- IgnoreInaccessible = true
+ IgnoreInaccessible = true,
+ // Don't skip any files.
+ AttributesToSkip = 0
};
}
diff --git a/Emby.Server.Implementations/Localization/Core/fi.json b/Emby.Server.Implementations/Localization/Core/fi.json
index 954759b5c..b45bdcbad 100644
--- a/Emby.Server.Implementations/Localization/Core/fi.json
+++ b/Emby.Server.Implementations/Localization/Core/fi.json
@@ -39,7 +39,7 @@
"Channels": "Kanavat",
"CameraImageUploadedFrom": "Uusi kamerakuva on ladattu {0}",
"Books": "Kirjat",
- "AuthenticationSucceededWithUserName": "{0} todennus onnistui",
+ "AuthenticationSucceededWithUserName": "Käyttäjän {0} todennus onnistui",
"Artists": "Artistit",
"Application": "Sovellus",
"AppDeviceValues": "Sovellus: {0}, Laite: {1}",
diff --git a/Emby.Server.Implementations/Localization/Core/hi.json b/Emby.Server.Implementations/Localization/Core/hi.json
index 4cc2b378b..b9e5f301d 100644
--- a/Emby.Server.Implementations/Localization/Core/hi.json
+++ b/Emby.Server.Implementations/Localization/Core/hi.json
@@ -26,5 +26,30 @@
"AuthenticationSucceededWithUserName": "सफलता से प्रमाणीकृत",
"Artists": "कलाकारों",
"Application": "एप्लिकेशन",
- "AppDeviceValues": "एप: {0}, मशीन: {1}"
+ "AppDeviceValues": "एप: {0}, मशीन: {1}",
+ "NotificationOptionPluginUninstalled": "प्लगइन अनइंस्टाल हो गया",
+ "NotificationOptionPluginInstalled": "प्लगइन इनस्टॉल हो गया",
+ "NotificationOptionPluginError": "प्लगइन फ़ैल हो गया",
+ "NotificationOptionInstallationFailed": "इंस्टालेशन फ़ैल हो गया",
+ "NotificationOptionAudioPlaybackStopped": "संगीत बंद कर दिया गया",
+ "NotificationOptionAudioPlayback": "संगीत शुरू कर दिया गया",
+ "NotificationOptionCameraImageUploaded": "कैमरा फोटो अपलोड किया गया",
+ "NotificationOptionApplicationUpdateInstalled": "एप्लीकेशन अपडेट इनस्टॉल कर दिया है",
+ "NotificationOptionApplicationUpdateAvailable": "एप्लीकेशन अपडेट उपलभ्द है",
+ "NewVersionIsAvailable": "जेलीफिन सर्वर का एक नया वर्जन डाउनलोड के लिए उपलब्ध है।",
+ "NameSeasonUnknown": "अनजान भाग",
+ "NameSeasonNumber": "भाग {0}",
+ "NameInstallFailed": "{0} इनस्टॉल करते समय फेल हो गया है",
+ "MusicVideos": "संगीत वीडियो",
+ "Music": "संगीत",
+ "Movies": "फ़िल्म",
+ "MixedContent": "मिला-जुला कंटेंट",
+ "MessageServerConfigurationUpdated": "सर्वर कॉन्फ़िगरेशन अपडेट हो गया है",
+ "MessageNamedServerConfigurationUpdatedWithValue": "सर्वर कॉन्फ़िगरेशन भाग {0} अपडेट हो गया है",
+ "MessageApplicationUpdatedTo": "जैलीफिन सर्वर {0} में अपडेट हो गया है",
+ "MessageApplicationUpdated": "जैलीफिन सर्वर अपडेट हो गया है",
+ "Latest": "सबसे नया",
+ "LabelIpAddressValue": "आई पी एड्रेस: {0}",
+ "ItemRemovedWithName": "{0} लाइब्रेरी में से निकाल दिया है",
+ "HomeVideos": "होम वीडियोस"
}
diff --git a/Emby.Server.Implementations/Localization/Core/kk.json b/Emby.Server.Implementations/Localization/Core/kk.json
index befe4e14f..f4f6b442e 100644
--- a/Emby.Server.Implementations/Localization/Core/kk.json
+++ b/Emby.Server.Implementations/Localization/Core/kk.json
@@ -1,122 +1,122 @@
{
- "Albums": "Álbomdar",
- "AppDeviceValues": "Qoldanba: {0}, Qurylǵy: {1}",
+ "Albums": "Älbomdar",
+ "AppDeviceValues": "Qoldanba: {0}, Qūrylğy: {1}",
"Application": "Qoldanba",
- "Artists": "Oryndaýshylar",
- "AuthenticationSucceededWithUserName": "{0} túpnusqalyq rastalýy sátti aıaqtaldy",
- "Books": "Kitaptar",
- "CameraImageUploadedFrom": "{0} kamerasynan jańa sýret júktep salyndy",
+ "Artists": "Oryndauşylar",
+ "AuthenticationSucceededWithUserName": "{0} tüpnūsqalyq rastaluy sättı aiaqtaldy",
+ "Books": "Kıtaptar",
+ "CameraImageUploadedFrom": "{0} kamerasynan jaŋa suret jüktep salyndy",
"Channels": "Arnalar",
"ChapterNameValue": "{0}-sahna",
- "Collections": "Jıyntyqtar",
- "DeviceOfflineWithName": "{0} ajyratylǵan",
- "DeviceOnlineWithName": "{0} qosylǵan",
- "FailedLoginAttemptWithUserName": "{0} tarapynan kirý áreketi sátsiz aıaqtaldy",
- "Favorites": "Tańdaýlylar",
+ "Collections": "Jiyntyqtar",
+ "DeviceOfflineWithName": "{0} ajyratylğan",
+ "DeviceOnlineWithName": "{0} qosylğan",
+ "FailedLoginAttemptWithUserName": "{0} tarapynan kıru äreketı sätsız aiaqtaldy",
+ "Favorites": "Taŋdaulylar",
"Folders": "Qaltalar",
"Genres": "Janrlar",
- "HeaderAlbumArtists": "Álbom oryndaýshylary",
- "HeaderContinueWatching": "Qaraýdy jalǵastyrý",
- "HeaderFavoriteAlbums": "Tańdaýly álbomdar",
- "HeaderFavoriteArtists": "Tańdaýly oryndaýshylar",
- "HeaderFavoriteEpisodes": "Tańdaýly bólimder",
- "HeaderFavoriteShows": "Tańdaýly kórsetimder",
- "HeaderFavoriteSongs": "Tańdaýly áýender",
- "HeaderLiveTV": "Efır",
- "HeaderNextUp": "Kezekti",
+ "HeaderAlbumArtists": "Älbom oryndauşylary",
+ "HeaderContinueWatching": "Qaraudy jalğastyru",
+ "HeaderFavoriteAlbums": "Taŋdauly älbomdar",
+ "HeaderFavoriteArtists": "Taŋdauly oryndauşylar",
+ "HeaderFavoriteEpisodes": "Taŋdauly bölımder",
+ "HeaderFavoriteShows": "Taŋdauly körsetımder",
+ "HeaderFavoriteSongs": "Taŋdauly äuender",
+ "HeaderLiveTV": "Efir",
+ "HeaderNextUp": "Kezektı",
"HeaderRecordingGroups": "Jazba toptary",
- "HomeVideos": "Úılik beıneler",
- "Inherit": "Muraǵa ıelený",
- "ItemAddedWithName": "{0} tasyǵyshhanaǵa ústeldi",
- "ItemRemovedWithName": "{0} tasyǵyshhanadan alastaldy",
- "LabelIpAddressValue": "IP-mekenjaıy: {0}",
- "LabelRunningTimeValue": "Oınatý ýaqyty: {0}",
- "Latest": "Eń keıingi",
- "MessageApplicationUpdated": "Jellyfin Serveri jańartyldy",
- "MessageApplicationUpdatedTo": "Jellyfin Serveri {0} nusqasyna jańartyldy",
- "MessageNamedServerConfigurationUpdatedWithValue": "Server konfıgýrasýasynyń {0} bólimi jańartyldy",
- "MessageServerConfigurationUpdated": "Server konfıgýrasıasy jańartyldy",
- "MixedContent": "Aralas mazmun",
- "Movies": "Fılmder",
- "Music": "Mýzyka",
- "MusicVideos": "Mýzykalyq beıneler",
- "NameInstallFailed": "{0} ornatylýy sátsiz",
- "NameSeasonNumber": "{0}-maýsym",
- "NameSeasonUnknown": "Belgisiz maýsym",
- "NewVersionIsAvailable": "Jańa Jellyfin Server nusqasy júktep alýǵa qoljetimdi.",
- "NotificationOptionApplicationUpdateAvailable": "Qoldanba jańartýy qoljetimdi",
- "NotificationOptionApplicationUpdateInstalled": "Qoldanba jańartýy ornatyldy",
- "NotificationOptionAudioPlayback": "Dybys oınatýy bastaldy",
- "NotificationOptionAudioPlaybackStopped": "Dybys oınatýy toqtatyldy",
- "NotificationOptionCameraImageUploaded": "Kameradan fotosýret júktep salynǵan",
- "NotificationOptionInstallationFailed": "Ornatý sátsizdigi",
- "NotificationOptionNewLibraryContent": "Jańa mazmun ústelgen",
- "NotificationOptionPluginError": "Plagın sátsizdigi",
- "NotificationOptionPluginInstalled": "Plagın ornatyldy",
- "NotificationOptionPluginUninstalled": "Plagın ornatýy boldyrylmady",
- "NotificationOptionPluginUpdateInstalled": "Plagın jańartýy ornatyldy",
- "NotificationOptionServerRestartRequired": "Serverdi qaıta iske qosý qajet",
- "NotificationOptionTaskFailed": "Josparlaǵan tapsyrma sátsizdigi",
- "NotificationOptionUserLockedOut": "Paıdalanýshy qursaýly",
- "NotificationOptionVideoPlayback": "Beıne oınatýy bastaldy",
- "NotificationOptionVideoPlaybackStopped": "Beıne oınatýy toqtatyldy",
- "Photos": "Fotosýretter",
- "Playlists": "Oınatý tizimderi",
- "Plugin": "Plagın",
+ "HomeVideos": "Üilık beineler",
+ "Inherit": "Mūrağa ielenu",
+ "ItemAddedWithName": "{0} tasyğyşhanağa üsteldı",
+ "ItemRemovedWithName": "{0} tasyğyşhanadan alastaldy",
+ "LabelIpAddressValue": "İP-mekenjaiy: {0}",
+ "LabelRunningTimeValue": "Oinatu uaqyty: {0}",
+ "Latest": "Eŋ keiıngı",
+ "MessageApplicationUpdated": "Jellyfin Serverı jaŋartyldy",
+ "MessageApplicationUpdatedTo": "Jellyfin Serverı {0} nūsqasyna jaŋartyldy",
+ "MessageNamedServerConfigurationUpdatedWithValue": "Server konfigurasuasynyŋ {0} bölımı jaŋartyldy",
+ "MessageServerConfigurationUpdated": "Server konfigurasiasy jaŋartyldy",
+ "MixedContent": "Aralas mazmūn",
+ "Movies": "Filmder",
+ "Music": "Muzyka",
+ "MusicVideos": "Muzykalyq beineler",
+ "NameInstallFailed": "{0} ornatyluy sätsız",
+ "NameSeasonNumber": "{0}-mausym",
+ "NameSeasonUnknown": "Belgısız mausym",
+ "NewVersionIsAvailable": "Jaŋa Jellyfin Server nūsqasy jüktep aluğa qoljetımdı.",
+ "NotificationOptionApplicationUpdateAvailable": "Qoldanba jaŋartuy qoljetımdı",
+ "NotificationOptionApplicationUpdateInstalled": "Qoldanba jaŋartuy ornatyldy",
+ "NotificationOptionAudioPlayback": "Dybys oinatuy bastaldy",
+ "NotificationOptionAudioPlaybackStopped": "Dybys oinatuy toqtatyldy",
+ "NotificationOptionCameraImageUploaded": "Kameradan fotosuret jüktep salynğan",
+ "NotificationOptionInstallationFailed": "Ornatu sätsızdıgı",
+ "NotificationOptionNewLibraryContent": "Jaŋa mazmūn üstelgen",
+ "NotificationOptionPluginError": "Plagin sätsızdıgı",
+ "NotificationOptionPluginInstalled": "Plagin ornatyldy",
+ "NotificationOptionPluginUninstalled": "Plagin ornatuy boldyrylmady",
+ "NotificationOptionPluginUpdateInstalled": "Plagin jaŋartuy ornatyldy",
+ "NotificationOptionServerRestartRequired": "Serverdı qaita ıske qosu qajet",
+ "NotificationOptionTaskFailed": "Josparlağan tapsyrma sätsızdıgı",
+ "NotificationOptionUserLockedOut": "Paidalanuşy qūrsauly",
+ "NotificationOptionVideoPlayback": "Beine oinatuy bastaldy",
+ "NotificationOptionVideoPlaybackStopped": "Beine oinatuy toqtatyldy",
+ "Photos": "Fotosuretter",
+ "Playlists": "Oinatu tızımderı",
+ "Plugin": "Plagin",
"PluginInstalledWithName": "{0} ornatyldy",
- "PluginUninstalledWithName": "{0} joıyldy",
- "PluginUpdatedWithName": "{0} jańartyldy",
- "ProviderValue": "Jetkizýshi: {0}",
- "ScheduledTaskFailedWithName": "{0} sátsiz",
- "ScheduledTaskStartedWithName": "{0} iske qosyldy",
- "ServerNameNeedsToBeRestarted": "{0} qaıta iske qosý qajet",
- "Shows": "Kórsetimder",
- "Songs": "Áýender",
- "StartupEmbyServerIsLoading": "Jellyfin Server júktelýde. Áreketti kóp uzamaı qaıtalańyz.",
+ "PluginUninstalledWithName": "{0} joiyldy",
+ "PluginUpdatedWithName": "{0} jaŋartyldy",
+ "ProviderValue": "Jetkızuşı: {0}",
+ "ScheduledTaskFailedWithName": "{0} sätsız",
+ "ScheduledTaskStartedWithName": "{0} ıske qosyldy",
+ "ServerNameNeedsToBeRestarted": "{0} qaita ıske qosu qajet",
+ "Shows": "Körsetımder",
+ "Songs": "Äuender",
+ "StartupEmbyServerIsLoading": "Jellyfin Server jüktelude. Ärekettı köp ūzamai qaitalaŋyz.",
"SubtitleDownloadFailureForItem": "Субтитрлер {0} үшін жүктеліп алынуы сәтсіз",
- "SubtitleDownloadFailureFromForItem": "{1} úshin sýbtıtrlerdi {0} kózinen júktep alý sátsiz",
- "Sync": "Úndestirý",
- "System": "Júıe",
- "TvShows": "TD-kórsetimder",
- "User": "Paıdalanýshy",
- "UserCreatedWithName": "Paıdalanýshy {0} jasalǵan",
- "UserDeletedWithName": "Paıdalanýshy {0} joıylǵan",
- "UserDownloadingItemWithValues": "{0} mynany júktep alýda: {1}",
- "UserLockedOutWithName": "Paıdalanýshy {0} qursaýly",
- "UserOfflineFromDevice": "{0} - {1} tarapynan ajyratylǵan",
- "UserOnlineFromDevice": "{0} - {1} arqyly qosylǵan",
- "UserPasswordChangedWithName": "Paıdalanýshy {0} úshin paról ózgertildi",
- "UserPolicyUpdatedWithName": "Paıdalanýshy {0} úshin saıasattary jańartyldy",
- "UserStartedPlayingItemWithValues": "{0} - {1} oınatýyn {2} bastady",
- "UserStoppedPlayingItemWithValues": "{0} - {1} oınatýyn {2} toqtatty",
- "ValueHasBeenAddedToLibrary": "{0} (tasyǵyshhanaǵa ústelindi)",
- "ValueSpecialEpisodeName": "Arnaıy - {0}",
- "VersionNumber": "Nusqasy {0}",
- "Default": "Ádepki",
- "TaskDownloadMissingSubtitles": "Joq sýbtıtrlerdi júktep alý",
- "TaskRefreshChannels": "Arnalardy jańǵyrtý",
- "TaskCleanTranscode": "Qaıta kodtaý katalogyn tazalaý",
- "TaskUpdatePlugins": "Plagınderdi jańartý",
- "TaskRefreshPeople": "Adamdardy jańartý",
- "TaskCleanLogs": "Jurnal katalogyn tazalaý",
- "TaskRefreshLibrary": "Tasyǵyshhanany skanerleý",
- "TaskRefreshChapterImages": "Sahna keskinderin shyǵaryp alý",
- "TaskCleanCache": "Kesh katalogyn tazalaý",
- "TaskCleanActivityLog": "Áreket jurnalyn tazalaý",
- "TasksChannelsCategory": "Internet-arnalar",
+ "SubtitleDownloadFailureFromForItem": "{1} üşın subtitrlerdı {0} közınen jüktep alu sätsız",
+ "Sync": "Ündestıru",
+ "System": "Jüie",
+ "TvShows": "TD-körsetımder",
+ "User": "Paidalanuşy",
+ "UserCreatedWithName": "Paidalanuşy {0} jasalğan",
+ "UserDeletedWithName": "Paidalanuşy {0} joiylğan",
+ "UserDownloadingItemWithValues": "{0} mynany jüktep aluda: {1}",
+ "UserLockedOutWithName": "Paidalanuşy {0} qūrsauly",
+ "UserOfflineFromDevice": "{0} - {1} tarapynan ajyratylğan",
+ "UserOnlineFromDevice": "{0} - {1} arqyly qosylğan",
+ "UserPasswordChangedWithName": "Paidalanuşy {0} üşın paröl özgertıldı",
+ "UserPolicyUpdatedWithName": "Paidalanuşy {0} üşın saiasattary jaŋartyldy",
+ "UserStartedPlayingItemWithValues": "{0} - {1} oinatuyn {2} bastady",
+ "UserStoppedPlayingItemWithValues": "{0} - {1} oinatuyn {2} toqtatty",
+ "ValueHasBeenAddedToLibrary": "{0} (tasyğyşhanağa üstelındı)",
+ "ValueSpecialEpisodeName": "Arnaiy - {0}",
+ "VersionNumber": "Nūsqasy {0}",
+ "Default": "Ädepkı",
+ "TaskDownloadMissingSubtitles": "Joq subtitrlerdı jüktep alu",
+ "TaskRefreshChannels": "Arnalardy jaŋğyrtu",
+ "TaskCleanTranscode": "Qaita kodtau katalogyn tazalau",
+ "TaskUpdatePlugins": "Plaginderdı jaŋartu",
+ "TaskRefreshPeople": "Adamdardy jaŋartu",
+ "TaskCleanLogs": "Jūrnal katalogyn tazalau",
+ "TaskRefreshLibrary": "Tasyğyşhanany skanerleu",
+ "TaskRefreshChapterImages": "Sahna keskınderın şyğaryp alu",
+ "TaskCleanCache": "Keş katalogyn tazalau",
+ "TaskCleanActivityLog": "Äreket jūrnalyn tazalau",
+ "TasksChannelsCategory": "İnternet-arnalar",
"TasksApplicationCategory": "Qoldanba",
- "TasksLibraryCategory": "Tasyǵyshhana",
- "TasksMaintenanceCategory": "Qyzmet kórsetý",
+ "TasksLibraryCategory": "Tasyğyşhana",
+ "TasksMaintenanceCategory": "Qyzmet körsetu",
"Undefined": "Anyqtalmady",
- "Forced": "Májbúrli",
- "TaskDownloadMissingSubtitlesDescription": "Metaderekter teńshelimi negіzіnde joq sýbtıtrlerdі Internetten іzdeıdі.",
- "TaskRefreshChannelsDescription": "Internet-arnalar málimetterin jańǵyrtady.",
- "TaskCleanTranscodeDescription": "Bіr kúnnen asqan qaıta kodtaý faıldaryn joıady.",
- "TaskUpdatePluginsDescription": "Avtomatty túrde jańartýǵa teńshelgen plagınder úshin jańartýlardy júktep alady jáne ornatady.",
- "TaskRefreshPeopleDescription": "Tasyǵyshhanadaǵy aktórler men rejısórler metaderekterіn jańartady.",
- "TaskCleanLogsDescription": "{0} kúnnen asqan jurnal faıldaryn joıady.",
- "TaskRefreshLibraryDescription": "Tasyǵyshhanadaǵy jańa faıldardy skanerleıdі jáne metaderekterdі jańartady.",
- "TaskRefreshChapterImagesDescription": "Sahnalarǵa bólіngen beıneler úshіn nobaılar jasaıdy.",
- "TaskCleanCacheDescription": "Júıede qajet emes keshtelgen faıldardy joıady.",
- "TaskCleanActivityLogDescription": "Áreketter jurnalyndaǵy teńshelgen jasynan asqan jazbalaly joıady."
+ "Forced": "Mäjbürlı",
+ "TaskDownloadMissingSubtitlesDescription": "Metaderekter teŋşelımı negіzіnde joq subtitrlerdі İnternetten іzdeidі.",
+ "TaskRefreshChannelsDescription": "İnternet-arnalar mälımetterın jaŋğyrtady.",
+ "TaskCleanTranscodeDescription": "Bіr künnen asqan qaita kodtau faildaryn joiady.",
+ "TaskUpdatePluginsDescription": "Avtomatty türde jaŋartuğa teŋşelgen plaginder üşın jaŋartulardy jüktep alady jäne ornatady.",
+ "TaskRefreshPeopleDescription": "Tasyğyşhanadağy aktörler men rejisörler metaderekterіn jaŋartady.",
+ "TaskCleanLogsDescription": "{0} künnen asqan jūrnal faildaryn joiady.",
+ "TaskRefreshLibraryDescription": "Tasyğyşhanadağy jaŋa faildardy skanerleidі jäne metaderekterdі jaŋartady.",
+ "TaskRefreshChapterImagesDescription": "Sahnalarğa bölіngen beineler üşіn nobailar jasaidy.",
+ "TaskCleanCacheDescription": "Jüiede qajet emes keştelgen faildardy joiady.",
+ "TaskCleanActivityLogDescription": "Äreketter jūrnalyndağy teŋşelgen jasynan asqan jazbalary joiady."
}
diff --git a/Emby.Server.Implementations/Localization/Core/ru.json b/Emby.Server.Implementations/Localization/Core/ru.json
index 03d30247a..9119cf0af 100644
--- a/Emby.Server.Implementations/Localization/Core/ru.json
+++ b/Emby.Server.Implementations/Localization/Core/ru.json
@@ -90,7 +90,7 @@
"UserStartedPlayingItemWithValues": "{0} - воспроизведение «{1}» на {2}",
"UserStoppedPlayingItemWithValues": "{0} - воспроизведение остановлено «{1}» на {2}",
"ValueHasBeenAddedToLibrary": "{0} (добавлено в медиатеку)",
- "ValueSpecialEpisodeName": "Специальный эпизод - {0}",
+ "ValueSpecialEpisodeName": "Спецэпизод - {0}",
"VersionNumber": "Версия {0}",
"TaskDownloadMissingSubtitles": "Загрузка отсутствующих субтитров",
"TaskRefreshChannels": "Обновление каналов",
diff --git a/Emby.Server.Implementations/Localization/Core/sv.json b/Emby.Server.Implementations/Localization/Core/sv.json
index 552710d70..345d41e9e 100644
--- a/Emby.Server.Implementations/Localization/Core/sv.json
+++ b/Emby.Server.Implementations/Localization/Core/sv.json
@@ -117,5 +117,5 @@
"TaskCleanActivityLogDescription": "Radera aktivitets logg inlägg som är äldre än definerad ålder.",
"TaskCleanActivityLog": "Rensa Aktivitets Logg",
"Undefined": "odefinierad",
- "Forced": "Tvinga"
+ "Forced": "Tvingad"
}
diff --git a/Emby.Server.Implementations/Plugins/PluginManager.cs b/Emby.Server.Implementations/Plugins/PluginManager.cs
index 1ab01252d..adf62124a 100644
--- a/Emby.Server.Implementations/Plugins/PluginManager.cs
+++ b/Emby.Server.Implementations/Plugins/PluginManager.cs
@@ -112,8 +112,6 @@ namespace Emby.Server.Implementations.Plugins
{
assembly = Assembly.LoadFrom(file);
- // This force loads all reference dll's that the plugin uses in the try..catch block.
- // Removing this will cause JF to bomb out if referenced dll's cause issues.
assembly.GetExportedTypes();
}
catch (FileLoadException ex)
@@ -122,6 +120,20 @@ namespace Emby.Server.Implementations.Plugins
ChangePluginState(plugin, PluginStatus.Malfunctioned);
continue;
}
+ catch (TypeLoadException ex) // Undocumented exception
+ {
+ _logger.LogError(ex, "Failed to load assembly {Path}. This error occurs when a plugin references an incompatible version of one of the shared libraries. Disabling plugin.", file);
+ ChangePluginState(plugin, PluginStatus.NotSupported);
+ continue;
+ }
+#pragma warning disable CA1031 // Do not catch general exception types
+ catch (Exception ex)
+#pragma warning restore CA1031 // Do not catch general exception types
+ {
+ _logger.LogError(ex, "Failed to load assembly {Path}. Unknown exception was thrown. Disabling plugin.", file);
+ ChangePluginState(plugin, PluginStatus.Malfunctioned);
+ continue;
+ }
_logger.LogInformation("Loaded assembly {Assembly} from {Path}", assembly.FullName, file);
yield return assembly;
@@ -374,7 +386,7 @@ namespace Emby.Server.Implementations.Plugins
private LocalPlugin? GetPluginByAssembly(Assembly assembly)
{
// Find which plugin it is by the path.
- return _plugins.FirstOrDefault(p => string.Equals(p.Path, Path.GetDirectoryName(assembly.Location), StringComparison.Ordinal));
+ return _plugins.FirstOrDefault(p => p.DllFiles.Contains(assembly.Location, StringComparer.Ordinal));
}
/// <summary>
@@ -421,15 +433,17 @@ namespace Emby.Server.Implementations.Plugins
{
plugin.Instance = instance;
var manifest = plugin.Manifest;
- var pluginStr = plugin.Instance.Version.ToString();
+ var pluginStr = instance.Version.ToString();
bool changed = false;
- if (string.Equals(manifest.Version, pluginStr, StringComparison.Ordinal))
+ if (string.Equals(manifest.Version, pluginStr, StringComparison.Ordinal)
+ || manifest.Id != instance.Id)
{
// If a plugin without a manifest failed to load due to an external issue (eg config),
// this updates the manifest to the actual plugin values.
manifest.Version = pluginStr;
manifest.Name = plugin.Instance.Name;
manifest.Description = plugin.Instance.Description;
+ manifest.Id = plugin.Instance.Id;
changed = true;
}
@@ -559,7 +573,7 @@ namespace Emby.Server.Implementations.Plugins
// Auto-create a plugin manifest, so we can disable it, if it fails to load.
manifest = new PluginManifest
{
- Status = PluginStatus.Restart,
+ Status = PluginStatus.Active,
Name = metafile,
AutoUpdate = false,
Id = metafile.GetMD5(),
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs
index 184d155d4..fedb5deb0 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs
@@ -80,10 +80,11 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
// Delete log files more than n days old
var minDateModified = DateTime.UtcNow.AddDays(-_configurationManager.CommonConfiguration.LogFileRetentionDays);
- // Only delete the .txt log files, the *.log files created by serilog get managed by itself
- var filesToDelete = _fileSystem.GetFiles(_configurationManager.CommonApplicationPaths.LogDirectoryPath, new[] { ".txt" }, true, true)
- .Where(f => _fileSystem.GetLastWriteTimeUtc(f) < minDateModified)
- .ToList();
+ // Only delete files that serilog doesn't manage (anything that doesn't start with 'log_'
+ var filesToDelete = _fileSystem.GetFiles(_configurationManager.CommonApplicationPaths.LogDirectoryPath, true)
+ .Where(f => !f.Name.StartsWith("log_", StringComparison.Ordinal)
+ && _fileSystem.GetLastWriteTimeUtc(f) < minDateModified)
+ .ToList();
var index = 0;
diff --git a/Jellyfin.Api/Attributes/ParameterObsoleteAttribute.cs b/Jellyfin.Api/Attributes/ParameterObsoleteAttribute.cs
new file mode 100644
index 000000000..56c9772b6
--- /dev/null
+++ b/Jellyfin.Api/Attributes/ParameterObsoleteAttribute.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace Jellyfin.Api.Attributes
+{
+ /// <summary>
+ /// Attribute to mark a parameter as obsolete.
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Parameter)]
+ public class ParameterObsoleteAttribute : Attribute
+ {
+ }
+}
diff --git a/Jellyfin.Api/Controllers/ChannelsController.cs b/Jellyfin.Api/Controllers/ChannelsController.cs
index b70c76e80..54bd80095 100644
--- a/Jellyfin.Api/Controllers/ChannelsController.cs
+++ b/Jellyfin.Api/Controllers/ChannelsController.cs
@@ -1,13 +1,12 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
-using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Api.Constants;
-using Jellyfin.Api.Extensions;
using Jellyfin.Api.Helpers;
using Jellyfin.Api.ModelBinders;
+using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
@@ -121,9 +120,9 @@ namespace Jellyfin.Api.Controllers
[FromQuery] Guid? userId,
[FromQuery] int? startIndex,
[FromQuery] int? limit,
- [FromQuery] string? sortOrder,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] SortOrder[] sortOrder,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFilter[] filters,
- [FromQuery] string? sortBy,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] sortBy,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields)
{
var user = userId.HasValue && !userId.Equals(Guid.Empty)
diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs
index b84136ac6..7d7747495 100644
--- a/Jellyfin.Api/Controllers/ItemsController.cs
+++ b/Jellyfin.Api/Controllers/ItemsController.cs
@@ -175,7 +175,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] int? limit,
[FromQuery] bool? recursive,
[FromQuery] string? searchTerm,
- [FromQuery] string? sortOrder,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] SortOrder[] sortOrder,
[FromQuery] Guid? parentId,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] excludeItemTypes,
@@ -184,7 +184,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] bool? isFavorite,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] mediaTypes,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] imageTypes,
- [FromQuery] string? sortBy,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] sortBy,
[FromQuery] bool? isPlayed,
[FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] genres,
[FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] officialRatings,
@@ -608,7 +608,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] int? limit,
[FromQuery] bool? recursive,
[FromQuery] string? searchTerm,
- [FromQuery] string? sortOrder,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] SortOrder[] sortOrder,
[FromQuery] Guid? parentId,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] excludeItemTypes,
@@ -617,7 +617,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] bool? isFavorite,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] mediaTypes,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] imageTypes,
- [FromQuery] string? sortBy,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] sortBy,
[FromQuery] bool? isPlayed,
[FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] genres,
[FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] officialRatings,
diff --git a/Jellyfin.Api/Controllers/LiveTvController.cs b/Jellyfin.Api/Controllers/LiveTvController.cs
index 6f2d43227..24ee833ef 100644
--- a/Jellyfin.Api/Controllers/LiveTvController.cs
+++ b/Jellyfin.Api/Controllers/LiveTvController.cs
@@ -553,8 +553,8 @@ namespace Jellyfin.Api.Controllers
[FromQuery] bool? isSports,
[FromQuery] int? startIndex,
[FromQuery] int? limit,
- [FromQuery] string? sortBy,
- [FromQuery] string? sortOrder,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] sortBy,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] SortOrder[] sortOrder,
[FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] genres,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] genreIds,
[FromQuery] bool? enableImages,
diff --git a/Jellyfin.Api/Controllers/MediaInfoController.cs b/Jellyfin.Api/Controllers/MediaInfoController.cs
index baa2e0636..e330f02b6 100644
--- a/Jellyfin.Api/Controllers/MediaInfoController.cs
+++ b/Jellyfin.Api/Controllers/MediaInfoController.cs
@@ -83,6 +83,7 @@ namespace Jellyfin.Api.Controllers
/// </summary>
/// <remarks>
/// For backwards compatibility parameters can be sent via Query or Body, with Query having higher precedence.
+ /// Query parameters are obsolete.
/// </remarks>
/// <param name="itemId">The item id.</param>
/// <param name="userId">The user id.</param>
@@ -106,20 +107,20 @@ namespace Jellyfin.Api.Controllers
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<PlaybackInfoResponse>> GetPostedPlaybackInfo(
[FromRoute, Required] Guid itemId,
- [FromQuery] Guid? userId,
- [FromQuery] int? maxStreamingBitrate,
- [FromQuery] long? startTimeTicks,
- [FromQuery] int? audioStreamIndex,
- [FromQuery] int? subtitleStreamIndex,
- [FromQuery] int? maxAudioChannels,
- [FromQuery] string? mediaSourceId,
- [FromQuery] string? liveStreamId,
- [FromQuery] bool? autoOpenLiveStream,
- [FromQuery] bool? enableDirectPlay,
- [FromQuery] bool? enableDirectStream,
- [FromQuery] bool? enableTranscoding,
- [FromQuery] bool? allowVideoStreamCopy,
- [FromQuery] bool? allowAudioStreamCopy,
+ [FromQuery, ParameterObsolete] Guid? userId,
+ [FromQuery, ParameterObsolete] int? maxStreamingBitrate,
+ [FromQuery, ParameterObsolete] long? startTimeTicks,
+ [FromQuery, ParameterObsolete] int? audioStreamIndex,
+ [FromQuery, ParameterObsolete] int? subtitleStreamIndex,
+ [FromQuery, ParameterObsolete] int? maxAudioChannels,
+ [FromQuery, ParameterObsolete] string? mediaSourceId,
+ [FromQuery, ParameterObsolete] string? liveStreamId,
+ [FromQuery, ParameterObsolete] bool? autoOpenLiveStream,
+ [FromQuery, ParameterObsolete] bool? enableDirectPlay,
+ [FromQuery, ParameterObsolete] bool? enableDirectStream,
+ [FromQuery, ParameterObsolete] bool? enableTranscoding,
+ [FromQuery, ParameterObsolete] bool? allowVideoStreamCopy,
+ [FromQuery, ParameterObsolete] bool? allowAudioStreamCopy,
[FromBody(EmptyBodyBehavior = EmptyBodyBehavior.Allow)] PlaybackInfoDto? playbackInfoDto)
{
var authInfo = _authContext.GetAuthorizationInfo(Request);
diff --git a/Jellyfin.Api/Controllers/PackageController.cs b/Jellyfin.Api/Controllers/PackageController.cs
index 9ab8e0bdc..c589f54ac 100644
--- a/Jellyfin.Api/Controllers/PackageController.cs
+++ b/Jellyfin.Api/Controllers/PackageController.cs
@@ -158,7 +158,7 @@ namespace Jellyfin.Api.Controllers
[HttpPost("Repositories")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
- public ActionResult SetRepositories([FromBody] List<RepositoryInfo> repositoryInfos)
+ public ActionResult SetRepositories([FromBody, Required] List<RepositoryInfo> repositoryInfos)
{
_serverConfigurationManager.Configuration.PluginRepositories = repositoryInfos;
_serverConfigurationManager.SaveConfiguration();
diff --git a/Jellyfin.Api/Controllers/PlaylistsController.cs b/Jellyfin.Api/Controllers/PlaylistsController.cs
index fcdad4bc7..a55e4ad2f 100644
--- a/Jellyfin.Api/Controllers/PlaylistsController.cs
+++ b/Jellyfin.Api/Controllers/PlaylistsController.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
+using Jellyfin.Api.Attributes;
using Jellyfin.Api.Constants;
using Jellyfin.Api.Extensions;
using Jellyfin.Api.Helpers;
@@ -57,6 +58,7 @@ namespace Jellyfin.Api.Controllers
/// </summary>
/// <remarks>
/// For backwards compatibility parameters can be sent via Query or Body, with Query having higher precedence.
+ /// Query parameters are obsolete.
/// </remarks>
/// <param name="name">The playlist name.</param>
/// <param name="ids">The item ids.</param>
@@ -70,10 +72,10 @@ namespace Jellyfin.Api.Controllers
[HttpPost]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<PlaylistCreationResult>> CreatePlaylist(
- [FromQuery] string? name,
- [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] IReadOnlyList<Guid> ids,
- [FromQuery] Guid? userId,
- [FromQuery] string? mediaType,
+ [FromQuery, ParameterObsolete] string? name,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder)), ParameterObsolete] IReadOnlyList<Guid> ids,
+ [FromQuery, ParameterObsolete] Guid? userId,
+ [FromQuery, ParameterObsolete] string? mediaType,
[FromBody(EmptyBodyBehavior = EmptyBodyBehavior.Allow)] CreatePlaylistDto? createPlaylistRequest)
{
if (ids.Count == 0)
diff --git a/Jellyfin.Api/Controllers/TrailersController.cs b/Jellyfin.Api/Controllers/TrailersController.cs
index 8e9ece14f..242b8f068 100644
--- a/Jellyfin.Api/Controllers/TrailersController.cs
+++ b/Jellyfin.Api/Controllers/TrailersController.cs
@@ -1,6 +1,7 @@
using System;
using Jellyfin.Api.Constants;
using Jellyfin.Api.ModelBinders;
+using Jellyfin.Data.Enums;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
@@ -144,7 +145,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] int? limit,
[FromQuery] bool? recursive,
[FromQuery] string? searchTerm,
- [FromQuery] string? sortOrder,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] SortOrder[] sortOrder,
[FromQuery] Guid? parentId,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] excludeItemTypes,
@@ -152,7 +153,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] bool? isFavorite,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] mediaTypes,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] imageTypes,
- [FromQuery] string? sortBy,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] sortBy,
[FromQuery] bool? isPlayed,
[FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] genres,
[FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] officialRatings,
diff --git a/Jellyfin.Api/Controllers/UniversalAudioController.cs b/Jellyfin.Api/Controllers/UniversalAudioController.cs
index 34c9f32fa..bacd95bac 100644
--- a/Jellyfin.Api/Controllers/UniversalAudioController.cs
+++ b/Jellyfin.Api/Controllers/UniversalAudioController.cs
@@ -278,7 +278,7 @@ namespace Jellyfin.Api.Controllers
var directPlayProfiles = new DirectPlayProfile[len];
for (int i = 0; i < len; i++)
{
- var parts = RequestHelpers.Split(containers[i], '|', true);
+ var parts = containers[i].Split('|', StringSplitOptions.RemoveEmptyEntries);
var audioCodecs = parts.Length == 1 ? null : string.Join(',', parts.Skip(1));
@@ -312,25 +312,52 @@ namespace Jellyfin.Api.Controllers
if (maxAudioSampleRate.HasValue)
{
// codec profile
- conditions.Add(new ProfileCondition { Condition = ProfileConditionType.LessThanEqual, IsRequired = false, Property = ProfileConditionValue.AudioSampleRate, Value = maxAudioSampleRate.Value.ToString(CultureInfo.InvariantCulture) });
+ conditions.Add(
+ new ProfileCondition
+ {
+ Condition = ProfileConditionType.LessThanEqual,
+ IsRequired = false,
+ Property = ProfileConditionValue.AudioSampleRate,
+ Value = maxAudioSampleRate.Value.ToString(CultureInfo.InvariantCulture)
+ });
}
if (maxAudioBitDepth.HasValue)
{
// codec profile
- conditions.Add(new ProfileCondition { Condition = ProfileConditionType.LessThanEqual, IsRequired = false, Property = ProfileConditionValue.AudioBitDepth, Value = maxAudioBitDepth.Value.ToString(CultureInfo.InvariantCulture) });
+ conditions.Add(
+ new ProfileCondition
+ {
+ Condition = ProfileConditionType.LessThanEqual,
+ IsRequired = false,
+ Property = ProfileConditionValue.AudioBitDepth,
+ Value = maxAudioBitDepth.Value.ToString(CultureInfo.InvariantCulture)
+ });
}
if (maxAudioChannels.HasValue)
{
// codec profile
- conditions.Add(new ProfileCondition { Condition = ProfileConditionType.LessThanEqual, IsRequired = false, Property = ProfileConditionValue.AudioChannels, Value = maxAudioChannels.Value.ToString(CultureInfo.InvariantCulture) });
+ conditions.Add(
+ new ProfileCondition
+ {
+ Condition = ProfileConditionType.LessThanEqual,
+ IsRequired = false,
+ Property = ProfileConditionValue.AudioChannels,
+ Value = maxAudioChannels.Value.ToString(CultureInfo.InvariantCulture)
+ });
}
if (conditions.Count > 0)
{
// codec profile
- codecProfiles.Add(new CodecProfile { Type = CodecType.Audio, Container = string.Join(',', containers), Conditions = conditions.ToArray() });
+ codecProfiles.Add(
+ new CodecProfile
+ {
+ Type = CodecType.Audio,
+ Container = string.Join(',', containers),
+ Conditions = conditions.ToArray()
+ });
}
deviceProfile.CodecProfiles = codecProfiles.ToArray();
diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs
index 0f0bee4bc..87a4ffd92 100644
--- a/Jellyfin.Api/Controllers/UserController.cs
+++ b/Jellyfin.Api/Controllers/UserController.cs
@@ -514,7 +514,7 @@ namespace Jellyfin.Api.Controllers
/// <returns>A <see cref="Task"/> containing a <see cref="PinRedeemResult"/>.</returns>
[HttpPost("ForgotPassword/Pin")]
[ProducesResponseType(StatusCodes.Status200OK)]
- public async Task<ActionResult<PinRedeemResult>> ForgotPasswordPin([FromBody] string? pin)
+ public async Task<ActionResult<PinRedeemResult>> ForgotPasswordPin([FromBody, Required] string pin)
{
var result = await _userManager.RedeemPasswordResetPin(pin).ConfigureAwait(false);
return result;
diff --git a/Jellyfin.Api/Controllers/YearsController.cs b/Jellyfin.Api/Controllers/YearsController.cs
index 48c639b08..7c27752f7 100644
--- a/Jellyfin.Api/Controllers/YearsController.cs
+++ b/Jellyfin.Api/Controllers/YearsController.cs
@@ -7,6 +7,7 @@ using Jellyfin.Api.Extensions;
using Jellyfin.Api.Helpers;
using Jellyfin.Api.ModelBinders;
using Jellyfin.Data.Entities;
+using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
@@ -70,13 +71,13 @@ namespace Jellyfin.Api.Controllers
public ActionResult<QueryResult<BaseItemDto>> GetYears(
[FromQuery] int? startIndex,
[FromQuery] int? limit,
- [FromQuery] string? sortOrder,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] SortOrder[] sortOrder,
[FromQuery] Guid? parentId,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] excludeItemTypes,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] includeItemTypes,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] mediaTypes,
- [FromQuery] string? sortBy,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] sortBy,
[FromQuery] bool? enableUserData,
[FromQuery] int? imageTypeLimit,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes,
diff --git a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs
index a4da54cfd..92ff42b49 100644
--- a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs
+++ b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs
@@ -427,7 +427,7 @@ namespace Jellyfin.Api.Helpers
if (framerate.HasValue)
{
builder.Append(",FRAME-RATE=")
- .Append(framerate.Value);
+ .Append(framerate.Value.ToString(CultureInfo.InvariantCulture));
}
}
diff --git a/Jellyfin.Api/Helpers/RequestHelpers.cs b/Jellyfin.Api/Helpers/RequestHelpers.cs
index efce11f8a..db0ccc657 100644
--- a/Jellyfin.Api/Helpers/RequestHelpers.cs
+++ b/Jellyfin.Api/Helpers/RequestHelpers.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Linq;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
@@ -8,7 +9,6 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
using Microsoft.AspNetCore.Http;
@@ -25,60 +25,33 @@ namespace Jellyfin.Api.Helpers
/// <param name="sortBy">Sort By. Comma delimited string.</param>
/// <param name="requestedSortOrder">Sort Order. Comma delimited string.</param>
/// <returns>Order By.</returns>
- public static ValueTuple<string, SortOrder>[] GetOrderBy(string? sortBy, string? requestedSortOrder)
+ public static (string, SortOrder)[] GetOrderBy(IReadOnlyList<string> sortBy, IReadOnlyList<SortOrder> requestedSortOrder)
{
- var val = sortBy;
-
- if (string.IsNullOrEmpty(val))
+ if (sortBy.Count == 0)
{
return Array.Empty<ValueTuple<string, SortOrder>>();
}
- var vals = val.Split(',');
- if (string.IsNullOrWhiteSpace(requestedSortOrder))
+ var result = new (string, SortOrder)[sortBy.Count];
+ var i = 0;
+ // Add elements which have a SortOrder specified
+ for (; i < requestedSortOrder.Count; i++)
{
- requestedSortOrder = "Ascending";
+ result[i] = (sortBy[i], requestedSortOrder[i]);
}
- var sortOrders = requestedSortOrder.Split(',');
-
- var result = new ValueTuple<string, SortOrder>[vals.Length];
-
- for (var i = 0; i < vals.Length; i++)
+ // Add remaining elements with the first specified SortOrder
+ // or the default one if no SortOrders are specified
+ var order = requestedSortOrder.Count > 0 ? requestedSortOrder[0] : SortOrder.Ascending;
+ for (; i < sortBy.Count; i++)
{
- var sortOrderIndex = sortOrders.Length > i ? i : 0;
-
- var sortOrderValue = sortOrders.Length > sortOrderIndex ? sortOrders[sortOrderIndex] : null;
- var sortOrder = string.Equals(sortOrderValue, "Descending", StringComparison.OrdinalIgnoreCase)
- ? SortOrder.Descending
- : SortOrder.Ascending;
-
- result[i] = new ValueTuple<string, SortOrder>(vals[i], sortOrder);
+ result[i] = (sortBy[i], order);
}
return result;
}
/// <summary>
- /// Splits a string at a separating character into an array of substrings.
- /// </summary>
- /// <param name="value">The string to split.</param>
- /// <param name="separator">The char that separates the substrings.</param>
- /// <param name="removeEmpty">Option to remove empty substrings from the array.</param>
- /// <returns>An array of the substrings.</returns>
- internal static string[] Split(string? value, char separator, bool removeEmpty)
- {
- if (string.IsNullOrWhiteSpace(value))
- {
- return Array.Empty<string>();
- }
-
- return removeEmpty
- ? value.Split(separator, StringSplitOptions.RemoveEmptyEntries)
- : value.Split(separator);
- }
-
- /// <summary>
/// Checks if the user can update an entry.
/// </summary>
/// <param name="authContext">Instance of the <see cref="IAuthorizationContext"/> interface.</param>
diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj
index f01f50cea..8437369b2 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.1" />
+ <PackageReference Include="Microsoft.AspNetCore.Authorization" Version="5.0.2" />
<PackageReference Include="Microsoft.Extensions.Http" Version="5.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.6.3" />
<PackageReference Include="Swashbuckle.AspNetCore.ReDoc" Version="5.6.3" />
diff --git a/Jellyfin.Api/Models/LiveTvDtos/GetProgramsDto.cs b/Jellyfin.Api/Models/LiveTvDtos/GetProgramsDto.cs
index a47ae926c..588ce717c 100644
--- a/Jellyfin.Api/Models/LiveTvDtos/GetProgramsDto.cs
+++ b/Jellyfin.Api/Models/LiveTvDtos/GetProgramsDto.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Text.Json.Serialization;
+using Jellyfin.Data.Enums;
using MediaBrowser.Common.Json.Converters;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
@@ -106,12 +107,14 @@ namespace Jellyfin.Api.Models.LiveTvDtos
/// Gets or sets specify one or more sort orders, comma delimited. Options: Name, StartDate.
/// Optional.
/// </summary>
- public string? SortBy { get; set; }
+ [JsonConverter(typeof(JsonCommaDelimitedArrayConverterFactory))]
+ public IReadOnlyList<string> SortBy { get; set; } = Array.Empty<string>();
/// <summary>
/// Gets or sets sort Order - Ascending,Descending.
/// </summary>
- public string? SortOrder { get; set; }
+ [JsonConverter(typeof(JsonCommaDelimitedArrayConverterFactory))]
+ public IReadOnlyList<SortOrder> SortOrder { get; set; } = Array.Empty<SortOrder>();
/// <summary>
/// Gets or sets the genres to return guide information for.
diff --git a/Jellyfin.Data/Jellyfin.Data.csproj b/Jellyfin.Data/Jellyfin.Data.csproj
index 4fb5594d4..b95879de4 100644
--- a/Jellyfin.Data/Jellyfin.Data.csproj
+++ b/Jellyfin.Data/Jellyfin.Data.csproj
@@ -41,8 +41,8 @@
</ItemGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="5.0.1" />
- <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.1" />
+ <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="5.0.2" />
+ <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.2" />
</ItemGroup>
<ItemGroup>
diff --git a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj
index 9e4a2065f..05052e5c0 100644
--- a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj
+++ b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj
@@ -26,11 +26,11 @@
<ItemGroup>
<PackageReference Include="System.Linq.Async" Version="5.0.0" />
- <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.1">
+ <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
- <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="5.0.1">
+ <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="5.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
diff --git a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs
index 4f65a31e0..da4cc267b 100644
--- a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs
+++ b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs
@@ -1,13 +1,10 @@
using System;
using System.Collections.Generic;
-using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Text;
using Emby.Server.Implementations;
using Jellyfin.Api.Auth;
using Jellyfin.Api.Auth.DefaultAuthorizationPolicy;
@@ -25,7 +22,6 @@ using Jellyfin.Api.Controllers;
using Jellyfin.Api.ModelBinders;
using Jellyfin.Data.Enums;
using Jellyfin.Networking.Configuration;
-using Jellyfin.Networking.Manager;
using Jellyfin.Server.Configuration;
using Jellyfin.Server.Filters;
using Jellyfin.Server.Formatters;
@@ -312,11 +308,15 @@ namespace Jellyfin.Server.Extensions
?? null;
});
+ // Allow parameters to properly be nullable.
+ c.UseAllOfToExtendReferenceSchemas();
+
// TODO - remove when all types are supported in System.Text.Json
c.AddSwaggerTypeMappings();
c.OperationFilter<SecurityRequirementsOperationFilter>();
c.OperationFilter<FileResponseFilter>();
+ c.OperationFilter<ParameterObsoleteFilter>();
c.DocumentFilter<WebsocketModelFilter>();
});
}
diff --git a/Jellyfin.Server/Filters/ParameterObsoleteFilter.cs b/Jellyfin.Server/Filters/ParameterObsoleteFilter.cs
new file mode 100644
index 000000000..e54044d0e
--- /dev/null
+++ b/Jellyfin.Server/Filters/ParameterObsoleteFilter.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Linq;
+using Jellyfin.Api.Attributes;
+using Microsoft.AspNetCore.Mvc.ApiExplorer;
+using Microsoft.OpenApi.Models;
+using Swashbuckle.AspNetCore.SwaggerGen;
+
+namespace Jellyfin.Server.Filters
+{
+ /// <summary>
+ /// Mark parameter as deprecated if it has the <see cref="ParameterObsoleteAttribute"/>.
+ /// </summary>
+ public class ParameterObsoleteFilter : IOperationFilter
+ {
+ /// <inheritdoc />
+ public void Apply(OpenApiOperation operation, OperationFilterContext context)
+ {
+ foreach (var parameterDescription in context.ApiDescription.ParameterDescriptions)
+ {
+ if (parameterDescription
+ .CustomAttributes()
+ .OfType<ParameterObsoleteAttribute>()
+ .Any())
+ {
+ foreach (var parameter in operation.Parameters)
+ {
+ if (parameter.Name.Equals(parameterDescription.Name, StringComparison.Ordinal))
+ {
+ parameter.Deprecated = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj
index 5940cf938..3ebcc3279 100644
--- a/Jellyfin.Server/Jellyfin.Server.csproj
+++ b/Jellyfin.Server/Jellyfin.Server.csproj
@@ -40,8 +40,8 @@
<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.1" />
- <PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="5.0.1" />
+ <PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="5.0.2" />
+ <PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="5.0.2" />
<PackageReference Include="prometheus-net" Version="4.1.1" />
<PackageReference Include="prometheus-net.AspNetCore" Version="4.1.1" />
<PackageReference Include="Serilog.AspNetCore" Version="3.4.0" />
diff --git a/MediaBrowser.Common/Extensions/ShuffleExtensions.cs b/MediaBrowser.Common/Extensions/ShuffleExtensions.cs
index 459bec110..6f0ea9bd5 100644
--- a/MediaBrowser.Common/Extensions/ShuffleExtensions.cs
+++ b/MediaBrowser.Common/Extensions/ShuffleExtensions.cs
@@ -33,8 +33,7 @@ namespace MediaBrowser.Common.Extensions
int n = list.Count;
while (n > 1)
{
- n--;
- int k = rng.Next(n + 1);
+ int k = rng.Next(n--);
T value = list[k];
list[k] = list[n];
list[n] = value;
diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.nuget.targets b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.nuget.targets
deleted file mode 100644
index f793e09bc..000000000
--- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.nuget.targets
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="utf-8" standalone="no"?>
-<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <Target Name="EmitMSBuildWarning" BeforeTargets="Build">
- <Warning Text="Packages containing MSBuild targets and props files cannot be fully installed in projects targeting multiple frameworks. The MSBuild targets and props files have been ignored." />
- </Target>
-</Project> \ No newline at end of file
diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj
index c271a9cf8..e5166672f 100644
--- a/MediaBrowser.Model/MediaBrowser.Model.csproj
+++ b/MediaBrowser.Model/MediaBrowser.Model.csproj
@@ -35,7 +35,7 @@
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0" />
<PackageReference Include="System.Globalization" Version="4.3.0" />
- <PackageReference Include="System.Text.Json" Version="5.0.0" />
+ <PackageReference Include="System.Text.Json" Version="5.0.1" />
</ItemGroup>
<ItemGroup>
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs
index 6ca462474..4c1f69763 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs
@@ -54,7 +54,6 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
result.HasMetadata = true;
result.Item = new Season
{
- Name = info.Name,
IndexNumber = seasonNumber,
Overview = seasonResult?.Overview
};
diff --git a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs
index 1adc5029d..9f22e618e 100644
--- a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs
+++ b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs
@@ -203,10 +203,11 @@ namespace MediaBrowser.XbmcMetadata.Savers
var directory = Path.GetDirectoryName(path) ?? throw new ArgumentException($"Provided path ({path}) is not valid.", nameof(path));
Directory.CreateDirectory(directory);
- // On Windows, savint the file will fail if the file is hidden or readonly
+ // On Windows, saving the file will fail if the file is hidden or readonly
FileSystem.SetAttributes(path, false, false);
- using (var filestream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read))
+ // use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
+ using (var filestream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None))
{
stream.CopyTo(filestream);
}
@@ -450,15 +451,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
writer.WriteElementString("plot", overview);
}
- if (item is Video)
- {
- var outline = (item.Tagline ?? string.Empty)
- .StripHtml()
- .Replace("&quot;", "'", StringComparison.Ordinal);
-
- writer.WriteElementString("outline", outline);
- }
- else
+ if (item is not Video)
{
writer.WriteElementString("outline", overview);
}
diff --git a/MediaBrowser.sln.GhostDoc.xml b/MediaBrowser.sln.GhostDoc.xml
deleted file mode 100644
index eafee0bf5..000000000
--- a/MediaBrowser.sln.GhostDoc.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<GhostDoc>
- <SpellChecker>
- <IncludeExtensions>
- </IncludeExtensions>
- <IgnoreExtensions>
- </IgnoreExtensions>
- <IgnoreFiles>
- </IgnoreFiles>
- </SpellChecker>
- <HelpConfigurations selected="HelpFile">
- <HelpConfiguration name="HelpFile">
- <OutputPath>D:\Development\MediaBrowser\Help</OutputPath>
- <ImageFolderPath />
- <HtmlFormats>
- <HtmlHelp>true</HtmlHelp>
- <MSHelpViewer>false</MSHelpViewer>
- <MSHelp2>false</MSHelp2>
- <Website>false</Website>
- </HtmlFormats>
- <IncludeScopes>
- <Public>true</Public>
- <Internal>false</Internal>
- <Protected>false</Protected>
- <Private>false</Private>
- <Inherited>true</Inherited>
- <EnableTags>false</EnableTags>
- <TagList />
- </IncludeScopes>
- <ResolveCrefLinks>true</ResolveCrefLinks>
- <HeaderText />
- <FooterText />
- <SelectedProjects />
- </HelpConfiguration>
- </HelpConfigurations>
-</GhostDoc>
diff --git a/deployment/Dockerfile.debian.amd64 b/deployment/Dockerfile.debian.amd64
index d2f98ca82..f5cf232d6 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/a0487784-534a-4912-a4dd-017382083865/be16057043a8f7b6f08c902dc48dd677/dotnet-sdk-5.0.101-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/7f736160-9f34-4595-8d72-13630c437aef/b9c4513afb0f8872eb95793c70ac52f6/dotnet-sdk-5.0.102-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 ffc94e088..d9414a610 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/a0487784-534a-4912-a4dd-017382083865/be16057043a8f7b6f08c902dc48dd677/dotnet-sdk-5.0.101-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/7f736160-9f34-4595-8d72-13630c437aef/b9c4513afb0f8872eb95793c70ac52f6/dotnet-sdk-5.0.102-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 b25f59329..7f2275aaa 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/a0487784-534a-4912-a4dd-017382083865/be16057043a8f7b6f08c902dc48dd677/dotnet-sdk-5.0.101-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/7f736160-9f34-4595-8d72-13630c437aef/b9c4513afb0f8872eb95793c70ac52f6/dotnet-sdk-5.0.102-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 2e993c25d..54d75dcbe 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/a0487784-534a-4912-a4dd-017382083865/be16057043a8f7b6f08c902dc48dd677/dotnet-sdk-5.0.101-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/7f736160-9f34-4595-8d72-13630c437aef/b9c4513afb0f8872eb95793c70ac52f6/dotnet-sdk-5.0.102-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 08b4ffa52..e4c724219 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/a0487784-534a-4912-a4dd-017382083865/be16057043a8f7b6f08c902dc48dd677/dotnet-sdk-5.0.101-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/7f736160-9f34-4595-8d72-13630c437aef/b9c4513afb0f8872eb95793c70ac52f6/dotnet-sdk-5.0.102-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 b8499c917..633802598 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/a0487784-534a-4912-a4dd-017382083865/be16057043a8f7b6f08c902dc48dd677/dotnet-sdk-5.0.101-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/7f736160-9f34-4595-8d72-13630c437aef/b9c4513afb0f8872eb95793c70ac52f6/dotnet-sdk-5.0.102-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 80c4d1469..ec0b015cc 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/a0487784-534a-4912-a4dd-017382083865/be16057043a8f7b6f08c902dc48dd677/dotnet-sdk-5.0.101-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/7f736160-9f34-4595-8d72-13630c437aef/b9c4513afb0f8872eb95793c70ac52f6/dotnet-sdk-5.0.102-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 f2bbe7f24..25f15be18 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/a0487784-534a-4912-a4dd-017382083865/be16057043a8f7b6f08c902dc48dd677/dotnet-sdk-5.0.101-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/7f736160-9f34-4595-8d72-13630c437aef/b9c4513afb0f8872eb95793c70ac52f6/dotnet-sdk-5.0.102-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 603becedf..cd71ce9d4 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/a0487784-534a-4912-a4dd-017382083865/be16057043a8f7b6f08c902dc48dd677/dotnet-sdk-5.0.101-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/7f736160-9f34-4595-8d72-13630c437aef/b9c4513afb0f8872eb95793c70ac52f6/dotnet-sdk-5.0.102-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 a6c7cc5d4..ea539b360 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/a0487784-534a-4912-a4dd-017382083865/be16057043a8f7b6f08c902dc48dd677/dotnet-sdk-5.0.101-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/7f736160-9f34-4595-8d72-13630c437aef/b9c4513afb0f8872eb95793c70ac52f6/dotnet-sdk-5.0.102-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 3a8005816..f2f5368f7 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/a0487784-534a-4912-a4dd-017382083865/be16057043a8f7b6f08c902dc48dd677/dotnet-sdk-5.0.101-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/7f736160-9f34-4595-8d72-13630c437aef/b9c4513afb0f8872eb95793c70ac52f6/dotnet-sdk-5.0.102-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 22b9e7ea8..ba597801b 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/a0487784-534a-4912-a4dd-017382083865/be16057043a8f7b6f08c902dc48dd677/dotnet-sdk-5.0.101-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/7f736160-9f34-4595-8d72-13630c437aef/b9c4513afb0f8872eb95793c70ac52f6/dotnet-sdk-5.0.102-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 b1ca61053..c73126841 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/a0487784-534a-4912-a4dd-017382083865/be16057043a8f7b6f08c902dc48dd677/dotnet-sdk-5.0.101-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/7f736160-9f34-4595-8d72-13630c437aef/b9c4513afb0f8872eb95793c70ac52f6/dotnet-sdk-5.0.102-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/hooks/pre_build b/hooks/pre_build
deleted file mode 100644
index 2fd6136c5..000000000
--- a/hooks/pre_build
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/bin/bash
-git submodule update --init --recursive
-
-# Register qemu-*-static for all supported processors except the
-# current one, but also remove all registered binfmt_misc before
-docker run --rm --privileged multiarch/qemu-user-static:register --reset
diff --git a/tests/Jellyfin.Api.Tests/Helpers/RequestHelpersTests.cs b/tests/Jellyfin.Api.Tests/Helpers/RequestHelpersTests.cs
new file mode 100644
index 000000000..606041c7f
--- /dev/null
+++ b/tests/Jellyfin.Api.Tests/Helpers/RequestHelpersTests.cs
@@ -0,0 +1,59 @@
+using System;
+using System.Collections.Generic;
+using Jellyfin.Api.Helpers;
+using Jellyfin.Data.Enums;
+using Xunit;
+
+namespace Jellyfin.Api.Tests.Helpers
+{
+ public class RequestHelpersTests
+ {
+ [Theory]
+ [MemberData(nameof(GetOrderBy_Success_TestData))]
+ public void GetOrderBy_Success(IReadOnlyList<string> sortBy, IReadOnlyList<SortOrder> requestedSortOrder, (string, SortOrder)[] expected)
+ {
+ Assert.Equal(expected, RequestHelpers.GetOrderBy(sortBy, requestedSortOrder));
+ }
+
+ public static IEnumerable<object[]> GetOrderBy_Success_TestData()
+ {
+ yield return new object[]
+ {
+ Array.Empty<string>(),
+ Array.Empty<SortOrder>(),
+ Array.Empty<(string, SortOrder)>()
+ };
+ yield return new object[]
+ {
+ new string[]
+ {
+ "IsFavoriteOrLiked",
+ "Random"
+ },
+ Array.Empty<SortOrder>(),
+ new (string, SortOrder)[]
+ {
+ ("IsFavoriteOrLiked", SortOrder.Ascending),
+ ("Random", SortOrder.Ascending),
+ }
+ };
+ yield return new object[]
+ {
+ new string[]
+ {
+ "SortName",
+ "ProductionYear"
+ },
+ new SortOrder[]
+ {
+ SortOrder.Descending
+ },
+ new (string, SortOrder)[]
+ {
+ ("SortName", SortOrder.Descending),
+ ("ProductionYear", SortOrder.Descending),
+ }
+ };
+ }
+ }
+}
diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
index c58813617..3da728c63 100644
--- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
+++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
@@ -16,12 +16,12 @@
<PackageReference Include="AutoFixture" Version="4.15.0" />
<PackageReference Include="AutoFixture.AutoMoq" Version="4.15.0" />
<PackageReference Include="AutoFixture.Xunit2" Version="4.15.0" />
- <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="5.0.1" />
+ <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="5.0.2" />
<PackageReference Include="Microsoft.Extensions.Options" Version="5.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
- <PackageReference Include="coverlet.collector" Version="3.0.1" />
+ <PackageReference Include="coverlet.collector" Version="3.0.2" />
<PackageReference Include="Moq" Version="4.16.0" />
</ItemGroup>
diff --git a/tests/Jellyfin.Common.Tests/Extensions/ShuffleExtensionsTests.cs b/tests/Jellyfin.Common.Tests/Extensions/ShuffleExtensionsTests.cs
new file mode 100644
index 000000000..cbdbcf112
--- /dev/null
+++ b/tests/Jellyfin.Common.Tests/Extensions/ShuffleExtensionsTests.cs
@@ -0,0 +1,22 @@
+using System;
+using MediaBrowser.Common.Extensions;
+using Xunit;
+
+namespace Jellyfin.Common.Tests.Extensions
+{
+ public static class ShuffleExtensionsTests
+ {
+ private static readonly Random _rng = new Random();
+
+ [Fact]
+ public static void Shuffle_Valid_Correct()
+ {
+ byte[] original = new byte[1 << 6];
+ _rng.NextBytes(original);
+ byte[] shuffled = (byte[])original.Clone();
+ shuffled.Shuffle();
+
+ Assert.NotEqual(original, shuffled);
+ }
+ }
+}
diff --git a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj
index 7ff4d0e87..57edbf902 100644
--- a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj
+++ b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj
@@ -16,7 +16,7 @@
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
- <PackageReference Include="coverlet.collector" Version="3.0.1" />
+ <PackageReference Include="coverlet.collector" Version="3.0.2" />
</ItemGroup>
<!-- Code Analyzers -->
diff --git a/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj b/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj
index af412eed3..c766c5445 100644
--- a/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj
+++ b/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj
@@ -16,7 +16,7 @@
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
- <PackageReference Include="coverlet.collector" Version="3.0.1" />
+ <PackageReference Include="coverlet.collector" Version="3.0.2" />
</ItemGroup>
<!-- Code Analyzers -->
diff --git a/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj b/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj
index 21389b926..52a9e1193 100644
--- a/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj
+++ b/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj
@@ -11,7 +11,7 @@
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
- <PackageReference Include="coverlet.collector" Version="3.0.1" />
+ <PackageReference Include="coverlet.collector" Version="3.0.2" />
</ItemGroup>
<!-- Code Analyzers -->
diff --git a/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj b/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj
index 9a1cfcfa4..24f6fb356 100644
--- a/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj
+++ b/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj
@@ -22,7 +22,7 @@
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
- <PackageReference Include="coverlet.collector" Version="3.0.1" />
+ <PackageReference Include="coverlet.collector" Version="3.0.2" />
</ItemGroup>
<!-- Code Analyzers -->
diff --git a/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj b/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj
index 31e41b12f..a4d5c0d6f 100644
--- a/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj
+++ b/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj
@@ -16,7 +16,7 @@
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
- <PackageReference Include="coverlet.collector" Version="3.0.1" />
+ <PackageReference Include="coverlet.collector" Version="3.0.2" />
</ItemGroup>
<ItemGroup>
diff --git a/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs b/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs
index 9df6904ef..bc5e6fa63 100644
--- a/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs
+++ b/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs
@@ -9,9 +9,8 @@ namespace Jellyfin.Naming.Tests.Video
{
public class MultiVersionTests
{
- private readonly NamingOptions _namingOptions = new NamingOptions();
+ private readonly VideoListResolver _videoListResolver = new VideoListResolver(new NamingOptions());
- // FIXME
[Fact]
public void TestMultiEdition1()
{
@@ -23,9 +22,7 @@ namespace Jellyfin.Naming.Tests.Video
@"/movies/X-Men Days of Future Past/X-Men Days of Future Past [hsbs].mkv"
};
- var resolver = GetResolver();
-
- var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
{
IsDirectory = false,
FullName = i
@@ -35,7 +32,6 @@ namespace Jellyfin.Naming.Tests.Video
Assert.Single(result[0].Extras);
}
- // FIXME
[Fact]
public void TestMultiEdition2()
{
@@ -47,9 +43,7 @@ namespace Jellyfin.Naming.Tests.Video
@"/movies/X-Men Days of Future Past/X-Men Days of Future Past [banana].mp4"
};
- var resolver = GetResolver();
-
- var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
{
IsDirectory = false,
FullName = i
@@ -69,9 +63,7 @@ namespace Jellyfin.Naming.Tests.Video
@"/movies/The Phantom of the Opera (1925)/The Phantom of the Opera (1925) - 1929 version.mkv"
};
- var resolver = GetResolver();
-
- var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
{
IsDirectory = false,
FullName = i
@@ -81,7 +73,6 @@ namespace Jellyfin.Naming.Tests.Video
Assert.Single(result[0].AlternateVersions);
}
- // FIXME
[Fact]
public void TestLetterFolders()
{
@@ -96,9 +87,7 @@ namespace Jellyfin.Naming.Tests.Video
@"/movies/M/Movie 7.mkv"
};
- var resolver = GetResolver();
-
- var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
{
IsDirectory = false,
FullName = i
@@ -109,7 +98,6 @@ namespace Jellyfin.Naming.Tests.Video
Assert.Empty(result[0].AlternateVersions);
}
- // FIXME
[Fact]
public void TestMultiVersionLimit()
{
@@ -125,9 +113,7 @@ namespace Jellyfin.Naming.Tests.Video
@"/movies/Movie/Movie-8.mkv"
};
- var resolver = GetResolver();
-
- var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
{
IsDirectory = false,
FullName = i
@@ -138,7 +124,6 @@ namespace Jellyfin.Naming.Tests.Video
Assert.Equal(7, result[0].AlternateVersions.Count);
}
- // FIXME
[Fact]
public void TestMultiVersionLimit2()
{
@@ -155,9 +140,7 @@ namespace Jellyfin.Naming.Tests.Video
@"/movies/Mo/Movie 9.mkv"
};
- var resolver = GetResolver();
-
- var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
{
IsDirectory = false,
FullName = i
@@ -168,7 +151,6 @@ namespace Jellyfin.Naming.Tests.Video
Assert.Empty(result[0].AlternateVersions);
}
- // FIXME
[Fact]
public void TestMultiVersion3()
{
@@ -181,9 +163,7 @@ namespace Jellyfin.Naming.Tests.Video
@"/movies/Movie/Movie 5.mkv"
};
- var resolver = GetResolver();
-
- var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
{
IsDirectory = false,
FullName = i
@@ -194,7 +174,6 @@ namespace Jellyfin.Naming.Tests.Video
Assert.Empty(result[0].AlternateVersions);
}
- // FIXME
[Fact]
public void TestMultiVersion4()
{
@@ -209,9 +188,7 @@ namespace Jellyfin.Naming.Tests.Video
@"/movies/Iron Man/Iron Man (2011).mkv"
};
- var resolver = GetResolver();
-
- var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
{
IsDirectory = false,
FullName = i
@@ -237,9 +214,7 @@ namespace Jellyfin.Naming.Tests.Video
@"/movies/Iron Man/Iron Man[test].mkv",
};
- var resolver = GetResolver();
-
- var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
{
IsDirectory = false,
FullName = i
@@ -253,7 +228,6 @@ namespace Jellyfin.Naming.Tests.Video
Assert.True(result[0].AlternateVersions[4].Is3D);
}
- // FIXME
[Fact]
public void TestMultiVersion6()
{
@@ -269,9 +243,7 @@ namespace Jellyfin.Naming.Tests.Video
@"/movies/Iron Man/Iron Man [test].mkv"
};
- var resolver = GetResolver();
-
- var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
{
IsDirectory = false,
FullName = i
@@ -294,9 +266,7 @@ namespace Jellyfin.Naming.Tests.Video
@"/movies/Iron Man/Iron Man - C (2007).mkv"
};
- var resolver = GetResolver();
-
- var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
{
IsDirectory = false,
FullName = i
@@ -319,9 +289,7 @@ namespace Jellyfin.Naming.Tests.Video
@"/movies/Iron Man/Iron Man_3d.hsbs.mkv"
};
- var resolver = GetResolver();
-
- var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
{
IsDirectory = false,
FullName = i
@@ -349,9 +317,7 @@ namespace Jellyfin.Naming.Tests.Video
@"/movies/Iron Man/Iron Man (2011).mkv"
};
- var resolver = GetResolver();
-
- var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
{
IsDirectory = false,
FullName = i
@@ -371,9 +337,7 @@ namespace Jellyfin.Naming.Tests.Video
@"/movies/Blade Runner (1982)/Blade Runner (1982) [EE by ADM] [480p HEVC AAC,AAC,AAC].mkv"
};
- var resolver = GetResolver();
-
- var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
{
IsDirectory = false,
FullName = i
@@ -393,9 +357,7 @@ namespace Jellyfin.Naming.Tests.Video
@"/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016) [2160p] Blu-ray.x265.AAC.mkv"
};
- var resolver = GetResolver();
-
- var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
{
IsDirectory = false,
FullName = i
@@ -409,16 +371,9 @@ namespace Jellyfin.Naming.Tests.Video
[Fact]
public void TestEmptyList()
{
- var resolver = GetResolver();
-
- var result = resolver.Resolve(new List<FileSystemMetadata>()).ToList();
+ var result = _videoListResolver.Resolve(new List<FileSystemMetadata>()).ToList();
Assert.Empty(result);
}
-
- private VideoListResolver GetResolver()
- {
- return new VideoListResolver(_namingOptions);
- }
}
}
diff --git a/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs b/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs
index b6447a7a6..ba5eaf1af 100644
--- a/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs
+++ b/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs
@@ -9,7 +9,7 @@ namespace Jellyfin.Naming.Tests.Video
{
public class VideoResolverTests
{
- private readonly NamingOptions _namingOptions = new NamingOptions();
+ private readonly VideoResolver _videoResolver = new VideoResolver(new NamingOptions());
public static IEnumerable<object[]> GetResolveFileTestData()
{
@@ -159,7 +159,7 @@ namespace Jellyfin.Naming.Tests.Video
[MemberData(nameof(GetResolveFileTestData))]
public void ResolveFile_ValidFileName_Success(VideoFileInfo expectedResult)
{
- var result = new VideoResolver(_namingOptions).ResolveFile(expectedResult.Path);
+ var result = _videoResolver.ResolveFile(expectedResult.Path);
Assert.NotNull(result);
Assert.Equal(result?.Path, expectedResult.Path);
@@ -179,7 +179,7 @@ namespace Jellyfin.Naming.Tests.Video
[Fact]
public void ResolveFile_EmptyPath()
{
- var result = new VideoResolver(_namingOptions).ResolveFile(string.Empty);
+ var result = _videoResolver.ResolveFile(string.Empty);
Assert.Null(result);
}
@@ -194,8 +194,7 @@ namespace Jellyfin.Naming.Tests.Video
string.Empty
};
- var resolver = new VideoResolver(_namingOptions);
- var results = paths.Select(path => resolver.ResolveDirectory(path)).ToList();
+ var results = paths.Select(path => _videoResolver.ResolveDirectory(path)).ToList();
Assert.Equal(3, results.Count);
Assert.NotNull(results[0]);
diff --git a/tests/Jellyfin.Networking.Tests/NetworkTesting/Jellyfin.Networking.Tests.csproj b/tests/Jellyfin.Networking.Tests/NetworkTesting/Jellyfin.Networking.Tests.csproj
index bb2bae8e8..d77645cd9 100644
--- a/tests/Jellyfin.Networking.Tests/NetworkTesting/Jellyfin.Networking.Tests.csproj
+++ b/tests/Jellyfin.Networking.Tests/NetworkTesting/Jellyfin.Networking.Tests.csproj
@@ -16,7 +16,7 @@
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
- <PackageReference Include="coverlet.collector" Version="3.0.1" />
+ <PackageReference Include="coverlet.collector" Version="3.0.2" />
<PackageReference Include="Moq" Version="4.16.0" />
</ItemGroup>
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj
index 7f909847e..469fe01e2 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj
+++ b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj
@@ -20,7 +20,7 @@
<PackageReference Include="Moq" Version="4.16.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
- <PackageReference Include="coverlet.collector" Version="3.0.1" />
+ <PackageReference Include="coverlet.collector" Version="3.0.2" />
</ItemGroup>
<!-- Code Analyzers -->
diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj b/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj
index fe40122a3..2106a78a8 100644
--- a/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj
+++ b/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj
@@ -18,7 +18,7 @@
<PackageReference Include="Moq" Version="4.16.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
- <PackageReference Include="coverlet.collector" Version="3.0.1" />
+ <PackageReference Include="coverlet.collector" Version="3.0.2" />
</ItemGroup>
<!-- Code Analyzers -->