aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/ISSUE_TEMPLATE/issue report.yml2
-rw-r--r--.github/workflows/ci-codeql-analysis.yml6
-rw-r--r--.github/workflows/ci-compat.yml149
-rw-r--r--.github/workflows/ci-openapi.yml4
-rw-r--r--.github/workflows/ci-tests.yml2
-rw-r--r--.github/workflows/commands.yml2
-rw-r--r--.github/workflows/issue-template-check.yml2
-rw-r--r--CONTRIBUTORS.md1
-rw-r--r--Directory.Packages.props14
-rw-r--r--Emby.Naming/Video/FileStackRule.cs2
-rw-r--r--Emby.Server.Implementations/ApplicationHost.cs7
-rw-r--r--Emby.Server.Implementations/ConfigurationOptions.cs3
-rw-r--r--Emby.Server.Implementations/Data/SqliteItemRepository.cs14
-rw-r--r--Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs3
-rw-r--r--Emby.Server.Implementations/Library/LibraryManager.cs4
-rw-r--r--Emby.Server.Implementations/Library/UserDataManager.cs76
-rw-r--r--Emby.Server.Implementations/Library/UserViewManager.cs19
-rw-r--r--Emby.Server.Implementations/Localization/Core/ar.json9
-rw-r--r--Emby.Server.Implementations/Localization/Core/cs.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/da.json6
-rw-r--r--Emby.Server.Implementations/Localization/Core/de.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/en-GB.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/es-AR.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/es.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/es_419.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/fa.json6
-rw-r--r--Emby.Server.Implementations/Localization/Core/fr.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/it.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/kw.json135
-rw-r--r--Emby.Server.Implementations/Localization/Core/nb.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/nl.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/pl.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/ru.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/sv.json6
-rw-r--r--Emby.Server.Implementations/Localization/Core/uk.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/vi.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/zh-CN.json4
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs105
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/TaskManager.cs49
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/AudioNormalizationTask.cs2
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs28
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/CleanActivityLogTask.cs3
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/CleanupCollectionAndPlaylistPathsTask.cs8
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs11
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs17
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs6
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/OptimizeDatabaseTask.cs17
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs18
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs16
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs7
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Triggers/DailyTrigger.cs20
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Triggers/StartupTrigger.cs24
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Triggers/WeeklyTrigger.cs22
-rw-r--r--Emby.Server.Implementations/Session/SessionManager.cs1
-rw-r--r--Emby.Server.Implementations/TV/TVSeriesManager.cs18
-rw-r--r--Jellyfin.Api/Attributes/AcceptsFileAttribute.cs2
-rw-r--r--Jellyfin.Api/Attributes/AcceptsImageFileAttribute.cs2
-rw-r--r--Jellyfin.Api/Attributes/ParameterObsoleteAttribute.cs2
-rw-r--r--Jellyfin.Api/Attributes/ProducesAudioFileAttribute.cs2
-rw-r--r--Jellyfin.Api/Attributes/ProducesFileAttribute.cs2
-rw-r--r--Jellyfin.Api/Attributes/ProducesImageFileAttribute.cs2
-rw-r--r--Jellyfin.Api/Attributes/ProducesPlaylistFileAttribute.cs2
-rw-r--r--Jellyfin.Api/Attributes/ProducesVideoFileAttribute.cs2
-rw-r--r--Jellyfin.Api/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandler.cs2
-rw-r--r--Jellyfin.Api/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationRequirement.cs2
-rw-r--r--Jellyfin.Api/Auth/FirstTimeSetupPolicy/FirstTimeSetupRequirement.cs2
-rw-r--r--Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessHandler.cs2
-rw-r--r--Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessRequirement.cs2
-rw-r--r--Jellyfin.Api/Auth/UserPermissionPolicy/UserPermissionHandler.cs2
-rw-r--r--Jellyfin.Api/Auth/UserPermissionPolicy/UserPermissionRequirement.cs2
-rw-r--r--Jellyfin.Api/Constants/InternalClaimTypes.cs2
-rw-r--r--Jellyfin.Api/Controllers/ClientLogController.cs2
-rw-r--r--Jellyfin.Api/Controllers/DynamicHlsController.cs6
-rw-r--r--Jellyfin.Api/Controllers/ImageController.cs13
-rw-r--r--Jellyfin.Api/Controllers/ItemsController.cs18
-rw-r--r--Jellyfin.Api/Controllers/LiveTvController.cs2
-rw-r--r--Jellyfin.Api/Controllers/TvShowsController.cs13
-rw-r--r--Jellyfin.Api/Controllers/UserController.cs24
-rw-r--r--Jellyfin.Api/Controllers/UserLibraryController.cs2
-rw-r--r--Jellyfin.Api/Controllers/UserViewsController.cs6
-rw-r--r--Jellyfin.Api/Extensions/ClaimsPrincipalExtensions.cs2
-rw-r--r--Jellyfin.Api/Extensions/DtoExtensions.cs2
-rw-r--r--Jellyfin.Api/Formatters/CssOutputFormatter.cs2
-rw-r--r--Jellyfin.Api/Formatters/XmlOutputFormatter.cs2
-rw-r--r--Jellyfin.Api/Helpers/AudioHelper.cs2
-rw-r--r--Jellyfin.Api/Helpers/DynamicHlsHelper.cs11
-rw-r--r--Jellyfin.Api/Helpers/HlsCodecStringHelpers.cs6
-rw-r--r--Jellyfin.Api/Helpers/HlsHelpers.cs2
-rw-r--r--Jellyfin.Api/Helpers/MediaInfoHelper.cs2
-rw-r--r--Jellyfin.Api/Helpers/RequestHelpers.cs15
-rw-r--r--Jellyfin.Api/Middleware/LegacyEmbyRouteRewriteMiddleware.cs2
-rw-r--r--Jellyfin.Api/Middleware/ResponseTimeMiddleware.cs2
-rw-r--r--Jellyfin.Api/Middleware/RobotsRedirectionMiddleware.cs2
-rw-r--r--Jellyfin.Api/ModelBinders/LegacyDateTimeModelBinder.cs2
-rw-r--r--Jellyfin.Api/ModelBinders/NullableEnumModelBinder.cs2
-rw-r--r--Jellyfin.Api/ModelBinders/NullableEnumModelBinderProvider.cs2
-rw-r--r--Jellyfin.Api/Models/LibraryDtos/LibraryOptionInfoDto.cs2
-rw-r--r--Jellyfin.Api/Models/LibraryDtos/LibraryOptionsResultDto.cs2
-rw-r--r--Jellyfin.Api/Models/LibraryDtos/LibraryTypeOptionsDto.cs2
-rw-r--r--Jellyfin.Api/Models/LibraryDtos/MediaUpdateInfoDto.cs2
-rw-r--r--Jellyfin.Api/Models/LibraryDtos/MediaUpdateInfoPathDto.cs2
-rw-r--r--Jellyfin.Api/Models/LibraryStructureDto/AddVirtualFolderDto.cs2
-rw-r--r--Jellyfin.Api/Models/LibraryStructureDto/MediaPathDto.cs2
-rw-r--r--Jellyfin.Api/Models/LibraryStructureDto/UpdateLibraryOptionsDto.cs2
-rw-r--r--Jellyfin.Api/Models/LibraryStructureDto/UpdateMediaPathRequestDto.cs2
-rw-r--r--Jellyfin.Api/Models/LiveTvDtos/GetProgramsDto.cs2
-rw-r--r--Jellyfin.Api/Models/LiveTvDtos/SetChannelMappingDto.cs2
-rw-r--r--Jellyfin.Api/Models/MediaInfoDtos/OpenLiveStreamDto.cs2
-rw-r--r--Jellyfin.Api/Models/MediaInfoDtos/PlaybackInfoDto.cs2
-rw-r--r--Jellyfin.Api/Models/PlaylistDtos/CreatePlaylistDto.cs2
-rw-r--r--Jellyfin.Api/Models/SessionDtos/ClientCapabilitiesDto.cs2
-rw-r--r--Jellyfin.Api/Models/StartupDtos/StartupRemoteAccessDto.cs2
-rw-r--r--Jellyfin.Api/Models/StreamingDtos/HlsAudioRequestDto.cs2
-rw-r--r--Jellyfin.Api/Models/StreamingDtos/HlsVideoRequestDto.cs2
-rw-r--r--Jellyfin.Api/Models/SubtitleDtos/UploadSubtitleDto.cs2
-rw-r--r--Jellyfin.Api/Models/UserDtos/AuthenticateUserByName.cs2
-rw-r--r--Jellyfin.Api/Models/UserDtos/CreateUserByName.cs2
-rw-r--r--Jellyfin.Api/Models/UserDtos/ForgotPasswordDto.cs2
-rw-r--r--Jellyfin.Api/Models/UserDtos/ForgotPasswordPinDto.cs2
-rw-r--r--Jellyfin.Api/Models/UserDtos/QuickConnectDto.cs2
-rw-r--r--Jellyfin.Api/Models/UserDtos/UpdateUserEasyPassword.cs2
-rw-r--r--Jellyfin.Api/Models/UserDtos/UpdateUserPassword.cs2
-rw-r--r--Jellyfin.Api/Models/UserViewDtos/SpecialViewOptionDto.cs2
-rw-r--r--Jellyfin.Data/Entities/AccessSchedule.cs2
-rw-r--r--Jellyfin.Data/Entities/CustomItemDisplayPreferences.cs2
-rw-r--r--Jellyfin.Data/Entities/DisplayPreferences.cs2
-rw-r--r--Jellyfin.Data/Entities/HomeSection.cs2
-rw-r--r--Jellyfin.Data/Entities/ImageInfo.cs2
-rw-r--r--Jellyfin.Data/Entities/ItemDisplayPreferences.cs2
-rw-r--r--Jellyfin.Data/Entities/Security/ApiKey.cs2
-rw-r--r--Jellyfin.Data/Enums/BaseItemKind.cs2
-rw-r--r--Jellyfin.Data/Enums/ChromecastVersion.cs2
-rw-r--r--Jellyfin.Data/Enums/HomeSectionType.cs2
-rw-r--r--Jellyfin.Data/Enums/IndexingKind.cs2
-rw-r--r--Jellyfin.Data/Enums/MediaType.cs2
-rw-r--r--Jellyfin.Data/Enums/ScrollDirection.cs2
-rw-r--r--Jellyfin.Data/Enums/SortOrder.cs2
-rw-r--r--Jellyfin.Data/Enums/SubtitlePlaybackMode.cs2
-rw-r--r--Jellyfin.Data/Events/System/PendingRestartEventArgs.cs2
-rw-r--r--Jellyfin.Data/Events/Users/UserCreatedEventArgs.cs2
-rw-r--r--Jellyfin.Data/Events/Users/UserDeletedEventArgs.cs2
-rw-r--r--Jellyfin.Data/Events/Users/UserLockedOutEventArgs.cs2
-rw-r--r--Jellyfin.Data/Events/Users/UserPasswordChangedEventArgs.cs2
-rw-r--r--Jellyfin.Data/Events/Users/UserUpdatedEventArgs.cs2
-rw-r--r--Jellyfin.Data/Interfaces/IHasPermissions.cs2
-rw-r--r--Jellyfin.Data/Jellyfin.Data.csproj2
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Library/LyricDownloadFailureLogger.cs2
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Library/SubtitleDownloadFailureLogger.cs2
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationFailedLogger.cs2
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationSucceededLogger.cs2
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStartLogger.cs2
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStopLogger.cs2
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Session/SessionEndedLogger.cs2
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Session/SessionStartedLogger.cs2
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/System/PendingRestartNotifier.cs2
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/System/TaskCompletedLogger.cs2
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/System/TaskCompletedNotifier.cs2
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationCancelledNotifier.cs2
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationFailedLogger.cs2
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationFailedNotifier.cs2
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstalledLogger.cs2
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstalledNotifier.cs2
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallingNotifier.cs2
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUninstalledLogger.cs2
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUninstalledNotifier.cs2
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUpdatedLogger.cs2
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Users/UserCreatedLogger.cs2
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Users/UserDeletedLogger.cs2
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Users/UserDeletedNotifier.cs2
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Users/UserLockedOutLogger.cs2
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Users/UserPasswordChangedLogger.cs2
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Users/UserUpdatedNotifier.cs2
-rw-r--r--Jellyfin.Server.Implementations/Events/EventManager.cs2
-rw-r--r--Jellyfin.Server.Implementations/Events/EventingServiceCollectionExtensions.cs2
-rw-r--r--Jellyfin.Server.Implementations/Extensions/ServiceCollectionExtensions.cs2
-rw-r--r--Jellyfin.Server.Implementations/Migrations/20200514181226_AddActivityLog.Designer.cs2
-rw-r--r--Jellyfin.Server.Implementations/Migrations/20200514181226_AddActivityLog.cs2
-rw-r--r--Jellyfin.Server.Implementations/Migrations/20200613202153_AddUsers.Designer.cs2
-rw-r--r--Jellyfin.Server.Implementations/Migrations/20200613202153_AddUsers.cs2
-rw-r--r--Jellyfin.Server.Implementations/Migrations/20200728005145_AddDisplayPreferences.Designer.cs2
-rw-r--r--Jellyfin.Server.Implementations/Migrations/20200728005145_AddDisplayPreferences.cs2
-rw-r--r--Jellyfin.Server.Implementations/Migrations/20200905220533_FixDisplayPreferencesIndex.Designer.cs2
-rw-r--r--Jellyfin.Server.Implementations/Migrations/20200905220533_FixDisplayPreferencesIndex.cs2
-rw-r--r--Jellyfin.Server.Implementations/Migrations/20201004171403_AddMaxActiveSessions.Designer.cs2
-rw-r--r--Jellyfin.Server.Implementations/Migrations/20201004171403_AddMaxActiveSessions.cs2
-rw-r--r--Jellyfin.Server.Implementations/Migrations/20201204223655_AddCustomDisplayPreferences.Designer.cs2
-rw-r--r--Jellyfin.Server.Implementations/Migrations/20201204223655_AddCustomDisplayPreferences.cs2
-rw-r--r--Jellyfin.Server.Implementations/Migrations/20210320181425_AddIndexesAndCollations.Designer.cs2
-rw-r--r--Jellyfin.Server.Implementations/Migrations/20210320181425_AddIndexesAndCollations.cs2
-rw-r--r--Jellyfin.Server.Implementations/Migrations/20210407110544_NullableCustomPrefValue.Designer.cs2
-rw-r--r--Jellyfin.Server.Implementations/Migrations/20210407110544_NullableCustomPrefValue.cs2
-rw-r--r--Jellyfin.Server.Implementations/Migrations/20210814002109_AddDevices.Designer.cs2
-rw-r--r--Jellyfin.Server.Implementations/Migrations/20210814002109_AddDevices.cs2
-rw-r--r--Jellyfin.Server.Implementations/Migrations/20221022080052_AddIndexActivityLogsDateCreated.Designer.cs2
-rw-r--r--Jellyfin.Server.Implementations/Migrations/20221022080052_AddIndexActivityLogsDateCreated.cs2
-rw-r--r--Jellyfin.Server.Implementations/Migrations/20230526173516_RemoveEasyPassword.Designer.cs2
-rw-r--r--Jellyfin.Server.Implementations/Migrations/20230526173516_RemoveEasyPassword.cs2
-rw-r--r--Jellyfin.Server.Implementations/Migrations/20230626233818_AddTrickplayInfos.Designer.cs2
-rw-r--r--Jellyfin.Server.Implementations/Migrations/20230626233818_AddTrickplayInfos.cs2
-rw-r--r--Jellyfin.Server.Implementations/Migrations/20230923170422_UserCastReceiver.Designer.cs2
-rw-r--r--Jellyfin.Server.Implementations/Migrations/20230923170422_UserCastReceiver.cs2
-rw-r--r--Jellyfin.Server.Implementations/Migrations/20240729140605_AddMediaSegments.Designer.cs2
-rw-r--r--Jellyfin.Server.Implementations/Migrations/20240729140605_AddMediaSegments.cs2
-rw-r--r--Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs2
-rw-r--r--Jellyfin.Server.Implementations/ModelBuilderExtensions.cs2
-rw-r--r--Jellyfin.Server.Implementations/Security/AuthenticationManager.cs2
-rw-r--r--Jellyfin.Server.Implementations/Users/DefaultAuthenticationProvider.cs11
-rw-r--r--Jellyfin.Server.Implementations/Users/DeviceAccessHost.cs2
-rw-r--r--Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs2
-rw-r--r--Jellyfin.Server.Implementations/Users/UserManager.cs3
-rw-r--r--Jellyfin.Server.Implementations/ValueConverters/DateTimeKindValueConverter.cs2
-rw-r--r--Jellyfin.Server/Configuration/CorsPolicyProvider.cs2
-rw-r--r--Jellyfin.Server/Filters/FileRequestFilter.cs2
-rw-r--r--Jellyfin.Server/Filters/FileResponseFilter.cs2
-rw-r--r--Jellyfin.Server/Filters/ParameterObsoleteFilter.cs2
-rw-r--r--Jellyfin.Server/Filters/SecurityRequirementsOperationFilter.cs2
-rw-r--r--Jellyfin.Server/Infrastructure/SymlinkFollowingPhysicalFileResultExecutor.cs2
-rw-r--r--Jellyfin.Server/Migrations/PreStartupRoutines/CreateNetworkConfiguration.cs2
-rw-r--r--Jellyfin.Server/Migrations/Routines/AddDefaultCastReceivers.cs2
-rw-r--r--Jellyfin.Server/Migrations/Routines/AddDefaultPluginRepository.cs2
-rw-r--r--Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs2
-rw-r--r--Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs2
-rw-r--r--MediaBrowser.Common/Providers/ProviderIdParsers.cs2
-rw-r--r--MediaBrowser.Controller/Channels/ChannelLatestMediaSearch.cs2
-rw-r--r--MediaBrowser.Controller/Channels/IDisableMediaSourceDisplay.cs2
-rw-r--r--MediaBrowser.Controller/Channels/IHasFolderAttributes.cs2
-rw-r--r--MediaBrowser.Controller/Channels/ISupportsDelete.cs2
-rw-r--r--MediaBrowser.Controller/Channels/ISupportsLatestMedia.cs2
-rw-r--r--MediaBrowser.Controller/Channels/ISupportsMediaProbe.cs2
-rw-r--r--MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs2
-rw-r--r--MediaBrowser.Controller/ClientEvent/IClientEventLogger.cs2
-rw-r--r--MediaBrowser.Controller/Collections/CollectionCreatedEventArgs.cs2
-rw-r--r--MediaBrowser.Controller/Entities/BaseItem.cs4
-rw-r--r--MediaBrowser.Controller/Entities/Folder.cs7
-rw-r--r--MediaBrowser.Controller/Entities/LinkedChildComparer.cs2
-rw-r--r--MediaBrowser.Controller/Entities/LinkedChildType.cs2
-rw-r--r--MediaBrowser.Controller/Entities/UserRootFolder.cs2
-rw-r--r--MediaBrowser.Controller/Entities/UserViewBuilder.cs2
-rw-r--r--MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs13
-rw-r--r--MediaBrowser.Controller/Library/IDirectStreamProvider.cs2
-rw-r--r--MediaBrowser.Controller/Library/IUserDataManager.cs39
-rw-r--r--MediaBrowser.Controller/Library/IUserManager.cs3
-rw-r--r--MediaBrowser.Controller/LiveTv/ActiveRecordingInfo.cs2
-rw-r--r--MediaBrowser.Controller/LiveTv/IListingsManager.cs2
-rw-r--r--MediaBrowser.Controller/LiveTv/ITunerHostManager.cs2
-rw-r--r--MediaBrowser.Controller/MediaEncoding/BaseEncodingJobOptions.cs2
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs574
-rw-r--r--MediaBrowser.Controller/MediaEncoding/FilterOptionType.cs7
-rw-r--r--MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs16
-rw-r--r--MediaBrowser.Controller/MediaEncoding/TranscodingJob.cs2
-rw-r--r--MediaBrowser.Controller/MediaEncoding/TranscodingJobType.cs2
-rw-r--r--MediaBrowser.Controller/MediaEncoding/TranscodingThrottler.cs2
-rw-r--r--MediaBrowser.Controller/MediaSegements/IMediaSegmentManager.cs2
-rw-r--r--MediaBrowser.Controller/Net/WebSocketListenerState.cs2
-rw-r--r--MediaBrowser.Controller/Net/WebSocketMessages/InboundWebSocketMessage.cs2
-rw-r--r--MediaBrowser.Controller/Net/WebSocketMessages/InboundWebSocketMessageOfT.cs2
-rw-r--r--MediaBrowser.Controller/Net/WebSocketMessages/OutboundWebSocketMessage.cs2
-rw-r--r--MediaBrowser.Controller/Net/WebSocketMessages/OutboundWebSocketMessageOfT.cs2
-rw-r--r--MediaBrowser.Controller/Providers/IExternalUrlProvider.cs2
-rw-r--r--MediaBrowser.Controller/Providers/RefreshPriority.cs2
-rw-r--r--MediaBrowser.Controller/Security/IAuthenticationManager.cs2
-rw-r--r--MediaBrowser.Controller/Streaming/ProgressiveFileStream.cs2
-rw-r--r--MediaBrowser.Controller/Streaming/StreamingRequestDto.cs2
-rw-r--r--MediaBrowser.LocalMetadata/XmlProviderUtils.cs2
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs10
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs2
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs117
-rw-r--r--MediaBrowser.MediaEncoding/Probing/MediaStreamInfoSideData.cs7
-rw-r--r--MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs6
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs23
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/SubtitleFormatExtensions.cs29
-rw-r--r--MediaBrowser.Model/Configuration/EncodingOptions.cs6
-rw-r--r--MediaBrowser.Model/Cryptography/Constants.cs2
-rw-r--r--MediaBrowser.Model/Entities/MediaStream.cs6
-rw-r--r--MediaBrowser.Model/Extensions/EnumerableExtensions.cs2
-rw-r--r--MediaBrowser.Model/Library/UserViewQuery.cs7
-rw-r--r--MediaBrowser.Model/LiveTv/ChannelMappingOptionsDto.cs2
-rw-r--r--MediaBrowser.Model/MediaBrowser.Model.csproj2
-rw-r--r--MediaBrowser.Model/Net/MimeTypes.cs123
-rw-r--r--MediaBrowser.Model/Querying/LatestItemsQuery.cs3
-rw-r--r--MediaBrowser.Model/Querying/NextUpQuery.cs26
-rw-r--r--MediaBrowser.Model/Session/HardwareEncodingType.cs2
-rw-r--r--MediaBrowser.Model/System/CastReceiverApplication.cs2
-rw-r--r--MediaBrowser.Model/Tasks/IConfigurableScheduledTask.cs9
-rw-r--r--MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs3
-rw-r--r--MediaBrowser.Model/Tasks/ITaskManager.cs31
-rw-r--r--MediaBrowser.Model/Tasks/TaskCompletionEventArgs.cs18
-rw-r--r--MediaBrowser.Model/Tasks/TaskInfo.cs5
-rw-r--r--MediaBrowser.Model/Tasks/TaskOptions.cs9
-rw-r--r--MediaBrowser.Model/Tasks/TaskTriggerInfo.cs17
-rw-r--r--MediaBrowser.Providers/Lyric/LrcLyricParser.cs125
-rw-r--r--MediaBrowser.Providers/Lyric/LyricScheduledTask.cs2
-rw-r--r--MediaBrowser.Providers/Plugins/AudioDb/Configuration/config.html2
-rw-r--r--MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/config.html2
-rw-r--r--MediaBrowser.Providers/Plugins/Omdb/Configuration/config.html2
-rw-r--r--MediaBrowser.Providers/Plugins/StudioImages/Configuration/PluginConfiguration.cs2
-rw-r--r--MediaBrowser.Providers/Plugins/StudioImages/Configuration/config.html2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs2
-rw-r--r--MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs31
-rw-r--r--fuzz/Emby.Server.Implementations.Fuzz/Program.cs2
-rw-r--r--fuzz/Jellyfin.Api.Fuzz/Program.cs2
-rw-r--r--global.json2
-rw-r--r--src/Jellyfin.Extensions/GuidExtensions.cs2
-rw-r--r--src/Jellyfin.Extensions/Json/Converters/JsonBoolNumberConverter.cs2
-rw-r--r--src/Jellyfin.Extensions/Json/Converters/JsonBoolStringConverter.cs2
-rw-r--r--src/Jellyfin.Extensions/Json/Converters/JsonCommaDelimitedArrayConverter.cs2
-rw-r--r--src/Jellyfin.Extensions/Json/Converters/JsonCommaDelimitedArrayConverterFactory.cs2
-rw-r--r--src/Jellyfin.Extensions/Json/Converters/JsonDateTimeConverter.cs2
-rw-r--r--src/Jellyfin.Extensions/Json/Converters/JsonDelimitedArrayConverter.cs2
-rw-r--r--src/Jellyfin.Extensions/Json/Converters/JsonFlagEnumConverter.cs2
-rw-r--r--src/Jellyfin.Extensions/Json/Converters/JsonFlagEnumConverterFactory.cs2
-rw-r--r--src/Jellyfin.Extensions/Json/Converters/JsonNullableStructConverter.cs2
-rw-r--r--src/Jellyfin.Extensions/Json/Converters/JsonNullableStructConverterFactory.cs2
-rw-r--r--src/Jellyfin.Extensions/Json/Converters/JsonPipeDelimitedArrayConverter.cs2
-rw-r--r--src/Jellyfin.Extensions/Json/Converters/JsonPipeDelimitedArrayConverterFactory.cs2
-rw-r--r--src/Jellyfin.Extensions/Json/Converters/JsonStringConverter.cs2
-rw-r--r--src/Jellyfin.Extensions/Json/Converters/JsonVersionConverter.cs2
-rw-r--r--src/Jellyfin.LiveTv/Configuration/LiveTvConfigurationExtensions.cs2
-rw-r--r--src/Jellyfin.LiveTv/Extensions/LiveTvServiceCollectionExtensions.cs2
-rw-r--r--src/Jellyfin.LiveTv/Listings/ListingsManager.cs2
-rw-r--r--src/Jellyfin.LiveTv/TunerHosts/TunerHostManager.cs2
-rw-r--r--src/Jellyfin.MediaEncoding.Hls/Cache/CacheDecorator.cs2
-rw-r--r--src/Jellyfin.MediaEncoding.Hls/Extensions/MediaEncodingHlsServiceCollectionExtensions.cs2
-rw-r--r--src/Jellyfin.MediaEncoding.Hls/Extractors/FfProbeKeyframeExtractor.cs2
-rw-r--r--src/Jellyfin.MediaEncoding.Hls/Extractors/MatroskaKeyframeExtractor.cs2
-rw-r--r--src/Jellyfin.MediaEncoding.Hls/Playlist/CreateMainPlaylistRequest.cs2
-rw-r--r--src/Jellyfin.MediaEncoding.Hls/Playlist/DynamicHlsPlaylistGenerator.cs2
-rw-r--r--src/Jellyfin.MediaEncoding.Hls/Playlist/IDynamicHlsPlaylistGenerator.cs2
-rw-r--r--src/Jellyfin.MediaEncoding.Hls/ScheduledTasks/KeyframeExtractionScheduledTask.cs2
-rw-r--r--src/Jellyfin.MediaEncoding.Keyframes/FfProbe/FfProbeKeyframeExtractor.cs2
-rw-r--r--src/Jellyfin.MediaEncoding.Keyframes/FfTool/FfToolKeyframeExtractor.cs2
-rw-r--r--src/Jellyfin.MediaEncoding.Keyframes/KeyframeData.cs2
-rw-r--r--src/Jellyfin.MediaEncoding.Keyframes/Matroska/MatroskaConstants.cs2
-rw-r--r--src/Jellyfin.MediaEncoding.Keyframes/Matroska/MatroskaKeyframeExtractor.cs2
-rw-r--r--src/Jellyfin.MediaEncoding.Keyframes/Matroska/Models/Info.cs2
-rw-r--r--src/Jellyfin.MediaEncoding.Keyframes/Matroska/Models/SeekHead.cs2
-rw-r--r--src/Jellyfin.Networking/AutoDiscoveryHost.cs2
-rw-r--r--tests/Jellyfin.Api.Tests/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandlerTests.cs2
-rw-r--r--tests/Jellyfin.Api.Tests/Auth/IgnoreSchedulePolicy/IgnoreScheduleHandlerTests.cs2
-rw-r--r--tests/Jellyfin.Api.Tests/TestHelpers.cs2
-rw-r--r--tests/Jellyfin.Common.Tests/Providers/ProviderIdParserTests.cs2
-rw-r--r--tests/Jellyfin.Controller.Tests/DirectoryServiceTests.cs2
-rw-r--r--tests/Jellyfin.Extensions.Tests/Json/Converters/JsonFlagEnumTests.cs2
-rw-r--r--tests/Jellyfin.LiveTv.Tests/Jellyfin.LiveTv.Tests.csproj2
-rw-r--r--tests/Jellyfin.MediaEncoding.Hls.Tests/Playlist/DynamicHlsPlaylistGeneratorTests.cs2
-rw-r--r--tests/Jellyfin.MediaEncoding.Keyframes.Tests/FfProbe/FfProbeKeyframeExtractorTests.cs2
-rw-r--r--tests/Jellyfin.MediaEncoding.Keyframes.Tests/FfProbe/Test Data/keyframes.txt2
-rw-r--r--tests/Jellyfin.MediaEncoding.Keyframes.Tests/FfProbe/Test Data/keyframes_result.json2
-rw-r--r--tests/Jellyfin.MediaEncoding.Keyframes.Tests/FfProbe/Test Data/keyframes_streamduration.txt2
-rw-r--r--tests/Jellyfin.MediaEncoding.Keyframes.Tests/FfProbe/Test Data/keyframes_streamduration_result.json2
-rw-r--r--tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs1
-rw-r--r--tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_metadata.json4
-rw-r--r--tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj2
-rw-r--r--tests/Jellyfin.Naming.Tests/Music/MultiDiscAlbumTests.cs2
-rw-r--r--tests/Jellyfin.Naming.Tests/TV/AbsoluteEpisodeNumberTests.cs2
-rw-r--r--tests/Jellyfin.Naming.Tests/TV/DailyEpisodeTests.cs2
-rw-r--r--tests/Jellyfin.Naming.Tests/TV/EpisodeNumberWithoutSeasonTests.cs2
-rw-r--r--tests/Jellyfin.Naming.Tests/TV/MultiEpisodeTests.cs2
-rw-r--r--tests/Jellyfin.Naming.Tests/TV/SeasonNumberTests.cs2
-rw-r--r--tests/Jellyfin.Naming.Tests/Video/Format3DTests.cs2
-rw-r--r--tests/Jellyfin.Naming.Tests/Video/StackTests.cs2
-rw-r--r--tests/Jellyfin.Providers.Tests/Omdb/JsonOmdbConverterTests.cs2
-rw-r--r--tests/Jellyfin.Providers.Tests/Tmdb/TmdbUtilsTests.cs2
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj2
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs2
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/SessionManager/SessionManagerTests.cs2
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Users/UserManagerTests.cs2
-rw-r--r--tests/Jellyfin.Server.Integration.Tests/Controllers/BaseJellyfinTestController.cs2
-rw-r--r--tests/Jellyfin.Server.Integration.Tests/Controllers/EncoderController.cs2
-rw-r--r--tests/Jellyfin.Server.Integration.Tests/Controllers/LiveTvControllerTests.cs96
-rw-r--r--tests/Jellyfin.Server.Integration.Tests/Controllers/PersonsControllerTests.cs2
-rw-r--r--tests/Jellyfin.Server.Integration.Tests/Controllers/PlaystateControllerTests.cs2
-rw-r--r--tests/Jellyfin.Server.Integration.Tests/Controllers/PluginsControllerTests.cs45
-rw-r--r--tests/Jellyfin.Server.Integration.Tests/Controllers/SessionControllerTests.cs2
-rw-r--r--tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj3
-rw-r--r--tests/Jellyfin.Server.Integration.Tests/JellyfinApplicationFactory.cs2
-rw-r--r--tests/Jellyfin.Server.Integration.Tests/Middleware/RobotsRedirectionMiddlewareTests.cs2
-rw-r--r--tests/Jellyfin.Server.Integration.Tests/Test Data/dummy.m3u81
-rw-r--r--tests/Jellyfin.XbmcMetadata.Tests/Location/MovieNfoLocationTests.cs2
-rw-r--r--tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicAlbumNfoProviderTests.cs2
-rw-r--r--tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicVideoNfoParserTests.cs2
-rw-r--r--tests/Jellyfin.XbmcMetadata.Tests/Parsers/SeasonNfoProviderTests.cs2
-rw-r--r--tests/Jellyfin.XbmcMetadata.Tests/Test Data/Dancing Queen.nfo2
-rw-r--r--tests/Jellyfin.XbmcMetadata.Tests/Test Data/Radarr.nfo2
-rw-r--r--tests/Jellyfin.XbmcMetadata.Tests/Test Data/Season 01.nfo2
-rw-r--r--tests/Jellyfin.XbmcMetadata.Tests/Test Data/The Best of 1980-1990.nfo2
-rw-r--r--tests/Jellyfin.XbmcMetadata.Tests/Test Data/The Bone Orchard.nfo2
387 files changed, 1798 insertions, 1228 deletions
diff --git a/.github/ISSUE_TEMPLATE/issue report.yml b/.github/ISSUE_TEMPLATE/issue report.yml
index b71b365f7..cfb5a6ec2 100644
--- a/.github/ISSUE_TEMPLATE/issue report.yml
+++ b/.github/ISSUE_TEMPLATE/issue report.yml
@@ -86,7 +86,7 @@ body:
label: Jellyfin Server version
description: What version of Jellyfin are you using?
options:
- - 10.9.9+
+ - 10.9.10+
- Master
- Unstable
- Older*
diff --git a/.github/workflows/ci-codeql-analysis.yml b/.github/workflows/ci-codeql-analysis.yml
index ca3be2665..513139ea5 100644
--- a/.github/workflows/ci-codeql-analysis.yml
+++ b/.github/workflows/ci-codeql-analysis.yml
@@ -27,11 +27,11 @@ jobs:
dotnet-version: '8.0.x'
- name: Initialize CodeQL
- uses: github/codeql-action/init@f0f3afee809481da311ca3a6ff1ff51d81dbeb24 # v3.26.4
+ uses: github/codeql-action/init@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6
with:
languages: ${{ matrix.language }}
queries: +security-extended
- name: Autobuild
- uses: github/codeql-action/autobuild@f0f3afee809481da311ca3a6ff1ff51d81dbeb24 # v3.26.4
+ uses: github/codeql-action/autobuild@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@f0f3afee809481da311ca3a6ff1ff51d81dbeb24 # v3.26.4
+ uses: github/codeql-action/analyze@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6
diff --git a/.github/workflows/ci-compat.yml b/.github/workflows/ci-compat.yml
new file mode 100644
index 000000000..c6e655d08
--- /dev/null
+++ b/.github/workflows/ci-compat.yml
@@ -0,0 +1,149 @@
+name: ABI Compatibility
+on:
+ pull_request_target:
+
+permissions: {}
+
+jobs:
+ abi-head:
+ name: ABI - HEAD
+ runs-on: ubuntu-latest
+ permissions: read-all
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
+ with:
+ ref: ${{ github.event.pull_request.head.sha }}
+ repository: ${{ github.event.pull_request.head.repo.full_name }}
+
+ - name: Build
+ run: |
+ dotnet build Jellyfin.Server -o ./out
+
+ - name: Upload Head
+ uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
+ with:
+ name: abi-head
+ retention-days: 14
+ if-no-files-found: error
+ path: out/
+
+ abi-base:
+ name: ABI - BASE
+ if: ${{ github.base_ref != '' }}
+ runs-on: ubuntu-latest
+ permissions: read-all
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
+ with:
+ ref: ${{ github.event.pull_request.head.sha }}
+ repository: ${{ github.event.pull_request.head.repo.full_name }}
+ fetch-depth: 0
+
+ - name: Checkout common ancestor
+ env:
+ HEAD_REF: ${{ github.head_ref }}
+ run: |
+ git remote add upstream https://github.com/${{ github.event.pull_request.base.repo.full_name }}
+ git -c protocol.version=2 fetch --prune --progress --no-recurse-submodules upstream +refs/heads/*:refs/remotes/upstream/* +refs/tags/*:refs/tags/*
+ ANCESTOR_REF=$(git merge-base upstream/${{ github.base_ref }} origin/$HEAD_REF)
+ git checkout --progress --force $ANCESTOR_REF
+
+ - name: Build
+ run: |
+ dotnet build Jellyfin.Server -o ./out
+
+ - name: Upload Head
+ uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
+ with:
+ name: abi-base
+ retention-days: 14
+ if-no-files-found: error
+ path: out/
+
+ abi-diff:
+ permissions:
+ pull-requests: write # to create or update comment (peter-evans/create-or-update-comment)
+
+ name: ABI - Difference
+ if: ${{ github.event_name == 'pull_request_target' }}
+ runs-on: ubuntu-latest
+ needs:
+ - abi-head
+ - abi-base
+
+ steps:
+ - name: Download abi-head
+ uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
+ with:
+ name: abi-head
+ path: abi-head
+
+ - name: Download abi-base
+ uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
+ with:
+ name: abi-base
+ path: abi-base
+
+ - name: Setup ApiCompat
+ run: |
+ dotnet tool install --global Microsoft.DotNet.ApiCompat.Tool
+
+ - name: Run ApiCompat
+ id: diff
+ run: |
+ {
+ echo 'body<<EOF'
+ for file in Jellyfin.Data.dll MediaBrowser.Common.dll MediaBrowser.Controller.dll MediaBrowser.Model.dll Emby.Naming.dll Jellyfin.Extensions.dll; do
+ COMPAT_OUTPUT="$( { apicompat --left ./abi-base/${file} --right ./abi-head/${file}; } 2>&1 )"
+ if [ "APICompat ran successfully without finding any breaking changes." != "${COMPAT_OUTPUT}" ]; then
+ printf "\n${file}\n${COMPAT_OUTPUT}\n"
+ fi
+ done
+ echo EOF
+ } >> $GITHUB_OUTPUT
+
+ - name: Find difference comment
+ uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e # v3.1.0
+ id: find-comment
+ with:
+ issue-number: ${{ github.event.pull_request.number }}
+ direction: last
+ body-includes: abi-diff-workflow-comment
+
+ - name: Reply or edit difference comment (changed)
+ uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0
+ if: ${{ steps.diff.outputs.body != '' }}
+ with:
+ issue-number: ${{ github.event.pull_request.number }}
+ comment-id: ${{ steps.find-comment.outputs.comment-id }}
+ edit-mode: replace
+ token: ${{ secrets.JF_BOT_TOKEN }}
+ body: |
+ <!--abi-diff-workflow-comment-->
+ <details>
+ <summary>ABI Difference</summary>
+
+ ```
+ ${{ steps.diff.outputs.body }}
+ ```
+
+ </details>
+
+ - name: Reply or edit difference comment (unchanged)
+ uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0
+ if: ${{ steps.diff.outputs.body == '' && steps.find-comment.outputs.comment-id != '' }}
+ with:
+ issue-number: ${{ github.event.pull_request.number }}
+ comment-id: ${{ steps.find-comment.outputs.comment-id }}
+ edit-mode: replace
+ token: ${{ secrets.JF_BOT_TOKEN }}
+ body: |
+ <!--abi-diff-workflow-comment-->
+ <details>
+ <summary>ABI Difference</summary>
+
+ No changes to the ABI found. See history of this comment for previous changes.
+
+ </details>
diff --git a/.github/workflows/ci-openapi.yml b/.github/workflows/ci-openapi.yml
index 93bfc83a2..d01c506db 100644
--- a/.github/workflows/ci-openapi.yml
+++ b/.github/workflows/ci-openapi.yml
@@ -27,7 +27,7 @@ jobs:
- name: Generate openapi.json
run: dotnet test tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj -c Release --filter "Jellyfin.Server.Integration.Tests.OpenApiSpecTests"
- name: Upload openapi.json
- uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
+ uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
with:
name: openapi-head
retention-days: 14
@@ -61,7 +61,7 @@ jobs:
- name: Generate openapi.json
run: dotnet test tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj -c Release --filter "Jellyfin.Server.Integration.Tests.OpenApiSpecTests"
- name: Upload openapi.json
- uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
+ uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
with:
name: openapi-base
retention-days: 14
diff --git a/.github/workflows/ci-tests.yml b/.github/workflows/ci-tests.yml
index 91c2be87b..af8106c0a 100644
--- a/.github/workflows/ci-tests.yml
+++ b/.github/workflows/ci-tests.yml
@@ -34,7 +34,7 @@ jobs:
--verbosity minimal
- name: Merge code coverage results
- uses: danielpalme/ReportGenerator-GitHub-Action@5808021ec4deecb0ab3da051d49b4ce65fcc20af # 5.3.8
+ uses: danielpalme/ReportGenerator-GitHub-Action@e3af7259842d9c814021ea121f85526e0872b25f # v5.3.9
with:
reports: "**/coverage.cobertura.xml"
targetdir: "merged/"
diff --git a/.github/workflows/commands.yml b/.github/workflows/commands.yml
index b79185855..4b469f0d7 100644
--- a/.github/workflows/commands.yml
+++ b/.github/workflows/commands.yml
@@ -132,7 +132,7 @@ jobs:
with:
repository: jellyfin/jellyfin-triage-script
- name: install python
- uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1
+ uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0
with:
python-version: '3.12'
cache: 'pip'
diff --git a/.github/workflows/issue-template-check.yml b/.github/workflows/issue-template-check.yml
index 6172455c2..d89076595 100644
--- a/.github/workflows/issue-template-check.yml
+++ b/.github/workflows/issue-template-check.yml
@@ -14,7 +14,7 @@ jobs:
with:
repository: jellyfin/jellyfin-triage-script
- name: install python
- uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1
+ uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0
with:
python-version: '3.12'
cache: 'pip'
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index 7c2e72327..cdf8df17f 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -187,6 +187,7 @@
- [HonestlyWhoKnows](https://github.com/honestlywhoknows)
- [TheMelmacian](https://github.com/TheMelmacian)
- [ItsAllAboutTheCode](https://github.com/ItsAllAboutTheCode)
+ - [pret0rian8](https://github.com/pret0rian)
# Emby Contributors
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 12fcc26a4..02937b193 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -9,8 +9,8 @@
<PackageVersion Include="AutoFixture.Xunit2" Version="4.18.1" />
<PackageVersion Include="AutoFixture" Version="4.18.1" />
<PackageVersion Include="BDInfo" Version="0.8.0" />
- <PackageVersion Include="BlurHashSharp.SkiaSharp" Version="1.3.2" />
- <PackageVersion Include="BlurHashSharp" Version="1.3.2" />
+ <PackageVersion Include="BlurHashSharp.SkiaSharp" Version="1.3.3" />
+ <PackageVersion Include="BlurHashSharp" Version="1.3.3" />
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
<PackageVersion Include="coverlet.collector" Version="6.0.2" />
<PackageVersion Include="Diacritics" Version="3.3.29" />
@@ -21,8 +21,8 @@
<PackageVersion Include="ICU4N.Transliterator" Version="60.1.0-alpha.356" />
<PackageVersion Include="IDisposableAnalyzers" Version="4.0.8" />
<PackageVersion Include="Jellyfin.XmlTv" Version="10.8.0" />
- <PackageVersion Include="libse" Version="4.0.7" />
- <PackageVersion Include="LrcParser" Version="2023.524.0" />
+ <PackageVersion Include="libse" Version="4.0.8" />
+ <PackageVersion Include="LrcParser" Version="2024.0728.2" />
<PackageVersion Include="MetaBrainz.MusicBrainz" Version="6.1.0" />
<PackageVersion Include="Microsoft.AspNetCore.Authorization" Version="8.0.8" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.8" />
@@ -47,7 +47,7 @@
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.1" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="8.0.2" />
- <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.11.0" />
+ <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageVersion Include="MimeTypes" Version="2.4.0" />
<PackageVersion Include="Mono.Nat" Version="3.0.4" />
<PackageVersion Include="Moq" Version="4.18.4" />
@@ -71,7 +71,7 @@
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.8" />
<PackageVersion Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" />
<PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
- <PackageVersion Include="Svg.Skia" Version="2.0.0" />
+ <PackageVersion Include="Svg.Skia" Version="2.0.0.1" />
<PackageVersion Include="Swashbuckle.AspNetCore.ReDoc" Version="6.5.0" />
<PackageVersion Include="Swashbuckle.AspNetCore" Version="6.2.3" />
<PackageVersion Include="System.Globalization" Version="4.3.0" />
@@ -80,7 +80,7 @@
<PackageVersion Include="System.Text.Json" Version="8.0.4" />
<PackageVersion Include="System.Threading.Tasks.Dataflow" Version="8.0.1" />
<PackageVersion Include="TagLibSharp" Version="2.3.0" />
- <PackageVersion Include="z440.atl.core" Version="6.1.0" />
+ <PackageVersion Include="z440.atl.core" Version="6.3.0" />
<PackageVersion Include="TMDbLib" Version="2.2.0" />
<PackageVersion Include="UTF.Unknown" Version="2.5.1" />
<PackageVersion Include="Xunit.Priority" Version="1.1.6" />
diff --git a/Emby.Naming/Video/FileStackRule.cs b/Emby.Naming/Video/FileStackRule.cs
index be0f79d33..485097d03 100644
--- a/Emby.Naming/Video/FileStackRule.cs
+++ b/Emby.Naming/Video/FileStackRule.cs
@@ -1,4 +1,4 @@
-using System.Diagnostics.CodeAnalysis;
+using System.Diagnostics.CodeAnalysis;
using System.Text.RegularExpressions;
namespace Emby.Naming.Video;
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index ceda0bd64..5292003f0 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -402,7 +402,12 @@ namespace Emby.Server.Implementations
ConfigurationManager.ConfigurationUpdated += OnConfigurationUpdated;
ConfigurationManager.NamedConfigurationUpdated += OnConfigurationUpdated;
- Resolve<IMediaEncoder>().SetFFmpegPath();
+ var ffmpegValid = Resolve<IMediaEncoder>().SetFFmpegPath();
+
+ if (!ffmpegValid)
+ {
+ throw new FfmpegException("Failed to find valid ffmpeg");
+ }
Logger.LogInformation("ServerId: {ServerId}", SystemId);
Logger.LogInformation("Core startup complete");
diff --git a/Emby.Server.Implementations/ConfigurationOptions.cs b/Emby.Server.Implementations/ConfigurationOptions.cs
index f0c267627..e86010513 100644
--- a/Emby.Server.Implementations/ConfigurationOptions.cs
+++ b/Emby.Server.Implementations/ConfigurationOptions.cs
@@ -19,7 +19,8 @@ namespace Emby.Server.Implementations
{ FfmpegAnalyzeDurationKey, "200M" },
{ PlaylistsAllowDuplicatesKey, bool.FalseString },
{ BindToUnixSocketKey, bool.FalseString },
- { SqliteCacheSizeKey, "20000" }
+ { SqliteCacheSizeKey, "20000" },
+ { FfmpegSkipValidationKey, bool.FalseString }
};
}
}
diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
index 5094dcf0d..60f5ee47a 100644
--- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
@@ -183,7 +183,8 @@ namespace Emby.Server.Implementations.Data
"ElPresentFlag",
"BlPresentFlag",
"DvBlSignalCompatibilityId",
- "IsHearingImpaired"
+ "IsHearingImpaired",
+ "Rotation"
};
private static readonly string _mediaStreamSaveColumnsInsertQuery =
@@ -343,7 +344,7 @@ namespace Emby.Server.Implementations.Data
base.Initialize();
const string CreateMediaStreamsTableCommand
- = "create table if not exists mediastreams (ItemId GUID, StreamIndex INT, StreamType TEXT, Codec TEXT, Language TEXT, ChannelLayout TEXT, Profile TEXT, AspectRatio TEXT, Path TEXT, IsInterlaced BIT, BitRate INT NULL, Channels INT NULL, SampleRate INT NULL, IsDefault BIT, IsForced BIT, IsExternal BIT, Height INT NULL, Width INT NULL, AverageFrameRate FLOAT NULL, RealFrameRate FLOAT NULL, Level FLOAT NULL, PixelFormat TEXT, BitDepth INT NULL, IsAnamorphic BIT NULL, RefFrames INT NULL, CodecTag TEXT NULL, Comment TEXT NULL, NalLengthSize TEXT NULL, IsAvc BIT NULL, Title TEXT NULL, TimeBase TEXT NULL, CodecTimeBase TEXT NULL, ColorPrimaries TEXT NULL, ColorSpace TEXT NULL, ColorTransfer TEXT NULL, DvVersionMajor INT NULL, DvVersionMinor INT NULL, DvProfile INT NULL, DvLevel INT NULL, RpuPresentFlag INT NULL, ElPresentFlag INT NULL, BlPresentFlag INT NULL, DvBlSignalCompatibilityId INT NULL, IsHearingImpaired BIT NULL, PRIMARY KEY (ItemId, StreamIndex))";
+ = "create table if not exists mediastreams (ItemId GUID, StreamIndex INT, StreamType TEXT, Codec TEXT, Language TEXT, ChannelLayout TEXT, Profile TEXT, AspectRatio TEXT, Path TEXT, IsInterlaced BIT, BitRate INT NULL, Channels INT NULL, SampleRate INT NULL, IsDefault BIT, IsForced BIT, IsExternal BIT, Height INT NULL, Width INT NULL, AverageFrameRate FLOAT NULL, RealFrameRate FLOAT NULL, Level FLOAT NULL, PixelFormat TEXT, BitDepth INT NULL, IsAnamorphic BIT NULL, RefFrames INT NULL, CodecTag TEXT NULL, Comment TEXT NULL, NalLengthSize TEXT NULL, IsAvc BIT NULL, Title TEXT NULL, TimeBase TEXT NULL, CodecTimeBase TEXT NULL, ColorPrimaries TEXT NULL, ColorSpace TEXT NULL, ColorTransfer TEXT NULL, DvVersionMajor INT NULL, DvVersionMinor INT NULL, DvProfile INT NULL, DvLevel INT NULL, RpuPresentFlag INT NULL, ElPresentFlag INT NULL, BlPresentFlag INT NULL, DvBlSignalCompatibilityId INT NULL, IsHearingImpaired BIT NULL, Rotation INT NULL, PRIMARY KEY (ItemId, StreamIndex))";
const string CreateMediaAttachmentsTableCommand
= "create table if not exists mediaattachments (ItemId GUID, AttachmentIndex INT, Codec TEXT, CodecTag TEXT NULL, Comment TEXT NULL, Filename TEXT NULL, MIMEType TEXT NULL, PRIMARY KEY (ItemId, AttachmentIndex))";
@@ -538,6 +539,8 @@ namespace Emby.Server.Implementations.Data
AddColumn(connection, "MediaStreams", "IsHearingImpaired", "BIT", existingColumnNames);
+ AddColumn(connection, "MediaStreams", "Rotation", "INT", existingColumnNames);
+
connection.Execute(string.Join(';', postQueries));
transaction.Commit();
@@ -5483,6 +5486,8 @@ AND Type = @InternalPersonType)");
statement.TryBind("@DvBlSignalCompatibilityId" + index, stream.DvBlSignalCompatibilityId);
statement.TryBind("@IsHearingImpaired" + index, stream.IsHearingImpaired);
+
+ statement.TryBind("@Rotation" + index, stream.Rotation);
}
statement.ExecuteNonQuery();
@@ -5694,6 +5699,11 @@ AND Type = @InternalPersonType)");
item.IsHearingImpaired = reader.TryGetBoolean(43, out var result) && result;
+ if (reader.TryGetInt32(44, out var rotation))
+ {
+ item.Rotation = rotation;
+ }
+
if (item.Type is MediaStreamType.Audio or MediaStreamType.Subtitle)
{
item.LocalizedDefault = _localization.GetLocalizedString("Default");
diff --git a/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs
index 47f9dfbc8..aef02ce6b 100644
--- a/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs
@@ -133,7 +133,8 @@ namespace Emby.Server.Implementations.EntryPoints
private UserDataChangeInfo GetUserDataChangeInfo(Guid userId, List<BaseItem> changedItems)
{
- var user = _userManager.GetUserById(userId);
+ var user = _userManager.GetUserById(userId)
+ ?? throw new ArgumentException("Invalid user ID", nameof(userId));
return new UserDataChangeInfo
{
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index cbded1ec6..48d24385e 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -669,7 +669,7 @@ namespace Emby.Server.Implementations.Library
if (parent is not null)
{
- var multiItemResolvers = resolvers is null ? MultiItemResolvers : resolvers.OfType<IMultiItemResolver>().ToArray();
+ var multiItemResolvers = resolvers is null ? MultiItemResolvers : resolvers.OfType<IMultiItemResolver>();
foreach (var resolver in multiItemResolvers)
{
@@ -1530,7 +1530,7 @@ namespace Emby.Server.Implementations.Library
{
var userViews = UserViewManager.GetUserViews(new UserViewQuery
{
- UserId = user.Id,
+ User = user,
IncludeHidden = true,
IncludeExternalContent = allowExternalContent
});
diff --git a/Emby.Server.Implementations/Library/UserDataManager.cs b/Emby.Server.Implementations/Library/UserDataManager.cs
index 8beeb8041..62d22b23f 100644
--- a/Emby.Server.Implementations/Library/UserDataManager.cs
+++ b/Emby.Server.Implementations/Library/UserDataManager.cs
@@ -1,10 +1,7 @@
-#nullable disable
-
-#pragma warning disable CS1591
-
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Globalization;
using System.Threading;
using Jellyfin.Data.Entities;
@@ -32,6 +29,12 @@ namespace Emby.Server.Implementations.Library
private readonly IUserManager _userManager;
private readonly IUserDataRepository _repository;
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UserDataManager"/> class.
+ /// </summary>
+ /// <param name="config">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
+ /// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
+ /// <param name="repository">Instance of the <see cref="IUserDataRepository"/> interface.</param>
public UserDataManager(
IServerConfigurationManager config,
IUserManager userManager,
@@ -42,15 +45,10 @@ namespace Emby.Server.Implementations.Library
_repository = repository;
}
- public event EventHandler<UserDataSaveEventArgs> UserDataSaved;
-
- public void SaveUserData(Guid userId, BaseItem item, UserItemData userData, UserDataSaveReason reason, CancellationToken cancellationToken)
- {
- var user = _userManager.GetUserById(userId);
-
- SaveUserData(user, item, userData, reason, cancellationToken);
- }
+ /// <inheritdoc />
+ public event EventHandler<UserDataSaveEventArgs>? UserDataSaved;
+ /// <inheritdoc />
public void SaveUserData(User user, BaseItem item, UserItemData userData, UserDataSaveReason reason, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(userData);
@@ -81,6 +79,7 @@ namespace Emby.Server.Implementations.Library
});
}
+ /// <inheritdoc />
public void SaveUserData(User user, BaseItem item, UpdateUserItemDataDto userDataDto, UserDataSaveReason reason)
{
ArgumentNullException.ThrowIfNull(user);
@@ -128,39 +127,7 @@ namespace Emby.Server.Implementations.Library
SaveUserData(user, item, userData, reason, CancellationToken.None);
}
- /// <summary>
- /// Save the provided user data for the given user. Batch operation. Does not fire any events or update the cache.
- /// </summary>
- /// <param name="userId">The user id.</param>
- /// <param name="userData">The user item data.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- public void SaveAllUserData(Guid userId, UserItemData[] userData, CancellationToken cancellationToken)
- {
- var user = _userManager.GetUserById(userId);
-
- _repository.SaveAllUserData(user.InternalId, userData, cancellationToken);
- }
-
- /// <summary>
- /// Retrieve all user data for the given user.
- /// </summary>
- /// <param name="userId">The user id.</param>
- /// <returns>A <see cref="List{UserItemData}"/> containing all of the user's item data.</returns>
- public List<UserItemData> GetAllUserData(Guid userId)
- {
- var user = _userManager.GetUserById(userId);
-
- return _repository.GetAllUserData(user.InternalId);
- }
-
- public UserItemData GetUserData(Guid userId, Guid itemId, List<string> keys)
- {
- var user = _userManager.GetUserById(userId);
-
- return GetUserData(user, itemId, keys);
- }
-
- public UserItemData GetUserData(User user, Guid itemId, List<string> keys)
+ private UserItemData GetUserData(User user, Guid itemId, List<string> keys)
{
var userId = user.InternalId;
@@ -186,7 +153,7 @@ namespace Emby.Server.Implementations.Library
};
}
- return null;
+ throw new UnreachableException();
}
/// <summary>
@@ -198,27 +165,18 @@ namespace Emby.Server.Implementations.Library
return internalUserId.ToString(CultureInfo.InvariantCulture) + "-" + itemId.ToString("N", CultureInfo.InvariantCulture);
}
+ /// <inheritdoc />
public UserItemData GetUserData(User user, BaseItem item)
{
return GetUserData(user, item.Id, item.GetUserDataKeys());
}
- public UserItemData GetUserData(Guid userId, BaseItem item)
- {
- return GetUserData(userId, item.Id, item.GetUserDataKeys());
- }
-
+ /// <inheritdoc />
public UserItemDataDto GetUserDataDto(BaseItem item, User user)
- {
- var userData = GetUserData(user, item);
- var dto = GetUserItemDataDto(userData);
-
- item.FillUserDataDtoValues(dto, userData, null, user, new DtoOptions());
- return dto;
- }
+ => GetUserDataDto(item, null, user, new DtoOptions());
/// <inheritdoc />
- public UserItemDataDto GetUserDataDto(BaseItem item, BaseItemDto itemDto, User user, DtoOptions options)
+ public UserItemDataDto GetUserDataDto(BaseItem item, BaseItemDto? itemDto, User user, DtoOptions options)
{
var userData = GetUserData(user, item);
var dto = GetUserItemDataDto(userData);
diff --git a/Emby.Server.Implementations/Library/UserViewManager.cs b/Emby.Server.Implementations/Library/UserViewManager.cs
index d9a559014..e9cf47d46 100644
--- a/Emby.Server.Implementations/Library/UserViewManager.cs
+++ b/Emby.Server.Implementations/Library/UserViewManager.cs
@@ -16,7 +16,6 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.Channels;
-using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Library;
using MediaBrowser.Model.Querying;
@@ -27,17 +26,15 @@ namespace Emby.Server.Implementations.Library
{
private readonly ILibraryManager _libraryManager;
private readonly ILocalizationManager _localizationManager;
- private readonly IUserManager _userManager;
private readonly IChannelManager _channelManager;
private readonly ILiveTvManager _liveTvManager;
private readonly IServerConfigurationManager _config;
- public UserViewManager(ILibraryManager libraryManager, ILocalizationManager localizationManager, IUserManager userManager, IChannelManager channelManager, ILiveTvManager liveTvManager, IServerConfigurationManager config)
+ public UserViewManager(ILibraryManager libraryManager, ILocalizationManager localizationManager, IChannelManager channelManager, ILiveTvManager liveTvManager, IServerConfigurationManager config)
{
_libraryManager = libraryManager;
_localizationManager = localizationManager;
- _userManager = userManager;
_channelManager = channelManager;
_liveTvManager = liveTvManager;
_config = config;
@@ -45,11 +42,7 @@ namespace Emby.Server.Implementations.Library
public Folder[] GetUserViews(UserViewQuery query)
{
- var user = _userManager.GetUserById(query.UserId);
- if (user is null)
- {
- throw new ArgumentException("User id specified in the query does not exist.", nameof(query));
- }
+ var user = query.User;
var folders = _libraryManager.GetUserRootFolder()
.GetChildren(user, true)
@@ -125,14 +118,14 @@ namespace Emby.Server.Implementations.Library
{
var channelResult = _channelManager.GetChannelsInternalAsync(new ChannelQuery
{
- UserId = query.UserId
+ UserId = user.Id
}).GetAwaiter().GetResult();
var channels = channelResult.Items;
list.AddRange(channels);
- if (_liveTvManager.GetEnabledUsers().Select(i => i.Id).Contains(query.UserId))
+ if (_liveTvManager.GetEnabledUsers().Select(i => i.Id).Contains(user.Id))
{
list.Add(_liveTvManager.GetInternalLiveTvFolder(CancellationToken.None));
}
@@ -207,9 +200,7 @@ namespace Emby.Server.Implementations.Library
public List<Tuple<BaseItem, List<BaseItem>>> GetLatestItems(LatestItemsQuery request, DtoOptions options)
{
- var user = _userManager.GetUserById(request.UserId);
-
- var libraryItems = GetItemsForLatestItems(user, request, options);
+ var libraryItems = GetItemsForLatestItems(request.User, request, options);
var list = new List<Tuple<BaseItem, List<BaseItem>>>();
diff --git a/Emby.Server.Implementations/Localization/Core/ar.json b/Emby.Server.Implementations/Localization/Core/ar.json
index 4245656ff..bd45b0b96 100644
--- a/Emby.Server.Implementations/Localization/Core/ar.json
+++ b/Emby.Server.Implementations/Localization/Core/ar.json
@@ -1,14 +1,14 @@
{
- "Albums": "البومات",
+ "Albums": "ألبومات",
"AppDeviceValues": "تطبيق: {0}, جهاز: {1}",
"Application": "تطبيق",
- "Artists": "الفنانين",
+ "Artists": "الفنانون",
"AuthenticationSucceededWithUserName": "نجحت عملية التوثيق بـ {0}",
"Books": "الكتب",
"CameraImageUploadedFrom": "رُفعت صورة الكاميرا الجديدة من {0}",
"Channels": "القنوات",
"ChapterNameValue": "الفصل {0}",
- "Collections": "التجميعات",
+ "Collections": "المجموعات",
"DeviceOfflineWithName": "قُطِع الاتصال ب{0}",
"DeviceOnlineWithName": "{0} متصل",
"FailedLoginAttemptWithUserName": "محاولة تسجيل الدخول فاشلة من {0}",
@@ -130,5 +130,6 @@
"TaskCleanCollectionsAndPlaylists": "حذف المجموعات وقوائم التشغيل",
"TaskCleanCollectionsAndPlaylistsDescription": "حذف عناصر من المجموعات وقوائم التشغيل التي لم تعد موجودة.",
"TaskAudioNormalization": "تطبيع الصوت",
- "TaskAudioNormalizationDescription": "مسح الملفات لتطبيع بيانات الصوت."
+ "TaskAudioNormalizationDescription": "مسح الملفات لتطبيع بيانات الصوت.",
+ "TaskDownloadMissingLyrics": "تنزيل عبارات القصيدة"
}
diff --git a/Emby.Server.Implementations/Localization/Core/cs.json b/Emby.Server.Implementations/Localization/Core/cs.json
index 14cfeb71a..ad9e555a3 100644
--- a/Emby.Server.Implementations/Localization/Core/cs.json
+++ b/Emby.Server.Implementations/Localization/Core/cs.json
@@ -130,5 +130,7 @@
"TaskCleanCollectionsAndPlaylists": "Pročistit kolekce a seznamy přehrávání",
"TaskCleanCollectionsAndPlaylistsDescription": "Odstraní neexistující položky z kolekcí a seznamů přehrávání.",
"TaskAudioNormalization": "Normalizace zvuku",
- "TaskAudioNormalizationDescription": "Skenovat soubory za účelem normalizace zvuku."
+ "TaskAudioNormalizationDescription": "Skenovat soubory za účelem normalizace zvuku.",
+ "TaskDownloadMissingLyrics": "Stáhnout chybějící texty k písni",
+ "TaskDownloadMissingLyricsDescription": "Stáhne texty k písni"
}
diff --git a/Emby.Server.Implementations/Localization/Core/da.json b/Emby.Server.Implementations/Localization/Core/da.json
index e871a4362..69217dba0 100644
--- a/Emby.Server.Implementations/Localization/Core/da.json
+++ b/Emby.Server.Implementations/Localization/Core/da.json
@@ -93,7 +93,7 @@
"ValueSpecialEpisodeName": "Special - {0}",
"VersionNumber": "Version {0}",
"TaskDownloadMissingSubtitlesDescription": "Søger på internettet efter manglende undertekster baseret på metadata-konfigurationen.",
- "TaskDownloadMissingSubtitles": "Hent manglende undertekster",
+ "TaskDownloadMissingSubtitles": "Hentede medie mangler undertekster",
"TaskUpdatePluginsDescription": "Henter og installerer opdateringer for plugins, som er konfigurerede til at blive opdateret automatisk.",
"TaskUpdatePlugins": "Opdater Plugins",
"TaskCleanLogsDescription": "Sletter log-filer som er mere end {0} dage gamle.",
@@ -130,5 +130,7 @@
"TaskCleanCollectionsAndPlaylists": "Ryd op i samlinger og afspilningslister",
"TaskCleanCollectionsAndPlaylistsDescription": "Fjerner elementer fra samlinger og afspilningslister der ikke eksisterer længere.",
"TaskAudioNormalizationDescription": "Skanner filer for data vedrørende audio-normalisering.",
- "TaskAudioNormalization": "Audio-normalisering"
+ "TaskAudioNormalization": "Audio-normalisering",
+ "TaskDownloadMissingLyricsDescription": "Hentede sange mangler sangtekster",
+ "TaskDownloadMissingLyrics": "Hentede medie mangler sangtekster"
}
diff --git a/Emby.Server.Implementations/Localization/Core/de.json b/Emby.Server.Implementations/Localization/Core/de.json
index ce98979e6..bbb162c77 100644
--- a/Emby.Server.Implementations/Localization/Core/de.json
+++ b/Emby.Server.Implementations/Localization/Core/de.json
@@ -130,5 +130,7 @@
"TaskCleanCollectionsAndPlaylists": "Sammlungen und Playlisten aufräumen",
"TaskCleanCollectionsAndPlaylistsDescription": "Lösche nicht mehr vorhandene Einträge aus den Sammlungen und Playlisten.",
"TaskAudioNormalization": "Audio Normalisierung",
- "TaskAudioNormalizationDescription": "Durchsucht Dateien nach Audionormalisierungsdaten."
+ "TaskAudioNormalizationDescription": "Durchsucht Dateien nach Audionormalisierungsdaten.",
+ "TaskDownloadMissingLyricsDescription": "Lädt Songtexte herunter",
+ "TaskDownloadMissingLyrics": "Fehlende Songtexte herunterladen"
}
diff --git a/Emby.Server.Implementations/Localization/Core/en-GB.json b/Emby.Server.Implementations/Localization/Core/en-GB.json
index 75285fe8e..65df1e45b 100644
--- a/Emby.Server.Implementations/Localization/Core/en-GB.json
+++ b/Emby.Server.Implementations/Localization/Core/en-GB.json
@@ -130,5 +130,7 @@
"TaskCleanCollectionsAndPlaylists": "Clean up collections and playlists",
"TaskCleanCollectionsAndPlaylistsDescription": "Removes items from collections and playlists that no longer exist.",
"TaskAudioNormalization": "Audio Normalisation",
- "TaskAudioNormalizationDescription": "Scans files for audio normalisation data."
+ "TaskAudioNormalizationDescription": "Scans files for audio normalisation data.",
+ "TaskDownloadMissingLyrics": "Download missing lyrics",
+ "TaskDownloadMissingLyricsDescription": "Downloads lyrics for songs"
}
diff --git a/Emby.Server.Implementations/Localization/Core/es-AR.json b/Emby.Server.Implementations/Localization/Core/es-AR.json
index 9433da28b..b926d9d30 100644
--- a/Emby.Server.Implementations/Localization/Core/es-AR.json
+++ b/Emby.Server.Implementations/Localization/Core/es-AR.json
@@ -130,5 +130,7 @@
"TaskAudioNormalization": "Normalización de audio",
"TaskAudioNormalizationDescription": "Escanea archivos en busca de datos de normalización de audio.",
"TaskCleanCollectionsAndPlaylists": "Limpiar colecciones y listas de reproducción",
- "TaskCleanCollectionsAndPlaylistsDescription": "Elimina elementos de colecciones y listas de reproducción que ya no existen."
+ "TaskCleanCollectionsAndPlaylistsDescription": "Elimina elementos de colecciones y listas de reproducción que ya no existen.",
+ "TaskDownloadMissingLyrics": "Descargar letra faltante",
+ "TaskDownloadMissingLyricsDescription": "Descarga letras de canciones"
}
diff --git a/Emby.Server.Implementations/Localization/Core/es.json b/Emby.Server.Implementations/Localization/Core/es.json
index 13e007b4c..210ee4446 100644
--- a/Emby.Server.Implementations/Localization/Core/es.json
+++ b/Emby.Server.Implementations/Localization/Core/es.json
@@ -130,5 +130,7 @@
"TaskCleanCollectionsAndPlaylists": "Limpiar colecciones y listas de reproducción",
"TaskCleanCollectionsAndPlaylistsDescription": "Elimina elementos de colecciones y listas de reproducción que ya no existen.",
"TaskAudioNormalization": "Normalización de audio",
- "TaskAudioNormalizationDescription": "Escanear archivos para obtener datos de normalización."
+ "TaskAudioNormalizationDescription": "Escanear archivos para obtener datos de normalización.",
+ "TaskDownloadMissingLyricsDescription": "Descargar letras para las canciones",
+ "TaskDownloadMissingLyrics": "Descargar letras faltantes"
}
diff --git a/Emby.Server.Implementations/Localization/Core/es_419.json b/Emby.Server.Implementations/Localization/Core/es_419.json
index e7deefbb0..b458ed423 100644
--- a/Emby.Server.Implementations/Localization/Core/es_419.json
+++ b/Emby.Server.Implementations/Localization/Core/es_419.json
@@ -129,5 +129,7 @@
"TaskAudioNormalization": "Normalización de audio",
"TaskCleanCollectionsAndPlaylistsDescription": "Quita elementos que ya no existen de colecciones y listas de reproducción.",
"TaskAudioNormalizationDescription": "Analiza los archivos para normalizar el audio.",
- "TaskCleanCollectionsAndPlaylists": "Limpieza de colecciones y listas de reproducción"
+ "TaskCleanCollectionsAndPlaylists": "Limpieza de colecciones y listas de reproducción",
+ "TaskDownloadMissingLyrics": "Descargar letra faltante",
+ "TaskDownloadMissingLyricsDescription": "Descarga letras de canciones"
}
diff --git a/Emby.Server.Implementations/Localization/Core/fa.json b/Emby.Server.Implementations/Localization/Core/fa.json
index ce5177d1f..b0ddec104 100644
--- a/Emby.Server.Implementations/Localization/Core/fa.json
+++ b/Emby.Server.Implementations/Localization/Core/fa.json
@@ -128,5 +128,9 @@
"TaskRefreshTrickplayImages": "تولید تصاویر Trickplay",
"TaskRefreshTrickplayImagesDescription": "تولید پیش‌نمایش های trickplay برای ویدیو های فعال شده در کتابخانه.",
"TaskCleanCollectionsAndPlaylists": "پاکسازی مجموعه ها و لیست پخش",
- "TaskCleanCollectionsAndPlaylistsDescription": "موارد را از مجموعه ها و لیست پخش هایی که دیگر وجود ندارند حذف میکند."
+ "TaskCleanCollectionsAndPlaylistsDescription": "موارد را از مجموعه ها و لیست پخش هایی که دیگر وجود ندارند حذف میکند.",
+ "TaskAudioNormalizationDescription": "بررسی فایل برای داده‌های نرمال کردن صدا.",
+ "TaskDownloadMissingLyrics": "دانلود متن‌های ناموجود",
+ "TaskDownloadMissingLyricsDescription": "دانلود متن شعر‌ها",
+ "TaskAudioNormalization": "نرمال کردن صدا"
}
diff --git a/Emby.Server.Implementations/Localization/Core/fr.json b/Emby.Server.Implementations/Localization/Core/fr.json
index e8c0f7fc3..1dba78add 100644
--- a/Emby.Server.Implementations/Localization/Core/fr.json
+++ b/Emby.Server.Implementations/Localization/Core/fr.json
@@ -130,5 +130,7 @@
"TaskCleanCollectionsAndPlaylists": "Nettoyer les collections et les listes de lecture",
"TaskCleanCollectionsAndPlaylistsDescription": "Supprime les éléments des collections et des listes de lecture qui n'existent plus.",
"TaskAudioNormalization": "Normalisation audio",
- "TaskAudioNormalizationDescription": "Analyse les fichiers à la recherche de données de normalisation audio."
+ "TaskAudioNormalizationDescription": "Analyse les fichiers à la recherche de données de normalisation audio.",
+ "TaskDownloadMissingLyricsDescription": "Téléchargement des paroles des chansons",
+ "TaskDownloadMissingLyrics": "Télécharger les paroles des chansons manquantes"
}
diff --git a/Emby.Server.Implementations/Localization/Core/it.json b/Emby.Server.Implementations/Localization/Core/it.json
index 0e694af02..961d1a0df 100644
--- a/Emby.Server.Implementations/Localization/Core/it.json
+++ b/Emby.Server.Implementations/Localization/Core/it.json
@@ -130,5 +130,7 @@
"TaskCleanCollectionsAndPlaylists": "Ripulire le collezioni e le playlist",
"TaskCleanCollectionsAndPlaylistsDescription": "Rimuove gli elementi dalle collezioni e dalle playlist che non esistono più.",
"TaskAudioNormalization": "Normalizzazione dell'audio",
- "TaskAudioNormalizationDescription": "Scansiona i file alla ricerca dei dati per la normalizzazione dell'audio."
+ "TaskAudioNormalizationDescription": "Scansiona i file alla ricerca dei dati per la normalizzazione dell'audio.",
+ "TaskDownloadMissingLyricsDescription": "Scarica testi per le canzoni",
+ "TaskDownloadMissingLyrics": "Scarica testi mancanti"
}
diff --git a/Emby.Server.Implementations/Localization/Core/kw.json b/Emby.Server.Implementations/Localization/Core/kw.json
new file mode 100644
index 000000000..ffb4345c8
--- /dev/null
+++ b/Emby.Server.Implementations/Localization/Core/kw.json
@@ -0,0 +1,135 @@
+{
+ "Collections": "Kuntellow",
+ "DeviceOfflineWithName": "{0} re anjunyas",
+ "External": "A-ves",
+ "Folders": "Plegellow",
+ "HeaderFavoriteAlbums": "Albomow Drudh",
+ "HeaderFavoriteArtists": "Artydhyon Drudh",
+ "HeaderFavoriteEpisodes": "Towlennow Drudh",
+ "HeaderFavoriteSongs": "Kanow Drudh",
+ "HeaderRecordingGroups": "Bagasow Rekordya",
+ "HearingImpaired": "Klewans Aperys",
+ "HomeVideos": "Gwydhyow Tre",
+ "Inherit": "Herya",
+ "LabelRunningTimeValue": "Prys ow ponya: {0}",
+ "Latest": "Diwettha",
+ "MessageApplicationUpdated": "Servell Jellyfin re beu nowedhys",
+ "MessageApplicationUpdatedTo": "Servell Jellyfin re beu nowedhys dhe {0}",
+ "MessageNamedServerConfigurationUpdatedWithValue": "Rann dewisyans servell {0} re beu nowedhys",
+ "MixedContent": "Dalgh kemmyskys",
+ "Movies": "Fylmow",
+ "MusicVideos": "Gwydhyow Ilow",
+ "NameSeasonUnknown": "Seson Anwodhvedhys",
+ "NotificationOptionAudioPlayback": "Seneans dallethys",
+ "NotificationOptionAudioPlaybackStopped": "Seneans hedhys",
+ "NotificationOptionPluginError": "Defowt ystynnans",
+ "NotificationOptionPluginUninstalled": "Ystynnans anynstallys",
+ "NotificationOptionPluginUpdateInstalled": "Nowedheans ystynnans ynstallys",
+ "Application": "Gweythres",
+ "Favorites": "Moyha Kerys",
+ "Forced": "Konstrynys",
+ "Albums": "Albomow",
+ "Books": "Lyvrow",
+ "Channels": "Kanolyow",
+ "AppDeviceValues": "App: {0}, Devis: {1}",
+ "Artists": "Artyhdyon",
+ "HeaderAlbumArtists": "Albom artydhyon",
+ "HeaderNextUp": "Nessa",
+ "CameraImageUploadedFrom": "Skeusen kamera nowydh re beu ughkargys a-dhyworth {0}",
+ "ChapterNameValue": "Chaptra {0}",
+ "FailedLoginAttemptWithUserName": "Assay omgelm fyllys a-dhyworth {0}",
+ "AuthenticationSucceededWithUserName": "{0} omgelmys yn sewen",
+ "Default": "Defowt",
+ "DeviceOnlineWithName": "{0} yw junys",
+ "ItemRemovedWithName": "{0} a veu dileys a-dhyworth an lyverva",
+ "LabelIpAddressValue": "Trigva PK: {)}",
+ "Music": "Ilow",
+ "HeaderContinueWatching": "Pesya Ow Kweles",
+ "NameSeasonNumber": "Seson {0}",
+ "NotificationOptionApplicationUpdateInstalled": "Nowedheans gweythres ynstallys",
+ "NotificationOptionCameraImageUploaded": "Skeusen kamera ughkargys",
+ "HeaderFavoriteShows": "Diskwedhyansow Drudh",
+ "HeaderLiveTV": "PW Yn Fyw",
+ "MessageServerConfigurationUpdated": "Dewisyans servell re beu nowedhys",
+ "ItemAddedWithName": "{0} a veu keworrys dhe'n lyverva",
+ "NameInstallFailed": "{0} ynstallyans fyllys",
+ "NotificationOptionNewLibraryContent": "Dalgh nowydh keworrys",
+ "NewVersionIsAvailable": "Yma versyon nowydh a Servell Jellyfin neb yw kavadow rag iskarga.",
+ "NotificationOptionApplicationUpdateAvailable": "Nowedheans gweythres kavadow",
+ "NotificationOptionInstallationFailed": "Defowt ynstallyans",
+ "Genres": "Eghennow",
+ "NotificationOptionPluginInstalled": "Ystynnans ynstallys",
+ "NotificationOptionServerRestartRequired": "Dastalleth servell yw res",
+ "StartupEmbyServerIsLoading": "Yma Servell Jellyfin ow kargya. Assay arta yn berr mar pleg.",
+ "SubtitleDownloadFailureFromForItem": "Istitlow a fyllis iskarga a-dhyworth {0] rag {1}",
+ "System": "Kevreyth",
+ "User": "Devnydhyer",
+ "UserDeletedWithName": "Devnydhyer {0} re beu dileys",
+ "UserLockedOutWithName": "Devnydhyer {0} re beu alhwedhys yn-mes",
+ "UserStoppedPlayingItemWithValues": "{0} re worfennas gwari {1} war {2}",
+ "UserOfflineFromDevice": "{0} re anjunyas a-dhyworth {1}",
+ "UserOnlineFromDevice": "{0} yw warlinen a-dhyworth {1}",
+ "NotificationOptionUserLockedOut": "Devnydhyer yw alhwedhys yn-mes",
+ "Photos": "Skeusennow",
+ "Playlists": "Rolyow-gwari",
+ "Plugin": "Ystynnans",
+ "PluginInstalledWithName": "{0} a veu ynstallys",
+ "UserPolicyUpdatedWithName": "Polici devnydhyer re beu nowedhys rag {0}",
+ "PluginUpdatedWithName": "{0} a veu nowedhys",
+ "ScheduledTaskFailedWithName": "{0} a fyllis",
+ "Songs": "Kanow",
+ "Sync": "Kesseni",
+ "TvShows": "Towlennow PW",
+ "Undefined": "Anstyrys",
+ "UserCreatedWithName": "Devnydhyer {0} re beu gwruthys",
+ "UserDownloadingItemWithValues": "Yma {0} owth iskarga {1}",
+ "UserPasswordChangedWithName": "Ger-tremena re beu chanjys rag devnydhyer {0}",
+ "UserStartedPlayingItemWithValues": "Yma {0} ow kwari {1} war {2}",
+ "ValueHasBeenAddedToLibrary": "{0} re beu keworrys dhe'th lyverva media",
+ "VersionNumber": "Versyon {0}",
+ "TasksLibraryCategory": "Lyverva",
+ "TaskCleanActivityLog": "Glanhe Kovlyver Gwrians",
+ "TaskRefreshPeople": "Disegha Tus",
+ "TaskRefreshLibrary": "Arhwilas Lyverva Media",
+ "TaskCleanTranscodeDescription": "Y hwra dilea restrennow treylya neg a veu gwrys kyns nans yw dydh.",
+ "NotificationOptionVideoPlaybackStopped": "Gwareans gwydhyow yw hedhys",
+ "NotificationOptionVideoPlayback": "Gwareans gwydhyow yw dallethys",
+ "PluginUninstalledWithName": "{0} a veu anynstallys",
+ "NotificationOptionTaskFailed": "Defowt oberen towlennys",
+ "ProviderValue": "Provier: {0}",
+ "ScheduledTaskStartedWithName": "{0} a dhallathas",
+ "ServerNameNeedsToBeRestarted": "Yma edhom dhe {0} a vos dastallathys",
+ "ValueSpecialEpisodeName": "Arbennik - {0}",
+ "TasksMaintenanceCategory": "Mentons",
+ "TasksApplicationCategory": "Gweythres",
+ "TasksChannelsCategory": "Kanolyow Kesrosweyth",
+ "TaskCleanLogs": "Glanhe Kevarwodhyador Kovlyver",
+ "TaskAudioNormalization": "Normalheans Klewans",
+ "TaskRefreshChannels": "Disegha Kanolyow",
+ "TaskCleanTranscode": "Glanhe Kevarwodhyador Treylya",
+ "TaskUpdatePlugins": "Nowedhi Ystynansow",
+ "Shows": "Diskwedhyansow",
+ "TaskCleanCache": "Glanhe Kevarwodhyador Gwithva",
+ "TaskCleanActivityLogDescription": "Y hwra dilea lin kovlyver gwrians kottha ages an bloodh dewisys.",
+ "TaskCleanCacheDescription": "Y hwra dilea restrennow gwithva nag yw res rag an kevreyth.",
+ "TaskRefreshPeopleDescription": "Y hwra nowedhi metadata rag gwarioryon ha kevarwodhoryon yn dha lyverva media.",
+ "TaskRefreshChapterImages": "Kuntel Imajys Chaptra",
+ "TaskRefreshChapterImagesDescription": "Y hwra ewines meus rag gwydhyowyow gans chaptraow.",
+ "TaskRefreshTrickplayImagesDescription": "Y hwra kynwelyow trickplay rag gwydhyowyow yn lyvervaow gallosegys.",
+ "TaskRefreshTrickplayImages": "Dinythi Imajys Trickplay",
+ "TaskCleanLogsDescription": "Y hwra dilea restrennow kovlyver a veu gwrys kyns nans yw {0} dydh.",
+ "TaskDownloadMissingLyrics": "Iskarga geryow kellys",
+ "TaskUpdatePluginsDescription": "Y hwra iskarga hag ynstallya nowedheansow rag ystynansow neb yw dewisys dhe nowedhi yn awtomatek.",
+ "TaskDownloadMissingSubtitles": "Iskarga istitlow kellys",
+ "TaskRefreshChannelsDescription": "Y hwra disegha kedhlow kanolyow kesrosweyth.",
+ "TaskDownloadMissingLyricsDescription": "Y hwra iskarga geryow rag kanow",
+ "TaskDownloadMissingSubtitlesDescription": "Y hwra hwilas an kesrosweyth rag istitlow kellys a-dhywoth dewisyans metadata.",
+ "TaskOptimizeDatabase": "Gwellhe selvanylyon",
+ "TaskOptimizeDatabaseDescription": "Y hwra kesstrotha ha berrhe efander rydh. Martesen y hwra gwellhe gwryth mar kwre'ta an oberen ma wosa ty dhe arhwilas an lyverva, po neb chanj aral neb a brof chanjyansow selvanylyon.",
+ "TaskAudioNormalizationDescription": "Y hwra arhwilas restrennow rag manylyon normalheans klewans.",
+ "TaskRefreshLibraryDescription": "Y hwra arhwilas dha lyverva media rag restrennow nowydh ha disegha metamanylyon.",
+ "TaskCleanCollectionsAndPlaylists": "Glanhe kuntellow ha rolyow-gwari",
+ "TaskKeyframeExtractor": "Estennell Framalhwedh",
+ "TaskCleanCollectionsAndPlaylistsDescription": "Y hwra dilea taklow a-dhyworth kuntellow ha rolyow-gwari na vos na moy.",
+ "TaskKeyframeExtractorDescription": "Y hwra kuntel framyowalhwedh a-dhyworth restrennow gwydhyowyow rag gul rolyow-gwari HLS moy poran. Martesen y hwra an oberen ma ow ponya rag termyn hir."
+}
diff --git a/Emby.Server.Implementations/Localization/Core/nb.json b/Emby.Server.Implementations/Localization/Core/nb.json
index b66818ddc..747652538 100644
--- a/Emby.Server.Implementations/Localization/Core/nb.json
+++ b/Emby.Server.Implementations/Localization/Core/nb.json
@@ -130,5 +130,7 @@
"TaskCleanCollectionsAndPlaylists": "Rydd kolleksjoner og spillelister",
"TaskAudioNormalization": "Lyd Normalisering",
"TaskAudioNormalizationDescription": "Skan filer for lyd normaliserende data",
- "TaskCleanCollectionsAndPlaylistsDescription": "Fjerner elementer fra kolleksjoner og spillelister som ikke lengere finnes"
+ "TaskCleanCollectionsAndPlaylistsDescription": "Fjerner elementer fra kolleksjoner og spillelister som ikke lengere finnes",
+ "TaskDownloadMissingLyrics": "Last ned manglende tekster",
+ "TaskDownloadMissingLyricsDescription": "Last ned sangtekster"
}
diff --git a/Emby.Server.Implementations/Localization/Core/nl.json b/Emby.Server.Implementations/Localization/Core/nl.json
index 0993c1a55..39e7cd546 100644
--- a/Emby.Server.Implementations/Localization/Core/nl.json
+++ b/Emby.Server.Implementations/Localization/Core/nl.json
@@ -130,5 +130,7 @@
"TaskCleanCollectionsAndPlaylists": "Collecties en afspeellijsten opruimen",
"TaskCleanCollectionsAndPlaylistsDescription": "Verwijdert niet langer bestaande items uit collecties en afspeellijsten.",
"TaskAudioNormalization": "Geluidsnormalisatie",
- "TaskAudioNormalizationDescription": "Scant bestanden op gegevens voor geluidsnormalisatie."
+ "TaskAudioNormalizationDescription": "Scant bestanden op gegevens voor geluidsnormalisatie.",
+ "TaskDownloadMissingLyrics": "Ontbrekende liedteksten downloaden",
+ "TaskDownloadMissingLyricsDescription": "Downloadt liedteksten"
}
diff --git a/Emby.Server.Implementations/Localization/Core/pl.json b/Emby.Server.Implementations/Localization/Core/pl.json
index f36385be2..a24a837ab 100644
--- a/Emby.Server.Implementations/Localization/Core/pl.json
+++ b/Emby.Server.Implementations/Localization/Core/pl.json
@@ -130,5 +130,7 @@
"TaskCleanCollectionsAndPlaylistsDescription": "Usuwa elementy z kolekcji i list odtwarzania, które już nie istnieją.",
"TaskCleanCollectionsAndPlaylists": "Oczyść kolekcje i listy odtwarzania",
"TaskAudioNormalization": "Normalizacja dźwięku",
- "TaskAudioNormalizationDescription": "Skanuje pliki w poszukiwaniu danych normalizacji dźwięku."
+ "TaskAudioNormalizationDescription": "Skanuje pliki w poszukiwaniu danych normalizacji dźwięku.",
+ "TaskDownloadMissingLyrics": "Pobierz brakujące słowa",
+ "TaskDownloadMissingLyricsDescription": "Pobierz słowa piosenek"
}
diff --git a/Emby.Server.Implementations/Localization/Core/ru.json b/Emby.Server.Implementations/Localization/Core/ru.json
index 3eb1e0468..01b8bfbe2 100644
--- a/Emby.Server.Implementations/Localization/Core/ru.json
+++ b/Emby.Server.Implementations/Localization/Core/ru.json
@@ -130,5 +130,7 @@
"TaskCleanCollectionsAndPlaylists": "Очистка коллекций и списков воспроизведения",
"TaskCleanCollectionsAndPlaylistsDescription": "Удаляет элементы из коллекций и списков воспроизведения, которые больше не существуют.",
"TaskAudioNormalization": "Нормализация звука",
- "TaskAudioNormalizationDescription": "Сканирует файлы на наличие данных о нормализации звука."
+ "TaskAudioNormalizationDescription": "Сканирует файлы на наличие данных о нормализации звука.",
+ "TaskDownloadMissingLyrics": "Загрузить недостающий текст",
+ "TaskDownloadMissingLyricsDescription": "Загружает текст песен"
}
diff --git a/Emby.Server.Implementations/Localization/Core/sv.json b/Emby.Server.Implementations/Localization/Core/sv.json
index f40c4478a..a4e2302d1 100644
--- a/Emby.Server.Implementations/Localization/Core/sv.json
+++ b/Emby.Server.Implementations/Localization/Core/sv.json
@@ -16,7 +16,7 @@
"Folders": "Mappar",
"Genres": "Genrer",
"HeaderAlbumArtists": "Albumartister",
- "HeaderContinueWatching": "Fortsätt titta på",
+ "HeaderContinueWatching": "Fortsätt titta",
"HeaderFavoriteAlbums": "Favoritalbum",
"HeaderFavoriteArtists": "Favoritartister",
"HeaderFavoriteEpisodes": "Favoritavsnitt",
@@ -130,5 +130,7 @@
"TaskCleanCollectionsAndPlaylists": "Rensa upp samlingar och spellistor",
"TaskAudioNormalization": "Ljudnormalisering",
"TaskCleanCollectionsAndPlaylistsDescription": "Tar bort objekt från samlingar och spellistor som inte längre finns.",
- "TaskAudioNormalizationDescription": "Skannar filer för ljudnormaliseringsdata."
+ "TaskAudioNormalizationDescription": "Skannar filer för ljudnormaliseringsdata.",
+ "TaskDownloadMissingLyrics": "Ladda ner saknad låttext",
+ "TaskDownloadMissingLyricsDescription": "Laddar ner låttexter"
}
diff --git a/Emby.Server.Implementations/Localization/Core/uk.json b/Emby.Server.Implementations/Localization/Core/uk.json
index 18073287b..97bad4532 100644
--- a/Emby.Server.Implementations/Localization/Core/uk.json
+++ b/Emby.Server.Implementations/Localization/Core/uk.json
@@ -129,5 +129,7 @@
"TaskCleanCollectionsAndPlaylists": "Очистити колекції і списки відтворення",
"TaskCleanCollectionsAndPlaylistsDescription": "Видаляє елементи з колекцій і списків відтворення, які більше не існують.",
"TaskAudioNormalizationDescription": "Сканує файли на наявність даних для нормалізації звуку.",
- "TaskAudioNormalization": "Нормалізація аудіо"
+ "TaskAudioNormalization": "Нормалізація аудіо",
+ "TaskDownloadMissingLyrics": "Завантажити відсутні тексти пісень",
+ "TaskDownloadMissingLyricsDescription": "Завантаження текстів пісень"
}
diff --git a/Emby.Server.Implementations/Localization/Core/vi.json b/Emby.Server.Implementations/Localization/Core/vi.json
index 4bedfe3b2..32e2f4bab 100644
--- a/Emby.Server.Implementations/Localization/Core/vi.json
+++ b/Emby.Server.Implementations/Localization/Core/vi.json
@@ -129,5 +129,7 @@
"TaskCleanCollectionsAndPlaylists": "Dọn dẹp bộ sưu tập và danh sách phát",
"TaskCleanCollectionsAndPlaylistsDescription": "Xóa các mục khỏi bộ sưu tập và danh sách phát không còn tồn tại.",
"TaskAudioNormalization": "Chuẩn Hóa Âm Thanh",
- "TaskAudioNormalizationDescription": "Quét tập tin để tìm dữ liệu chuẩn hóa âm thanh."
+ "TaskAudioNormalizationDescription": "Quét tập tin để tìm dữ liệu chuẩn hóa âm thanh.",
+ "TaskDownloadMissingLyricsDescription": "Tải xuống lời cho bài hát",
+ "TaskDownloadMissingLyrics": "Tải xuống lời bị thiếu"
}
diff --git a/Emby.Server.Implementations/Localization/Core/zh-CN.json b/Emby.Server.Implementations/Localization/Core/zh-CN.json
index 808f73793..4bec590fb 100644
--- a/Emby.Server.Implementations/Localization/Core/zh-CN.json
+++ b/Emby.Server.Implementations/Localization/Core/zh-CN.json
@@ -130,5 +130,7 @@
"TaskCleanCollectionsAndPlaylists": "清理合集和播放列表",
"TaskCleanCollectionsAndPlaylistsDescription": "清理合集和播放列表中已不存在的项目。",
"TaskAudioNormalization": "音频标准化",
- "TaskAudioNormalizationDescription": "扫描文件以寻找音频标准化数据。"
+ "TaskAudioNormalizationDescription": "扫描文件以寻找音频标准化数据。",
+ "TaskDownloadMissingLyrics": "下载缺失的歌词",
+ "TaskDownloadMissingLyricsDescription": "下载歌曲歌词"
}
diff --git a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
index 40e1bbf15..9b342cfbe 100644
--- a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
@@ -1,8 +1,7 @@
#nullable disable
-#pragma warning disable CS1591
-
using System;
+using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
@@ -24,51 +23,15 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// </summary>
public class ScheduledTaskWorker : IScheduledTaskWorker
{
- /// <summary>
- /// The options for the json Serializer.
- /// </summary>
private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options;
-
- /// <summary>
- /// Gets or sets the application paths.
- /// </summary>
- /// <value>The application paths.</value>
private readonly IApplicationPaths _applicationPaths;
-
- /// <summary>
- /// Gets or sets the logger.
- /// </summary>
- /// <value>The logger.</value>
private readonly ILogger _logger;
-
- /// <summary>
- /// Gets or sets the task manager.
- /// </summary>
- /// <value>The task manager.</value>
private readonly ITaskManager _taskManager;
-
- /// <summary>
- /// The _last execution result sync lock.
- /// </summary>
- private readonly object _lastExecutionResultSyncLock = new object();
-
- private bool _readFromFile = false;
-
- /// <summary>
- /// The _last execution result.
- /// </summary>
+ private readonly object _lastExecutionResultSyncLock = new();
+ private bool _readFromFile;
private TaskResult _lastExecutionResult;
-
private Task _currentTask;
-
- /// <summary>
- /// The _triggers.
- /// </summary>
private Tuple<TaskTriggerInfo, ITaskTrigger>[] _triggers;
-
- /// <summary>
- /// The _id.
- /// </summary>
private string _id;
/// <summary>
@@ -104,18 +67,13 @@ namespace Emby.Server.Implementations.ScheduledTasks
InitTriggerEvents();
}
+ /// <inheritdoc />
public event EventHandler<GenericEventArgs<double>> TaskProgress;
- /// <summary>
- /// Gets the scheduled task.
- /// </summary>
- /// <value>The scheduled task.</value>
+ /// <inheritdoc />
public IScheduledTask ScheduledTask { get; private set; }
- /// <summary>
- /// Gets the last execution result.
- /// </summary>
- /// <value>The last execution result.</value>
+ /// <inheritdoc />
public TaskResult LastExecutionResult
{
get
@@ -169,22 +127,13 @@ namespace Emby.Server.Implementations.ScheduledTasks
}
}
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
+ /// <inheritdoc />
public string Name => ScheduledTask.Name;
- /// <summary>
- /// Gets the description.
- /// </summary>
- /// <value>The description.</value>
+ /// <inheritdoc />
public string Description => ScheduledTask.Description;
- /// <summary>
- /// Gets the category.
- /// </summary>
- /// <value>The category.</value>
+ /// <inheritdoc />
public string Category => ScheduledTask.Category;
/// <summary>
@@ -199,10 +148,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// <value>The current execution start time.</value>
private DateTime CurrentExecutionStartTime { get; set; }
- /// <summary>
- /// Gets the state.
- /// </summary>
- /// <value>The state.</value>
+ /// <inheritdoc />
public TaskState State
{
get
@@ -218,10 +164,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
}
}
- /// <summary>
- /// Gets the current progress.
- /// </summary>
- /// <value>The current progress.</value>
+ /// <inheritdoc />
public double? CurrentProgress { get; private set; }
/// <summary>
@@ -247,12 +190,8 @@ namespace Emby.Server.Implementations.ScheduledTasks
}
}
- /// <summary>
- /// Gets or sets the triggers that define when the task will run.
- /// </summary>
- /// <value>The triggers.</value>
- /// <exception cref="ArgumentNullException"><c>value</c> is <c>null</c>.</exception>
- public TaskTriggerInfo[] Triggers
+ /// <inheritdoc />
+ public IReadOnlyList<TaskTriggerInfo> Triggers
{
get
{
@@ -272,10 +211,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
}
}
- /// <summary>
- /// Gets the unique id.
- /// </summary>
- /// <value>The unique id.</value>
+ /// <inheritdoc />
public string Id
{
get
@@ -290,6 +226,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
ReloadTriggerEvents(true);
}
+ /// <inheritdoc />
public void ReloadTriggerEvents()
{
ReloadTriggerEvents(false);
@@ -529,14 +466,14 @@ namespace Emby.Server.Implementations.ScheduledTasks
}
catch
{
- return new TaskTriggerInfo[]
- {
- new TaskTriggerInfo
+ return
+ [
+ new()
{
IntervalTicks = TimeSpan.FromDays(1).Ticks,
Type = TaskTriggerInfo.TriggerInterval
}
- };
+ ];
}
}
@@ -589,9 +526,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
((TaskManager)_taskManager).OnTaskCompleted(this, result);
}
- /// <summary>
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
- /// </summary>
+ /// <inheritdoc />
public void Dispose()
{
Dispose(true);
diff --git a/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs b/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs
index 42c30c959..a5e4104ff 100644
--- a/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs
@@ -1,5 +1,3 @@
-#pragma warning disable CS1591
-
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@@ -41,21 +39,16 @@ namespace Emby.Server.Implementations.ScheduledTasks
ScheduledTasks = Array.Empty<IScheduledTaskWorker>();
}
+ /// <inheritdoc />
public event EventHandler<GenericEventArgs<IScheduledTaskWorker>>? TaskExecuting;
+ /// <inheritdoc />
public event EventHandler<TaskCompletionEventArgs>? TaskCompleted;
- /// <summary>
- /// Gets the list of Scheduled Tasks.
- /// </summary>
- /// <value>The scheduled tasks.</value>
- public IScheduledTaskWorker[] ScheduledTasks { get; private set; }
+ /// <inheritdoc />
+ public IReadOnlyList<IScheduledTaskWorker> ScheduledTasks { get; private set; }
- /// <summary>
- /// Cancels if running and queue.
- /// </summary>
- /// <typeparam name="T">The task type.</typeparam>
- /// <param name="options">Task options.</param>
+ /// <inheritdoc />
public void CancelIfRunningAndQueue<T>(TaskOptions options)
where T : IScheduledTask
{
@@ -65,16 +58,14 @@ namespace Emby.Server.Implementations.ScheduledTasks
QueueScheduledTask<T>(options);
}
+ /// <inheritdoc />
public void CancelIfRunningAndQueue<T>()
where T : IScheduledTask
{
CancelIfRunningAndQueue<T>(new TaskOptions());
}
- /// <summary>
- /// Cancels if running.
- /// </summary>
- /// <typeparam name="T">The task type.</typeparam>
+ /// <inheritdoc />
public void CancelIfRunning<T>()
where T : IScheduledTask
{
@@ -82,11 +73,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
((ScheduledTaskWorker)task).CancelIfRunning();
}
- /// <summary>
- /// Queues the scheduled task.
- /// </summary>
- /// <typeparam name="T">The task type.</typeparam>
- /// <param name="options">Task options.</param>
+ /// <inheritdoc />
public void QueueScheduledTask<T>(TaskOptions options)
where T : IScheduledTask
{
@@ -102,12 +89,14 @@ namespace Emby.Server.Implementations.ScheduledTasks
}
}
+ /// <inheritdoc />
public void QueueScheduledTask<T>()
where T : IScheduledTask
{
QueueScheduledTask<T>(new TaskOptions());
}
+ /// <inheritdoc />
public void QueueIfNotRunning<T>()
where T : IScheduledTask
{
@@ -119,6 +108,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
}
}
+ /// <inheritdoc />
public void Execute<T>()
where T : IScheduledTask
{
@@ -144,11 +134,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
}
}
- /// <summary>
- /// Queues the scheduled task.
- /// </summary>
- /// <param name="task">The task.</param>
- /// <param name="options">The task options.</param>
+ /// <inheritdoc />
public void QueueScheduledTask(IScheduledTask task, TaskOptions options)
{
var scheduledTask = ScheduledTasks.FirstOrDefault(t => t.ScheduledTask.GetType() == task.GetType());
@@ -186,10 +172,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
}
}
- /// <summary>
- /// Adds the tasks.
- /// </summary>
- /// <param name="tasks">The tasks.</param>
+ /// <inheritdoc />
public void AddTasks(IEnumerable<IScheduledTask> tasks)
{
var list = tasks.Select(t => new ScheduledTaskWorker(t, _applicationPaths, this, _logger));
@@ -197,9 +180,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
ScheduledTasks = ScheduledTasks.Concat(list).ToArray();
}
- /// <summary>
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
- /// </summary>
+ /// <inheritdoc />
public void Dispose()
{
Dispose(true);
@@ -218,11 +199,13 @@ namespace Emby.Server.Implementations.ScheduledTasks
}
}
+ /// <inheritdoc />
public void Cancel(IScheduledTaskWorker task)
{
((ScheduledTaskWorker)task).Cancel();
}
+ /// <inheritdoc />
public Task Execute(IScheduledTaskWorker task, TaskOptions options)
{
return ((ScheduledTaskWorker)task).Execute(options);
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/AudioNormalizationTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/AudioNormalizationTask.cs
index 301c04915..eb6afe05d 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/AudioNormalizationTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/AudioNormalizationTask.cs
@@ -142,7 +142,7 @@ public partial class AudioNormalizationTask : IScheduledTask
continue;
}
- t.LUFS = await CalculateLUFSAsync(string.Format(CultureInfo.InvariantCulture, "-i \"{0}\"", t.Path.Replace("\"", "\\\"", StringComparison.Ordinal)), cancellationToken);
+ t.LUFS = await CalculateLUFSAsync(string.Format(CultureInfo.InvariantCulture, "-i \"{0}\"", t.Path.Replace("\"", "\\\"", StringComparison.Ordinal)), cancellationToken).ConfigureAwait(false);
}
_itemRepository.SaveItems(tracks, cancellationToken);
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs
index 36456504b..cb3f5b836 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs
@@ -36,13 +36,13 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
/// <summary>
/// Initializes a new instance of the <see cref="ChapterImagesTask" /> class.
/// </summary>
- /// <param name="logger">The logger.</param>.
- /// <param name="libraryManager">The library manager.</param>.
- /// <param name="itemRepo">The item repository.</param>
- /// <param name="appPaths">The application paths.</param>
- /// <param name="encodingManager">The encoding manager.</param>
- /// <param name="fileSystem">The filesystem.</param>
- /// <param name="localization">The localization manager.</param>
+ /// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param>
+ /// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
+ /// <param name="itemRepo">Instance of the <see cref="IItemRepository"/> interface.</param>
+ /// <param name="appPaths">Instance of the <see cref="IApplicationPaths"/> interface.</param>
+ /// <param name="encodingManager">Instance of the <see cref="IEncodingManager"/> interface.</param>
+ /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
+ /// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
public ChapterImagesTask(
ILogger<ChapterImagesTask> logger,
ILibraryManager libraryManager,
@@ -76,15 +76,15 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
/// <inheritdoc />
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
- return new[]
- {
+ return
+ [
new TaskTriggerInfo
{
Type = TaskTriggerInfo.TriggerDaily,
TimeOfDayTicks = TimeSpan.FromHours(2).Ticks,
MaxRuntimeTicks = TimeSpan.FromHours(4).Ticks
}
- };
+ ];
}
/// <inheritdoc />
@@ -92,18 +92,18 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
{
var videos = _libraryManager.GetItemList(new InternalItemsQuery
{
- MediaTypes = new[] { MediaType.Video },
+ MediaTypes = [MediaType.Video],
IsFolder = false,
Recursive = true,
DtoOptions = new DtoOptions(false)
{
EnableImages = false
},
- SourceTypes = new SourceType[] { SourceType.Library },
+ SourceTypes = [SourceType.Library],
IsVirtualItem = false
})
- .OfType<Video>()
- .ToList();
+ .OfType<Video>()
+ .ToList();
var numComplete = 0;
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanActivityLogTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanActivityLogTask.cs
index 776079044..fe1832165 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanActivityLogTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanActivityLogTask.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
-using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Configuration;
@@ -72,7 +71,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
/// <inheritdoc />
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
- return Enumerable.Empty<TaskTriggerInfo>();
+ return [];
}
}
}
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanupCollectionAndPlaylistPathsTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanupCollectionAndPlaylistPathsTask.cs
index 804097219..25e7ebe79 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanupCollectionAndPlaylistPathsTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanupCollectionAndPlaylistPathsTask.cs
@@ -35,9 +35,9 @@ public class CleanupCollectionAndPlaylistPathsTask : IScheduledTask
/// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
/// <param name="collectionManager">Instance of the <see cref="ICollectionManager"/> interface.</param>
/// <param name="playlistManager">Instance of the <see cref="IPlaylistManager"/> interface.</param>
- /// <param name="logger">The logger.</param>
- /// <param name="providerManager">The provider manager.</param>
- /// <param name="fileSystem">The filesystem.</param>
+ /// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param>
+ /// <param name="providerManager">Instance of the <see cref="IProviderManager"/> interface.</param>
+ /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
public CleanupCollectionAndPlaylistPathsTask(
ILocalizationManager localization,
ICollectionManager collectionManager,
@@ -135,6 +135,6 @@ public class CleanupCollectionAndPlaylistPathsTask : IScheduledTask
/// <inheritdoc />
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
- return new[] { new TaskTriggerInfo() { Type = TaskTriggerInfo.TriggerStartup } };
+ return [new TaskTriggerInfo() { Type = TaskTriggerInfo.TriggerStartup }];
}
}
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs
index fc3ad90f6..0325cb9af 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs
@@ -67,17 +67,14 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
/// <inheritdoc />
public bool IsLogged => true;
- /// <summary>
- /// Creates the triggers that define when the task will run.
- /// </summary>
- /// <returns>IEnumerable{BaseTaskTrigger}.</returns>
+ /// <inheritdoc />
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
- return new[]
- {
+ return
+ [
// Every so often
new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks }
- };
+ ];
}
/// <inheritdoc />
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs
index 9739d7327..9babe8cf9 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs
@@ -23,9 +23,9 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
/// <summary>
/// Initializes a new instance of the <see cref="DeleteLogFileTask" /> class.
/// </summary>
- /// <param name="configurationManager">The configuration manager.</param>
- /// <param name="fileSystem">The file system.</param>
- /// <param name="localization">The localization manager.</param>
+ /// <param name="configurationManager">Instance of the <see cref="IConfigurationManager"/> interface.</param>
+ /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
+ /// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
public DeleteLogFileTask(IConfigurationManager configurationManager, IFileSystem fileSystem, ILocalizationManager localization)
{
_configurationManager = configurationManager;
@@ -57,16 +57,13 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
/// <inheritdoc />
public bool IsLogged => true;
- /// <summary>
- /// Creates the triggers that define when the task will run.
- /// </summary>
- /// <returns>IEnumerable{BaseTaskTrigger}.</returns>
+ /// <inheritdoc />
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
- return new[]
- {
+ return
+ [
new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks }
- };
+ ];
}
/// <inheritdoc />
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs
index 254500ccd..315c245cc 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs
@@ -65,8 +65,8 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
/// <inheritdoc />
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
- return new[]
- {
+ return
+ [
new TaskTriggerInfo
{
Type = TaskTriggerInfo.TriggerStartup
@@ -76,7 +76,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
Type = TaskTriggerInfo.TriggerInterval,
IntervalTicks = TimeSpan.FromHours(24).Ticks
}
- };
+ ];
}
/// <inheritdoc />
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/OptimizeDatabaseTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/OptimizeDatabaseTask.cs
index 1f3cb9b63..3e4925f74 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/OptimizeDatabaseTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/OptimizeDatabaseTask.cs
@@ -22,9 +22,9 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
/// <summary>
/// Initializes a new instance of the <see cref="OptimizeDatabaseTask" /> class.
/// </summary>
- /// <param name="logger">The logger.</param>
- /// <param name="localization">The localization manager.</param>
- /// <param name="provider">The jellyfin DB context provider.</param>
+ /// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param>
+ /// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
+ /// <param name="provider">Instance of the <see cref="IDbContextFactory{JellyfinDbContext}"/> interface.</param>
public OptimizeDatabaseTask(
ILogger<OptimizeDatabaseTask> logger,
ILocalizationManager localization,
@@ -56,17 +56,14 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
/// <inheritdoc />
public bool IsLogged => true;
- /// <summary>
- /// Creates the triggers that define when the task will run.
- /// </summary>
- /// <returns>IEnumerable{BaseTaskTrigger}.</returns>
+ /// <inheritdoc />
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
- return new[]
- {
+ return
+ [
// Every so often
new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks }
- };
+ ];
}
/// <inheritdoc />
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs
index 7d60ea731..c63bad474 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs
@@ -1,5 +1,3 @@
-#pragma warning disable CS1591
-
using System;
using System.Collections.Generic;
using System.Threading;
@@ -13,37 +11,41 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
/// <summary>
/// Class PeopleValidationTask.
/// </summary>
- public class PeopleValidationTask : IScheduledTask
+ public class PeopleValidationTask : IScheduledTask, IConfigurableScheduledTask
{
- /// <summary>
- /// The library manager.
- /// </summary>
private readonly ILibraryManager _libraryManager;
private readonly ILocalizationManager _localization;
/// <summary>
/// Initializes a new instance of the <see cref="PeopleValidationTask" /> class.
/// </summary>
- /// <param name="libraryManager">The library manager.</param>
- /// <param name="localization">The localization manager.</param>
+ /// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
+ /// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
public PeopleValidationTask(ILibraryManager libraryManager, ILocalizationManager localization)
{
_libraryManager = libraryManager;
_localization = localization;
}
+ /// <inheritdoc />
public string Name => _localization.GetLocalizedString("TaskRefreshPeople");
+ /// <inheritdoc />
public string Description => _localization.GetLocalizedString("TaskRefreshPeopleDescription");
+ /// <inheritdoc />
public string Category => _localization.GetLocalizedString("TasksLibraryCategory");
+ /// <inheritdoc />
public string Key => "RefreshPeople";
+ /// <inheritdoc />
public bool IsHidden => false;
+ /// <inheritdoc />
public bool IsEnabled => true;
+ /// <inheritdoc />
public bool IsLogged => true;
/// <summary>
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs
index f9d366ebd..ad72a4c87 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs
@@ -1,5 +1,3 @@
-#pragma warning disable CS1591
-
using System;
using System.Collections.Generic;
using System.IO;
@@ -19,14 +17,17 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
/// </summary>
public class PluginUpdateTask : IScheduledTask, IConfigurableScheduledTask
{
- /// <summary>
- /// The _logger.
- /// </summary>
private readonly ILogger<PluginUpdateTask> _logger;
private readonly IInstallationManager _installationManager;
private readonly ILocalizationManager _localization;
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PluginUpdateTask" /> class.
+ /// </summary>
+ /// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param>
+ /// <param name="installationManager">Instance of the <see cref="IInstallationManager"/> interface.</param>
+ /// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
public PluginUpdateTask(ILogger<PluginUpdateTask> logger, IInstallationManager installationManager, ILocalizationManager localization)
{
_logger = logger;
@@ -55,10 +56,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
/// <inheritdoc />
public bool IsLogged => true;
- /// <summary>
- /// Creates the triggers that define when the task will run.
- /// </summary>
- /// <returns>IEnumerable{BaseTaskTrigger}.</returns>
+ /// <inheritdoc />
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
// At startup
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs
index 065008157..a59f0f366 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs
@@ -1,5 +1,3 @@
-#pragma warning disable CS1591
-
using System;
using System.Collections.Generic;
using System.Threading;
@@ -45,10 +43,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
/// <inheritdoc />
public string Key => "RefreshLibrary";
- /// <summary>
- /// Creates the triggers that define when the task will run.
- /// </summary>
- /// <returns>IEnumerable{BaseTaskTrigger}.</returns>
+ /// <inheritdoc />
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
yield return new TaskTriggerInfo
diff --git a/Emby.Server.Implementations/ScheduledTasks/Triggers/DailyTrigger.cs b/Emby.Server.Implementations/ScheduledTasks/Triggers/DailyTrigger.cs
index 63f11a22c..6d2a74da4 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Triggers/DailyTrigger.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Triggers/DailyTrigger.cs
@@ -25,23 +25,13 @@ namespace Emby.Server.Implementations.ScheduledTasks.Triggers
TaskOptions = taskOptions;
}
- /// <summary>
- /// Occurs when [triggered].
- /// </summary>
+ /// <inheritdoc />
public event EventHandler<EventArgs>? Triggered;
- /// <summary>
- /// Gets the options of this task.
- /// </summary>
+ /// <inheritdoc />
public TaskOptions TaskOptions { get; }
- /// <summary>
- /// Stars waiting for the trigger action.
- /// </summary>
- /// <param name="lastResult">The last result.</param>
- /// <param name="logger">The logger.</param>
- /// <param name="taskName">The name of the task.</param>
- /// <param name="isApplicationStartup">if set to <c>true</c> [is application startup].</param>
+ /// <inheritdoc />
public void Start(TaskResult? lastResult, ILogger logger, string taskName, bool isApplicationStartup)
{
DisposeTimer();
@@ -58,9 +48,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Triggers
_timer = new Timer(_ => OnTriggered(), null, dueTime, TimeSpan.FromMilliseconds(-1));
}
- /// <summary>
- /// Stops waiting for the trigger action.
- /// </summary>
+ /// <inheritdoc />
public void Stop()
{
DisposeTimer();
diff --git a/Emby.Server.Implementations/ScheduledTasks/Triggers/StartupTrigger.cs b/Emby.Server.Implementations/ScheduledTasks/Triggers/StartupTrigger.cs
index b16693c07..535aa20f9 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Triggers/StartupTrigger.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Triggers/StartupTrigger.cs
@@ -1,5 +1,3 @@
-#pragma warning disable CS1591
-
using System;
using System.Threading.Tasks;
using MediaBrowser.Model.Tasks;
@@ -12,7 +10,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Triggers
/// </summary>
public sealed class StartupTrigger : ITaskTrigger
{
- public const int DelayMs = 3000;
+ private const int DelayMs = 3000;
/// <summary>
/// Initializes a new instance of the <see cref="StartupTrigger"/> class.
@@ -23,23 +21,13 @@ namespace Emby.Server.Implementations.ScheduledTasks.Triggers
TaskOptions = taskOptions;
}
- /// <summary>
- /// Occurs when [triggered].
- /// </summary>
+ /// <inheritdoc />
public event EventHandler<EventArgs>? Triggered;
- /// <summary>
- /// Gets the options of this task.
- /// </summary>
+ /// <inheritdoc />
public TaskOptions TaskOptions { get; }
- /// <summary>
- /// Stars waiting for the trigger action.
- /// </summary>
- /// <param name="lastResult">The last result.</param>
- /// <param name="logger">The logger.</param>
- /// <param name="taskName">The name of the task.</param>
- /// <param name="isApplicationStartup">if set to <c>true</c> [is application startup].</param>
+ /// <inheritdoc />
public async void Start(TaskResult? lastResult, ILogger logger, string taskName, bool isApplicationStartup)
{
if (isApplicationStartup)
@@ -50,9 +38,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Triggers
}
}
- /// <summary>
- /// Stops waiting for the trigger action.
- /// </summary>
+ /// <inheritdoc />
public void Stop()
{
}
diff --git a/Emby.Server.Implementations/ScheduledTasks/Triggers/WeeklyTrigger.cs b/Emby.Server.Implementations/ScheduledTasks/Triggers/WeeklyTrigger.cs
index fab49f2fb..ad94fdda5 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Triggers/WeeklyTrigger.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Triggers/WeeklyTrigger.cs
@@ -13,7 +13,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Triggers
private readonly TimeSpan _timeOfDay;
private readonly DayOfWeek _dayOfWeek;
private Timer? _timer;
- private bool _disposed = false;
+ private bool _disposed;
/// <summary>
/// Initializes a new instance of the <see cref="WeeklyTrigger"/> class.
@@ -28,23 +28,13 @@ namespace Emby.Server.Implementations.ScheduledTasks.Triggers
TaskOptions = taskOptions;
}
- /// <summary>
- /// Occurs when [triggered].
- /// </summary>
+ /// <inheritdoc />
public event EventHandler<EventArgs>? Triggered;
- /// <summary>
- /// Gets the options of this task.
- /// </summary>
+ /// <inheritdoc />
public TaskOptions TaskOptions { get; }
- /// <summary>
- /// Stars waiting for the trigger action.
- /// </summary>
- /// <param name="lastResult">The last result.</param>
- /// <param name="logger">The logger.</param>
- /// <param name="taskName">The name of the task.</param>
- /// <param name="isApplicationStartup">if set to <c>true</c> [is application startup].</param>
+ /// <inheritdoc />
public void Start(TaskResult? lastResult, ILogger logger, string taskName, bool isApplicationStartup)
{
DisposeTimer();
@@ -81,9 +71,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Triggers
return triggerDate.Add(_timeOfDay);
}
- /// <summary>
- /// Stops waiting for the trigger action.
- /// </summary>
+ /// <inheritdoc />
public void Stop()
{
DisposeTimer();
diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs
index 681c252b6..72e164b52 100644
--- a/Emby.Server.Implementations/Session/SessionManager.cs
+++ b/Emby.Server.Implementations/Session/SessionManager.cs
@@ -1468,7 +1468,6 @@ namespace Emby.Server.Implementations.Session
user = await _userManager.AuthenticateUser(
request.Username,
request.Password,
- null,
request.RemoteEndPoint,
true).ConfigureAwait(false);
}
diff --git a/Emby.Server.Implementations/TV/TVSeriesManager.cs b/Emby.Server.Implementations/TV/TVSeriesManager.cs
index 34c9e86f2..d11b03a2e 100644
--- a/Emby.Server.Implementations/TV/TVSeriesManager.cs
+++ b/Emby.Server.Implementations/TV/TVSeriesManager.cs
@@ -19,14 +19,12 @@ namespace Emby.Server.Implementations.TV
{
public class TVSeriesManager : ITVSeriesManager
{
- private readonly IUserManager _userManager;
private readonly IUserDataManager _userDataManager;
private readonly ILibraryManager _libraryManager;
private readonly IServerConfigurationManager _configurationManager;
- public TVSeriesManager(IUserManager userManager, IUserDataManager userDataManager, ILibraryManager libraryManager, IServerConfigurationManager configurationManager)
+ public TVSeriesManager(IUserDataManager userDataManager, ILibraryManager libraryManager, IServerConfigurationManager configurationManager)
{
- _userManager = userManager;
_userDataManager = userDataManager;
_libraryManager = libraryManager;
_configurationManager = configurationManager;
@@ -34,12 +32,7 @@ namespace Emby.Server.Implementations.TV
public QueryResult<BaseItem> GetNextUp(NextUpQuery query, DtoOptions options)
{
- var user = _userManager.GetUserById(query.UserId);
-
- if (user is null)
- {
- throw new ArgumentException("User not found");
- }
+ var user = query.User;
string? presentationUniqueKey = null;
if (!query.SeriesId.IsNullOrEmpty())
@@ -83,12 +76,7 @@ namespace Emby.Server.Implementations.TV
public QueryResult<BaseItem> GetNextUp(NextUpQuery request, BaseItem[] parentsFolders, DtoOptions options)
{
- var user = _userManager.GetUserById(request.UserId);
-
- if (user is null)
- {
- throw new ArgumentException("User not found");
- }
+ var user = request.User;
string? presentationUniqueKey = null;
int? limit = null;
diff --git a/Jellyfin.Api/Attributes/AcceptsFileAttribute.cs b/Jellyfin.Api/Attributes/AcceptsFileAttribute.cs
index a6c89bab8..65832d261 100644
--- a/Jellyfin.Api/Attributes/AcceptsFileAttribute.cs
+++ b/Jellyfin.Api/Attributes/AcceptsFileAttribute.cs
@@ -1,4 +1,4 @@
-#pragma warning disable CA1813 // Avoid unsealed attributes
+#pragma warning disable CA1813 // Avoid unsealed attributes
using System;
diff --git a/Jellyfin.Api/Attributes/AcceptsImageFileAttribute.cs b/Jellyfin.Api/Attributes/AcceptsImageFileAttribute.cs
index 57433202e..26fec5fa0 100644
--- a/Jellyfin.Api/Attributes/AcceptsImageFileAttribute.cs
+++ b/Jellyfin.Api/Attributes/AcceptsImageFileAttribute.cs
@@ -1,4 +1,4 @@
-namespace Jellyfin.Api.Attributes;
+namespace Jellyfin.Api.Attributes;
/// <summary>
/// Produces file attribute of "image/*".
diff --git a/Jellyfin.Api/Attributes/ParameterObsoleteAttribute.cs b/Jellyfin.Api/Attributes/ParameterObsoleteAttribute.cs
index bf64fef5d..83a292941 100644
--- a/Jellyfin.Api/Attributes/ParameterObsoleteAttribute.cs
+++ b/Jellyfin.Api/Attributes/ParameterObsoleteAttribute.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
namespace Jellyfin.Api.Attributes;
diff --git a/Jellyfin.Api/Attributes/ProducesAudioFileAttribute.cs b/Jellyfin.Api/Attributes/ProducesAudioFileAttribute.cs
index 7ce09c299..231568127 100644
--- a/Jellyfin.Api/Attributes/ProducesAudioFileAttribute.cs
+++ b/Jellyfin.Api/Attributes/ProducesAudioFileAttribute.cs
@@ -1,4 +1,4 @@
-namespace Jellyfin.Api.Attributes;
+namespace Jellyfin.Api.Attributes;
/// <summary>
/// Produces file attribute of "image/*".
diff --git a/Jellyfin.Api/Attributes/ProducesFileAttribute.cs b/Jellyfin.Api/Attributes/ProducesFileAttribute.cs
index c728f68e0..b4bc4506c 100644
--- a/Jellyfin.Api/Attributes/ProducesFileAttribute.cs
+++ b/Jellyfin.Api/Attributes/ProducesFileAttribute.cs
@@ -1,4 +1,4 @@
-#pragma warning disable CA1813 // Avoid unsealed attributes
+#pragma warning disable CA1813 // Avoid unsealed attributes
using System;
diff --git a/Jellyfin.Api/Attributes/ProducesImageFileAttribute.cs b/Jellyfin.Api/Attributes/ProducesImageFileAttribute.cs
index f145a061e..eb1d1caef 100644
--- a/Jellyfin.Api/Attributes/ProducesImageFileAttribute.cs
+++ b/Jellyfin.Api/Attributes/ProducesImageFileAttribute.cs
@@ -1,4 +1,4 @@
-namespace Jellyfin.Api.Attributes;
+namespace Jellyfin.Api.Attributes;
/// <summary>
/// Produces file attribute of "image/*".
diff --git a/Jellyfin.Api/Attributes/ProducesPlaylistFileAttribute.cs b/Jellyfin.Api/Attributes/ProducesPlaylistFileAttribute.cs
index c03ed740c..73173fd33 100644
--- a/Jellyfin.Api/Attributes/ProducesPlaylistFileAttribute.cs
+++ b/Jellyfin.Api/Attributes/ProducesPlaylistFileAttribute.cs
@@ -1,4 +1,4 @@
-namespace Jellyfin.Api.Attributes;
+namespace Jellyfin.Api.Attributes;
/// <summary>
/// Produces file attribute of "image/*".
diff --git a/Jellyfin.Api/Attributes/ProducesVideoFileAttribute.cs b/Jellyfin.Api/Attributes/ProducesVideoFileAttribute.cs
index 10dec0c00..167da686c 100644
--- a/Jellyfin.Api/Attributes/ProducesVideoFileAttribute.cs
+++ b/Jellyfin.Api/Attributes/ProducesVideoFileAttribute.cs
@@ -1,4 +1,4 @@
-namespace Jellyfin.Api.Attributes;
+namespace Jellyfin.Api.Attributes;
/// <summary>
/// Produces file attribute of "video/*".
diff --git a/Jellyfin.Api/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandler.cs b/Jellyfin.Api/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandler.cs
index 7d0fe5589..4928d5ed2 100644
--- a/Jellyfin.Api/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandler.cs
+++ b/Jellyfin.Api/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandler.cs
@@ -1,4 +1,4 @@
-using System.Threading.Tasks;
+using System.Threading.Tasks;
using Jellyfin.Api.Constants;
using Jellyfin.Api.Extensions;
using Jellyfin.Data.Enums;
diff --git a/Jellyfin.Api/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationRequirement.cs b/Jellyfin.Api/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationRequirement.cs
index 5ba1bc330..222384750 100644
--- a/Jellyfin.Api/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationRequirement.cs
+++ b/Jellyfin.Api/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationRequirement.cs
@@ -1,4 +1,4 @@
-using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Authorization;
namespace Jellyfin.Api.Auth.DefaultAuthorizationPolicy
{
diff --git a/Jellyfin.Api/Auth/FirstTimeSetupPolicy/FirstTimeSetupRequirement.cs b/Jellyfin.Api/Auth/FirstTimeSetupPolicy/FirstTimeSetupRequirement.cs
index 6252a2feb..adae12eff 100644
--- a/Jellyfin.Api/Auth/FirstTimeSetupPolicy/FirstTimeSetupRequirement.cs
+++ b/Jellyfin.Api/Auth/FirstTimeSetupPolicy/FirstTimeSetupRequirement.cs
@@ -1,4 +1,4 @@
-using Jellyfin.Api.Auth.DefaultAuthorizationPolicy;
+using Jellyfin.Api.Auth.DefaultAuthorizationPolicy;
namespace Jellyfin.Api.Auth.FirstTimeSetupPolicy
{
diff --git a/Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessHandler.cs b/Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessHandler.cs
index 75ec9fcec..5fcf72fb4 100644
--- a/Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessHandler.cs
+++ b/Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessHandler.cs
@@ -1,4 +1,4 @@
-using System.Threading.Tasks;
+using System.Threading.Tasks;
using Jellyfin.Api.Extensions;
using Jellyfin.Data.Enums;
using MediaBrowser.Common.Extensions;
diff --git a/Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessRequirement.cs b/Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessRequirement.cs
index 220b223b3..bb6e52b46 100644
--- a/Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessRequirement.cs
+++ b/Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessRequirement.cs
@@ -1,4 +1,4 @@
-using Jellyfin.Api.Auth.DefaultAuthorizationPolicy;
+using Jellyfin.Api.Auth.DefaultAuthorizationPolicy;
using Jellyfin.Data.Enums;
namespace Jellyfin.Api.Auth.SyncPlayAccessPolicy
diff --git a/Jellyfin.Api/Auth/UserPermissionPolicy/UserPermissionHandler.cs b/Jellyfin.Api/Auth/UserPermissionPolicy/UserPermissionHandler.cs
index 764c0a435..f20779f6c 100644
--- a/Jellyfin.Api/Auth/UserPermissionPolicy/UserPermissionHandler.cs
+++ b/Jellyfin.Api/Auth/UserPermissionPolicy/UserPermissionHandler.cs
@@ -1,4 +1,4 @@
-using System.Threading.Tasks;
+using System.Threading.Tasks;
using Jellyfin.Api.Extensions;
using Jellyfin.Extensions;
using MediaBrowser.Common.Extensions;
diff --git a/Jellyfin.Api/Auth/UserPermissionPolicy/UserPermissionRequirement.cs b/Jellyfin.Api/Auth/UserPermissionPolicy/UserPermissionRequirement.cs
index 4694556eb..a7c3cce97 100644
--- a/Jellyfin.Api/Auth/UserPermissionPolicy/UserPermissionRequirement.cs
+++ b/Jellyfin.Api/Auth/UserPermissionPolicy/UserPermissionRequirement.cs
@@ -1,4 +1,4 @@
-using Jellyfin.Api.Auth.DefaultAuthorizationPolicy;
+using Jellyfin.Api.Auth.DefaultAuthorizationPolicy;
using Jellyfin.Data.Enums;
namespace Jellyfin.Api.Auth.UserPermissionPolicy
diff --git a/Jellyfin.Api/Constants/InternalClaimTypes.cs b/Jellyfin.Api/Constants/InternalClaimTypes.cs
index 73c4acb88..281955754 100644
--- a/Jellyfin.Api/Constants/InternalClaimTypes.cs
+++ b/Jellyfin.Api/Constants/InternalClaimTypes.cs
@@ -1,4 +1,4 @@
-namespace Jellyfin.Api.Constants;
+namespace Jellyfin.Api.Constants;
/// <summary>
/// Internal claim types for authorization.
diff --git a/Jellyfin.Api/Controllers/ClientLogController.cs b/Jellyfin.Api/Controllers/ClientLogController.cs
index 2c5dbacbb..139888bde 100644
--- a/Jellyfin.Api/Controllers/ClientLogController.cs
+++ b/Jellyfin.Api/Controllers/ClientLogController.cs
@@ -1,4 +1,4 @@
-using System.Net.Mime;
+using System.Net.Mime;
using System.Threading.Tasks;
using Jellyfin.Api.Attributes;
using Jellyfin.Api.Extensions;
diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs
index 016c5b163..662e2acbc 100644
--- a/Jellyfin.Api/Controllers/DynamicHlsController.cs
+++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs
@@ -45,6 +45,7 @@ public class DynamicHlsController : BaseJellyfinApiController
private const TranscodingJobType TranscodingJobType = MediaBrowser.Controller.MediaEncoding.TranscodingJobType.Hls;
private readonly Version _minFFmpegFlacInMp4 = new Version(6, 0);
+ private readonly Version _minFFmpegX265BframeInFmp4 = new Version(7, 0, 1);
private readonly ILibraryManager _libraryManager;
private readonly IUserManager _userManager;
@@ -1851,13 +1852,12 @@ public class DynamicHlsController : BaseJellyfinApiController
args += _encodingHelper.GetHlsVideoKeyFrameArguments(state, codec, state.SegmentLength, isEventPlaylist, startNumber);
// Currently b-frames in libx265 breaks the FMP4-HLS playback on iOS, disable it for now.
- if (string.Equals(codec, "libx265", StringComparison.OrdinalIgnoreCase))
+ if (string.Equals(codec, "libx265", StringComparison.OrdinalIgnoreCase)
+ && _mediaEncoder.EncoderVersion < _minFFmpegX265BframeInFmp4)
{
args += " -bf 0";
}
- // args += " -mixed-refs 0 -refs 3 -x264opts b_pyramid=0:weightb=0:weightp=0";
-
// video processing filters.
var videoProcessParam = _encodingHelper.GetVideoProcessingFilterParam(state, _encodingOptions, codec);
diff --git a/Jellyfin.Api/Controllers/ImageController.cs b/Jellyfin.Api/Controllers/ImageController.cs
index 6a169eae3..b71199026 100644
--- a/Jellyfin.Api/Controllers/ImageController.cs
+++ b/Jellyfin.Api/Controllers/ImageController.cs
@@ -109,7 +109,7 @@ public class ImageController : BaseJellyfinApiController
return NotFound();
}
- if (!RequestHelpers.AssertCanUpdateUser(_userManager, HttpContext.User, requestUserId, true))
+ if (!RequestHelpers.AssertCanUpdateUser(HttpContext.User, user, true))
{
return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to update the image.");
}
@@ -203,13 +203,18 @@ public class ImageController : BaseJellyfinApiController
[FromQuery] Guid? userId)
{
var requestUserId = RequestHelpers.GetUserId(User, userId);
- if (!RequestHelpers.AssertCanUpdateUser(_userManager, HttpContext.User, requestUserId, true))
+ var user = _userManager.GetUserById(requestUserId);
+ if (user is null)
+ {
+ return NotFound();
+ }
+
+ if (!RequestHelpers.AssertCanUpdateUser(HttpContext.User, user, true))
{
return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to delete the image.");
}
- var user = _userManager.GetUserById(requestUserId);
- if (user?.ProfileImage is null)
+ if (user.ProfileImage is null)
{
return NoContent();
}
diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs
index d33634412..828bd5174 100644
--- a/Jellyfin.Api/Controllers/ItemsController.cs
+++ b/Jellyfin.Api/Controllers/ItemsController.cs
@@ -972,12 +972,17 @@ public class ItemsController : BaseJellyfinApiController
[FromRoute, Required] Guid itemId)
{
var requestUserId = RequestHelpers.GetUserId(User, userId);
- if (!RequestHelpers.AssertCanUpdateUser(_userManager, User, requestUserId, true))
+ var user = _userManager.GetUserById(requestUserId);
+ if (user is null)
+ {
+ return NotFound();
+ }
+
+ if (!RequestHelpers.AssertCanUpdateUser(User, user, true))
{
return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to view this item user data.");
}
- var user = _userManager.GetUserById(requestUserId) ?? throw new ResourceNotFoundException();
var item = _libraryManager.GetItemById<BaseItem>(itemId, user);
if (item is null)
{
@@ -1023,12 +1028,17 @@ public class ItemsController : BaseJellyfinApiController
[FromBody, Required] UpdateUserItemDataDto userDataDto)
{
var requestUserId = RequestHelpers.GetUserId(User, userId);
- if (!RequestHelpers.AssertCanUpdateUser(_userManager, User, requestUserId, true))
+ var user = _userManager.GetUserById(requestUserId);
+ if (user is null)
+ {
+ return NotFound();
+ }
+
+ if (!RequestHelpers.AssertCanUpdateUser(User, user, true))
{
return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to update this item user data.");
}
- var user = _userManager.GetUserById(requestUserId) ?? throw new ResourceNotFoundException();
var item = _libraryManager.GetItemById<BaseItem>(itemId, user);
if (item is null)
{
diff --git a/Jellyfin.Api/Controllers/LiveTvController.cs b/Jellyfin.Api/Controllers/LiveTvController.cs
index 6c3d01103..0ae8baa67 100644
--- a/Jellyfin.Api/Controllers/LiveTvController.cs
+++ b/Jellyfin.Api/Controllers/LiveTvController.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
diff --git a/Jellyfin.Api/Controllers/TvShowsController.cs b/Jellyfin.Api/Controllers/TvShowsController.cs
index 426402667..914ccd7f9 100644
--- a/Jellyfin.Api/Controllers/TvShowsController.cs
+++ b/Jellyfin.Api/Controllers/TvShowsController.cs
@@ -90,7 +90,12 @@ public class TvShowsController : BaseJellyfinApiController
[FromQuery] bool enableResumable = true,
[FromQuery] bool enableRewatching = false)
{
- userId = RequestHelpers.GetUserId(User, userId);
+ var user = _userManager.GetUserById(RequestHelpers.GetUserId(User, userId));
+ if (user is null)
+ {
+ return NotFound();
+ }
+
var options = new DtoOptions { Fields = fields }
.AddClientFields(User)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
@@ -102,7 +107,7 @@ public class TvShowsController : BaseJellyfinApiController
ParentId = parentId,
SeriesId = seriesId,
StartIndex = startIndex,
- UserId = userId.Value,
+ User = user,
EnableTotalRecordCount = enableTotalRecordCount,
DisableFirstEpisode = disableFirstEpisode,
NextUpDateCutoff = nextUpDateCutoff ?? DateTime.MinValue,
@@ -111,10 +116,6 @@ public class TvShowsController : BaseJellyfinApiController
},
options);
- var user = userId.IsNullOrEmpty()
- ? null
- : _userManager.GetUserById(userId.Value);
-
var returnItems = _dtoService.GetBaseItemDtos(result.Items, options, user);
return new QueryResult<BaseItemDto>(
diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs
index c3923a2ad..d7886d247 100644
--- a/Jellyfin.Api/Controllers/UserController.cs
+++ b/Jellyfin.Api/Controllers/UserController.cs
@@ -274,16 +274,15 @@ public class UserController : BaseJellyfinApiController
[FromBody, Required] UpdateUserPassword request)
{
var requestUserId = userId ?? User.GetUserId();
- if (!RequestHelpers.AssertCanUpdateUser(_userManager, User, requestUserId, true))
+ var user = _userManager.GetUserById(requestUserId);
+ if (user is null)
{
- return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to update the password.");
+ return NotFound();
}
- var user = _userManager.GetUserById(requestUserId);
-
- if (user is null)
+ if (!RequestHelpers.AssertCanUpdateUser(User, user, true))
{
- return NotFound("User not found");
+ return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to update the password.");
}
if (request.ResetPassword)
@@ -297,7 +296,6 @@ public class UserController : BaseJellyfinApiController
var success = await _userManager.AuthenticateUser(
user.Username,
request.CurrentPw ?? string.Empty,
- request.CurrentPw ?? string.Empty,
HttpContext.GetNormalizedRemoteIP().ToString(),
false).ConfigureAwait(false);
@@ -386,7 +384,7 @@ public class UserController : BaseJellyfinApiController
return NotFound();
}
- if (!RequestHelpers.AssertCanUpdateUser(_userManager, User, requestUserId, true))
+ if (!RequestHelpers.AssertCanUpdateUser(User, user, true))
{
return StatusCode(StatusCodes.Status403Forbidden, "User update not allowed.");
}
@@ -396,7 +394,7 @@ public class UserController : BaseJellyfinApiController
await _userManager.RenameUser(user, updateUser.Name).ConfigureAwait(false);
}
- await _userManager.UpdateConfigurationAsync(user.Id, updateUser.Configuration).ConfigureAwait(false);
+ await _userManager.UpdateConfigurationAsync(requestUserId, updateUser.Configuration).ConfigureAwait(false);
return NoContent();
}
@@ -495,7 +493,13 @@ public class UserController : BaseJellyfinApiController
[FromBody, Required] UserConfiguration userConfig)
{
var requestUserId = userId ?? User.GetUserId();
- if (!RequestHelpers.AssertCanUpdateUser(_userManager, User, requestUserId, true))
+ var user = _userManager.GetUserById(requestUserId);
+ if (user is null)
+ {
+ return NotFound();
+ }
+
+ if (!RequestHelpers.AssertCanUpdateUser(User, user, true))
{
return StatusCode(StatusCodes.Status403Forbidden, "User configuration update not allowed");
}
diff --git a/Jellyfin.Api/Controllers/UserLibraryController.cs b/Jellyfin.Api/Controllers/UserLibraryController.cs
index 421f1bfb5..e7bf71727 100644
--- a/Jellyfin.Api/Controllers/UserLibraryController.cs
+++ b/Jellyfin.Api/Controllers/UserLibraryController.cs
@@ -560,7 +560,7 @@ public class UserLibraryController : BaseJellyfinApiController
IsPlayed = isPlayed,
Limit = limit,
ParentId = parentId ?? Guid.Empty,
- UserId = requestUserId,
+ User = user,
},
dtoOptions);
diff --git a/Jellyfin.Api/Controllers/UserViewsController.cs b/Jellyfin.Api/Controllers/UserViewsController.cs
index 01da50d02..e24f78a88 100644
--- a/Jellyfin.Api/Controllers/UserViewsController.cs
+++ b/Jellyfin.Api/Controllers/UserViewsController.cs
@@ -8,6 +8,7 @@ using Jellyfin.Api.Helpers;
using Jellyfin.Api.ModelBinders;
using Jellyfin.Api.Models.UserViewDtos;
using Jellyfin.Data.Enums;
+using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
@@ -69,8 +70,9 @@ public class UserViewsController : BaseJellyfinApiController
[FromQuery] bool includeHidden = false)
{
userId = RequestHelpers.GetUserId(User, userId);
+ var user = _userManager.GetUserById(userId.Value) ?? throw new ResourceNotFoundException();
- var query = new UserViewQuery { UserId = userId.Value, IncludeHidden = includeHidden };
+ var query = new UserViewQuery { User = user, IncludeHidden = includeHidden };
if (includeExternalContent.HasValue)
{
@@ -87,8 +89,6 @@ public class UserViewsController : BaseJellyfinApiController
var dtoOptions = new DtoOptions().AddClientFields(User);
dtoOptions.Fields = [..dtoOptions.Fields, ItemFields.PrimaryImageAspectRatio, ItemFields.DisplayPreferencesId];
- var user = _userManager.GetUserById(userId.Value);
-
var dtos = Array.ConvertAll(folders, i => _dtoService.GetBaseItemDto(i, dtoOptions, user));
return new QueryResult<BaseItemDto>(dtos);
diff --git a/Jellyfin.Api/Extensions/ClaimsPrincipalExtensions.cs b/Jellyfin.Api/Extensions/ClaimsPrincipalExtensions.cs
index d2e8eb378..608905562 100644
--- a/Jellyfin.Api/Extensions/ClaimsPrincipalExtensions.cs
+++ b/Jellyfin.Api/Extensions/ClaimsPrincipalExtensions.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Linq;
using System.Security.Claims;
using Jellyfin.Api.Constants;
diff --git a/Jellyfin.Api/Extensions/DtoExtensions.cs b/Jellyfin.Api/Extensions/DtoExtensions.cs
index f52b58bab..f919a4707 100644
--- a/Jellyfin.Api/Extensions/DtoExtensions.cs
+++ b/Jellyfin.Api/Extensions/DtoExtensions.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Security.Claims;
using Jellyfin.Extensions;
diff --git a/Jellyfin.Api/Formatters/CssOutputFormatter.cs b/Jellyfin.Api/Formatters/CssOutputFormatter.cs
index 0a3891138..495f771e1 100644
--- a/Jellyfin.Api/Formatters/CssOutputFormatter.cs
+++ b/Jellyfin.Api/Formatters/CssOutputFormatter.cs
@@ -1,4 +1,4 @@
-using System.Text;
+using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Formatters;
diff --git a/Jellyfin.Api/Formatters/XmlOutputFormatter.cs b/Jellyfin.Api/Formatters/XmlOutputFormatter.cs
index d5dea0f09..1c9feedcb 100644
--- a/Jellyfin.Api/Formatters/XmlOutputFormatter.cs
+++ b/Jellyfin.Api/Formatters/XmlOutputFormatter.cs
@@ -1,4 +1,4 @@
-using System.Net.Mime;
+using System.Net.Mime;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
diff --git a/Jellyfin.Api/Helpers/AudioHelper.cs b/Jellyfin.Api/Helpers/AudioHelper.cs
index c80a9d582..1c9d6e449 100644
--- a/Jellyfin.Api/Helpers/AudioHelper.cs
+++ b/Jellyfin.Api/Helpers/AudioHelper.cs
@@ -1,4 +1,4 @@
-using System.IO;
+using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
diff --git a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs
index 6f040cfae..ba92d811c 100644
--- a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs
+++ b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs
@@ -198,6 +198,17 @@ public class DynamicHlsHelper
AddSubtitles(state, subtitleStreams, builder, _httpContextAccessor.HttpContext.User);
}
+ // Video rotation metadata is only supported in fMP4 remuxing
+ if (state.VideoStream is not null
+ && state.VideoRequest is not null
+ && (state.VideoStream?.Rotation ?? 0) != 0
+ && EncodingHelper.IsCopyCodec(state.OutputVideoCodec)
+ && !string.IsNullOrWhiteSpace(state.Request.SegmentContainer)
+ && !string.Equals(state.Request.SegmentContainer, "mp4", StringComparison.OrdinalIgnoreCase))
+ {
+ playlistUrl += "&AllowVideoStreamCopy=false";
+ }
+
var basicPlaylist = AppendPlaylist(builder, state, playlistUrl, totalBitrate, subtitleGroup);
if (state.VideoStream is not null && state.VideoRequest is not null)
diff --git a/Jellyfin.Api/Helpers/HlsCodecStringHelpers.cs b/Jellyfin.Api/Helpers/HlsCodecStringHelpers.cs
index d0bfa1fbe..0efb7f45d 100644
--- a/Jellyfin.Api/Helpers/HlsCodecStringHelpers.cs
+++ b/Jellyfin.Api/Helpers/HlsCodecStringHelpers.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Globalization;
using System.Text;
@@ -19,12 +19,12 @@ public static class HlsCodecStringHelpers
/// <summary>
/// Codec name for AC-3.
/// </summary>
- public const string AC3 = "mp4a.a5";
+ public const string AC3 = "ac-3";
/// <summary>
/// Codec name for E-AC-3.
/// </summary>
- public const string EAC3 = "mp4a.a6";
+ public const string EAC3 = "ec-3";
/// <summary>
/// Codec name for FLAC.
diff --git a/Jellyfin.Api/Helpers/HlsHelpers.cs b/Jellyfin.Api/Helpers/HlsHelpers.cs
index c8a36c562..cad8d650e 100644
--- a/Jellyfin.Api/Helpers/HlsHelpers.cs
+++ b/Jellyfin.Api/Helpers/HlsHelpers.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Globalization;
using System.IO;
using System.Threading;
diff --git a/Jellyfin.Api/Helpers/MediaInfoHelper.cs b/Jellyfin.Api/Helpers/MediaInfoHelper.cs
index 212d678a8..9bda27031 100644
--- a/Jellyfin.Api/Helpers/MediaInfoHelper.cs
+++ b/Jellyfin.Api/Helpers/MediaInfoHelper.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Globalization;
using System.Linq;
using System.Net;
diff --git a/Jellyfin.Api/Helpers/RequestHelpers.cs b/Jellyfin.Api/Helpers/RequestHelpers.cs
index a3d7f471e..eb83a37ba 100644
--- a/Jellyfin.Api/Helpers/RequestHelpers.cs
+++ b/Jellyfin.Api/Helpers/RequestHelpers.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
@@ -86,18 +86,17 @@ public static class RequestHelpers
/// <summary>
/// Checks if the user can update an entry.
/// </summary>
- /// <param name="userManager">An instance of the <see cref="IUserManager"/> interface.</param>
/// <param name="claimsPrincipal">The <see cref="ClaimsPrincipal"/> for the current request.</param>
- /// <param name="userId">The user id.</param>
+ /// <param name="user">The user id.</param>
/// <param name="restrictUserPreferences">Whether to restrict the user preferences.</param>
/// <returns>A <see cref="bool"/> whether the user can update the entry.</returns>
- internal static bool AssertCanUpdateUser(IUserManager userManager, ClaimsPrincipal claimsPrincipal, Guid userId, bool restrictUserPreferences)
+ internal static bool AssertCanUpdateUser(ClaimsPrincipal claimsPrincipal, User user, bool restrictUserPreferences)
{
var authenticatedUserId = claimsPrincipal.GetUserId();
var isAdministrator = claimsPrincipal.IsInRole(UserRoles.Administrator);
// If they're going to update the record of another user, they must be an administrator
- if (!userId.Equals(authenticatedUserId) && !isAdministrator)
+ if (!user.Id.Equals(authenticatedUserId) && !isAdministrator)
{
return false;
}
@@ -108,12 +107,6 @@ public static class RequestHelpers
return true;
}
- var user = userManager.GetUserById(userId);
- if (user is null)
- {
- throw new ResourceNotFoundException();
- }
-
return user.EnableUserPreferenceAccess;
}
diff --git a/Jellyfin.Api/Middleware/LegacyEmbyRouteRewriteMiddleware.cs b/Jellyfin.Api/Middleware/LegacyEmbyRouteRewriteMiddleware.cs
index 17d8997d5..2cbb18326 100644
--- a/Jellyfin.Api/Middleware/LegacyEmbyRouteRewriteMiddleware.cs
+++ b/Jellyfin.Api/Middleware/LegacyEmbyRouteRewriteMiddleware.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
diff --git a/Jellyfin.Api/Middleware/ResponseTimeMiddleware.cs b/Jellyfin.Api/Middleware/ResponseTimeMiddleware.cs
index 279ea70d8..4a13d6c79 100644
--- a/Jellyfin.Api/Middleware/ResponseTimeMiddleware.cs
+++ b/Jellyfin.Api/Middleware/ResponseTimeMiddleware.cs
@@ -1,4 +1,4 @@
-using System.Diagnostics;
+using System.Diagnostics;
using System.Globalization;
using System.Threading.Tasks;
using MediaBrowser.Common.Extensions;
diff --git a/Jellyfin.Api/Middleware/RobotsRedirectionMiddleware.cs b/Jellyfin.Api/Middleware/RobotsRedirectionMiddleware.cs
index acf3645fd..f716452da 100644
--- a/Jellyfin.Api/Middleware/RobotsRedirectionMiddleware.cs
+++ b/Jellyfin.Api/Middleware/RobotsRedirectionMiddleware.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
diff --git a/Jellyfin.Api/ModelBinders/LegacyDateTimeModelBinder.cs b/Jellyfin.Api/ModelBinders/LegacyDateTimeModelBinder.cs
index 87a30773e..1ecf89b20 100644
--- a/Jellyfin.Api/ModelBinders/LegacyDateTimeModelBinder.cs
+++ b/Jellyfin.Api/ModelBinders/LegacyDateTimeModelBinder.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Globalization;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.ModelBinding;
diff --git a/Jellyfin.Api/ModelBinders/NullableEnumModelBinder.cs b/Jellyfin.Api/ModelBinders/NullableEnumModelBinder.cs
index a2e139ca1..79a3e135a 100644
--- a/Jellyfin.Api/ModelBinders/NullableEnumModelBinder.cs
+++ b/Jellyfin.Api/ModelBinders/NullableEnumModelBinder.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.ComponentModel;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.ModelBinding;
diff --git a/Jellyfin.Api/ModelBinders/NullableEnumModelBinderProvider.cs b/Jellyfin.Api/ModelBinders/NullableEnumModelBinderProvider.cs
index 43ffdaefd..d4ec04eef 100644
--- a/Jellyfin.Api/ModelBinders/NullableEnumModelBinderProvider.cs
+++ b/Jellyfin.Api/ModelBinders/NullableEnumModelBinderProvider.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
diff --git a/Jellyfin.Api/Models/LibraryDtos/LibraryOptionInfoDto.cs b/Jellyfin.Api/Models/LibraryDtos/LibraryOptionInfoDto.cs
index 6401522f6..3af6f532a 100644
--- a/Jellyfin.Api/Models/LibraryDtos/LibraryOptionInfoDto.cs
+++ b/Jellyfin.Api/Models/LibraryDtos/LibraryOptionInfoDto.cs
@@ -1,4 +1,4 @@
-namespace Jellyfin.Api.Models.LibraryDtos;
+namespace Jellyfin.Api.Models.LibraryDtos;
/// <summary>
/// Library option info dto.
diff --git a/Jellyfin.Api/Models/LibraryDtos/LibraryOptionsResultDto.cs b/Jellyfin.Api/Models/LibraryDtos/LibraryOptionsResultDto.cs
index 53b5e3b7c..d07349bdf 100644
--- a/Jellyfin.Api/Models/LibraryDtos/LibraryOptionsResultDto.cs
+++ b/Jellyfin.Api/Models/LibraryDtos/LibraryOptionsResultDto.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
namespace Jellyfin.Api.Models.LibraryDtos;
diff --git a/Jellyfin.Api/Models/LibraryDtos/LibraryTypeOptionsDto.cs b/Jellyfin.Api/Models/LibraryDtos/LibraryTypeOptionsDto.cs
index 125a6746e..f76c4a967 100644
--- a/Jellyfin.Api/Models/LibraryDtos/LibraryTypeOptionsDto.cs
+++ b/Jellyfin.Api/Models/LibraryDtos/LibraryTypeOptionsDto.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
diff --git a/Jellyfin.Api/Models/LibraryDtos/MediaUpdateInfoDto.cs b/Jellyfin.Api/Models/LibraryDtos/MediaUpdateInfoDto.cs
index b34e0bba5..7b7e9dfd0 100644
--- a/Jellyfin.Api/Models/LibraryDtos/MediaUpdateInfoDto.cs
+++ b/Jellyfin.Api/Models/LibraryDtos/MediaUpdateInfoDto.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
namespace Jellyfin.Api.Models.LibraryDtos;
diff --git a/Jellyfin.Api/Models/LibraryDtos/MediaUpdateInfoPathDto.cs b/Jellyfin.Api/Models/LibraryDtos/MediaUpdateInfoPathDto.cs
index 5bbaea669..dbcc73c62 100644
--- a/Jellyfin.Api/Models/LibraryDtos/MediaUpdateInfoPathDto.cs
+++ b/Jellyfin.Api/Models/LibraryDtos/MediaUpdateInfoPathDto.cs
@@ -1,4 +1,4 @@
-namespace Jellyfin.Api.Models.LibraryDtos;
+namespace Jellyfin.Api.Models.LibraryDtos;
/// <summary>
/// The media update info path.
diff --git a/Jellyfin.Api/Models/LibraryStructureDto/AddVirtualFolderDto.cs b/Jellyfin.Api/Models/LibraryStructureDto/AddVirtualFolderDto.cs
index 16d3f65c9..0066d1e3c 100644
--- a/Jellyfin.Api/Models/LibraryStructureDto/AddVirtualFolderDto.cs
+++ b/Jellyfin.Api/Models/LibraryStructureDto/AddVirtualFolderDto.cs
@@ -1,4 +1,4 @@
-using MediaBrowser.Model.Configuration;
+using MediaBrowser.Model.Configuration;
namespace Jellyfin.Api.Models.LibraryStructureDto;
diff --git a/Jellyfin.Api/Models/LibraryStructureDto/MediaPathDto.cs b/Jellyfin.Api/Models/LibraryStructureDto/MediaPathDto.cs
index 7a549aada..8a313d59a 100644
--- a/Jellyfin.Api/Models/LibraryStructureDto/MediaPathDto.cs
+++ b/Jellyfin.Api/Models/LibraryStructureDto/MediaPathDto.cs
@@ -1,4 +1,4 @@
-using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations;
using MediaBrowser.Model.Configuration;
namespace Jellyfin.Api.Models.LibraryStructureDto;
diff --git a/Jellyfin.Api/Models/LibraryStructureDto/UpdateLibraryOptionsDto.cs b/Jellyfin.Api/Models/LibraryStructureDto/UpdateLibraryOptionsDto.cs
index 225c7c7bc..2dbd30256 100644
--- a/Jellyfin.Api/Models/LibraryStructureDto/UpdateLibraryOptionsDto.cs
+++ b/Jellyfin.Api/Models/LibraryStructureDto/UpdateLibraryOptionsDto.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using MediaBrowser.Model.Configuration;
namespace Jellyfin.Api.Models.LibraryStructureDto;
diff --git a/Jellyfin.Api/Models/LibraryStructureDto/UpdateMediaPathRequestDto.cs b/Jellyfin.Api/Models/LibraryStructureDto/UpdateMediaPathRequestDto.cs
index a4d33f3b9..85935b94e 100644
--- a/Jellyfin.Api/Models/LibraryStructureDto/UpdateMediaPathRequestDto.cs
+++ b/Jellyfin.Api/Models/LibraryStructureDto/UpdateMediaPathRequestDto.cs
@@ -1,4 +1,4 @@
-using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations;
using MediaBrowser.Model.Configuration;
namespace Jellyfin.Api.Models.LibraryStructureDto;
diff --git a/Jellyfin.Api/Models/LiveTvDtos/GetProgramsDto.cs b/Jellyfin.Api/Models/LiveTvDtos/GetProgramsDto.cs
index 7210cc8f7..190d90681 100644
--- a/Jellyfin.Api/Models/LiveTvDtos/GetProgramsDto.cs
+++ b/Jellyfin.Api/Models/LiveTvDtos/GetProgramsDto.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text.Json.Serialization;
diff --git a/Jellyfin.Api/Models/LiveTvDtos/SetChannelMappingDto.cs b/Jellyfin.Api/Models/LiveTvDtos/SetChannelMappingDto.cs
index 2dbaece5e..1acabb767 100644
--- a/Jellyfin.Api/Models/LiveTvDtos/SetChannelMappingDto.cs
+++ b/Jellyfin.Api/Models/LiveTvDtos/SetChannelMappingDto.cs
@@ -1,4 +1,4 @@
-using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations;
namespace Jellyfin.Api.Models.LiveTvDtos;
diff --git a/Jellyfin.Api/Models/MediaInfoDtos/OpenLiveStreamDto.cs b/Jellyfin.Api/Models/MediaInfoDtos/OpenLiveStreamDto.cs
index 99b3f7020..53104988f 100644
--- a/Jellyfin.Api/Models/MediaInfoDtos/OpenLiveStreamDto.cs
+++ b/Jellyfin.Api/Models/MediaInfoDtos/OpenLiveStreamDto.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.MediaInfo;
diff --git a/Jellyfin.Api/Models/MediaInfoDtos/PlaybackInfoDto.cs b/Jellyfin.Api/Models/MediaInfoDtos/PlaybackInfoDto.cs
index 0ef1867cd..9e12ddde6 100644
--- a/Jellyfin.Api/Models/MediaInfoDtos/PlaybackInfoDto.cs
+++ b/Jellyfin.Api/Models/MediaInfoDtos/PlaybackInfoDto.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using MediaBrowser.Model.Dlna;
namespace Jellyfin.Api.Models.MediaInfoDtos;
diff --git a/Jellyfin.Api/Models/PlaylistDtos/CreatePlaylistDto.cs b/Jellyfin.Api/Models/PlaylistDtos/CreatePlaylistDto.cs
index 3cbdd031a..61a3f2ed6 100644
--- a/Jellyfin.Api/Models/PlaylistDtos/CreatePlaylistDto.cs
+++ b/Jellyfin.Api/Models/PlaylistDtos/CreatePlaylistDto.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
using Jellyfin.Data.Enums;
diff --git a/Jellyfin.Api/Models/SessionDtos/ClientCapabilitiesDto.cs b/Jellyfin.Api/Models/SessionDtos/ClientCapabilitiesDto.cs
index b72dcff88..c699c469d 100644
--- a/Jellyfin.Api/Models/SessionDtos/ClientCapabilitiesDto.cs
+++ b/Jellyfin.Api/Models/SessionDtos/ClientCapabilitiesDto.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text.Json.Serialization;
diff --git a/Jellyfin.Api/Models/StartupDtos/StartupRemoteAccessDto.cs b/Jellyfin.Api/Models/StartupDtos/StartupRemoteAccessDto.cs
index 0e7be24c4..1ae2cad4b 100644
--- a/Jellyfin.Api/Models/StartupDtos/StartupRemoteAccessDto.cs
+++ b/Jellyfin.Api/Models/StartupDtos/StartupRemoteAccessDto.cs
@@ -1,4 +1,4 @@
-using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations;
namespace Jellyfin.Api.Models.StartupDtos;
diff --git a/Jellyfin.Api/Models/StreamingDtos/HlsAudioRequestDto.cs b/Jellyfin.Api/Models/StreamingDtos/HlsAudioRequestDto.cs
index bd176bb6a..e1a51eecb 100644
--- a/Jellyfin.Api/Models/StreamingDtos/HlsAudioRequestDto.cs
+++ b/Jellyfin.Api/Models/StreamingDtos/HlsAudioRequestDto.cs
@@ -1,4 +1,4 @@
-using MediaBrowser.Controller.Streaming;
+using MediaBrowser.Controller.Streaming;
namespace Jellyfin.Api.Models.StreamingDtos;
diff --git a/Jellyfin.Api/Models/StreamingDtos/HlsVideoRequestDto.cs b/Jellyfin.Api/Models/StreamingDtos/HlsVideoRequestDto.cs
index 53b6d7575..22da44bcc 100644
--- a/Jellyfin.Api/Models/StreamingDtos/HlsVideoRequestDto.cs
+++ b/Jellyfin.Api/Models/StreamingDtos/HlsVideoRequestDto.cs
@@ -1,4 +1,4 @@
-using MediaBrowser.Controller.Streaming;
+using MediaBrowser.Controller.Streaming;
namespace Jellyfin.Api.Models.StreamingDtos;
diff --git a/Jellyfin.Api/Models/SubtitleDtos/UploadSubtitleDto.cs b/Jellyfin.Api/Models/SubtitleDtos/UploadSubtitleDto.cs
index 2c45e704b..9fac1625b 100644
--- a/Jellyfin.Api/Models/SubtitleDtos/UploadSubtitleDto.cs
+++ b/Jellyfin.Api/Models/SubtitleDtos/UploadSubtitleDto.cs
@@ -1,4 +1,4 @@
-using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations;
namespace Jellyfin.Api.Models.SubtitleDtos;
diff --git a/Jellyfin.Api/Models/UserDtos/AuthenticateUserByName.cs b/Jellyfin.Api/Models/UserDtos/AuthenticateUserByName.cs
index 70c18a98a..6ab997266 100644
--- a/Jellyfin.Api/Models/UserDtos/AuthenticateUserByName.cs
+++ b/Jellyfin.Api/Models/UserDtos/AuthenticateUserByName.cs
@@ -1,4 +1,4 @@
-namespace Jellyfin.Api.Models.UserDtos;
+namespace Jellyfin.Api.Models.UserDtos;
/// <summary>
/// The authenticate user by name request body.
diff --git a/Jellyfin.Api/Models/UserDtos/CreateUserByName.cs b/Jellyfin.Api/Models/UserDtos/CreateUserByName.cs
index 4f9fc4e78..484a0f123 100644
--- a/Jellyfin.Api/Models/UserDtos/CreateUserByName.cs
+++ b/Jellyfin.Api/Models/UserDtos/CreateUserByName.cs
@@ -1,4 +1,4 @@
-using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations;
namespace Jellyfin.Api.Models.UserDtos;
diff --git a/Jellyfin.Api/Models/UserDtos/ForgotPasswordDto.cs b/Jellyfin.Api/Models/UserDtos/ForgotPasswordDto.cs
index 8ea51af2b..e91894db5 100644
--- a/Jellyfin.Api/Models/UserDtos/ForgotPasswordDto.cs
+++ b/Jellyfin.Api/Models/UserDtos/ForgotPasswordDto.cs
@@ -1,4 +1,4 @@
-using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations;
namespace Jellyfin.Api.Models.UserDtos;
diff --git a/Jellyfin.Api/Models/UserDtos/ForgotPasswordPinDto.cs b/Jellyfin.Api/Models/UserDtos/ForgotPasswordPinDto.cs
index 91b5520ee..701b8508a 100644
--- a/Jellyfin.Api/Models/UserDtos/ForgotPasswordPinDto.cs
+++ b/Jellyfin.Api/Models/UserDtos/ForgotPasswordPinDto.cs
@@ -1,4 +1,4 @@
-using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations;
namespace Jellyfin.Api.Models.UserDtos;
diff --git a/Jellyfin.Api/Models/UserDtos/QuickConnectDto.cs b/Jellyfin.Api/Models/UserDtos/QuickConnectDto.cs
index 245002f80..1b14351a8 100644
--- a/Jellyfin.Api/Models/UserDtos/QuickConnectDto.cs
+++ b/Jellyfin.Api/Models/UserDtos/QuickConnectDto.cs
@@ -1,4 +1,4 @@
-using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations;
namespace Jellyfin.Api.Models.UserDtos;
diff --git a/Jellyfin.Api/Models/UserDtos/UpdateUserEasyPassword.cs b/Jellyfin.Api/Models/UserDtos/UpdateUserEasyPassword.cs
index 80b6203bc..f19d0b57a 100644
--- a/Jellyfin.Api/Models/UserDtos/UpdateUserEasyPassword.cs
+++ b/Jellyfin.Api/Models/UserDtos/UpdateUserEasyPassword.cs
@@ -1,4 +1,4 @@
-namespace Jellyfin.Api.Models.UserDtos;
+namespace Jellyfin.Api.Models.UserDtos;
/// <summary>
/// The update user easy password request body.
diff --git a/Jellyfin.Api/Models/UserDtos/UpdateUserPassword.cs b/Jellyfin.Api/Models/UserDtos/UpdateUserPassword.cs
index 5347fcc9a..0576a8aa1 100644
--- a/Jellyfin.Api/Models/UserDtos/UpdateUserPassword.cs
+++ b/Jellyfin.Api/Models/UserDtos/UpdateUserPassword.cs
@@ -1,4 +1,4 @@
-namespace Jellyfin.Api.Models.UserDtos;
+namespace Jellyfin.Api.Models.UserDtos;
/// <summary>
/// The update user password request body.
diff --git a/Jellyfin.Api/Models/UserViewDtos/SpecialViewOptionDto.cs b/Jellyfin.Api/Models/UserViewDtos/SpecialViewOptionDto.cs
index 314b6a324..3d3688792 100644
--- a/Jellyfin.Api/Models/UserViewDtos/SpecialViewOptionDto.cs
+++ b/Jellyfin.Api/Models/UserViewDtos/SpecialViewOptionDto.cs
@@ -1,4 +1,4 @@
-namespace Jellyfin.Api.Models.UserViewDtos;
+namespace Jellyfin.Api.Models.UserViewDtos;
/// <summary>
/// Special view option dto.
diff --git a/Jellyfin.Data/Entities/AccessSchedule.cs b/Jellyfin.Data/Entities/AccessSchedule.cs
index befc4ca02..f534e49f3 100644
--- a/Jellyfin.Data/Entities/AccessSchedule.cs
+++ b/Jellyfin.Data/Entities/AccessSchedule.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.ComponentModel.DataAnnotations.Schema;
using System.Xml.Serialization;
using Jellyfin.Data.Enums;
diff --git a/Jellyfin.Data/Entities/CustomItemDisplayPreferences.cs b/Jellyfin.Data/Entities/CustomItemDisplayPreferences.cs
index b07b7c731..a60659512 100644
--- a/Jellyfin.Data/Entities/CustomItemDisplayPreferences.cs
+++ b/Jellyfin.Data/Entities/CustomItemDisplayPreferences.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
diff --git a/Jellyfin.Data/Entities/DisplayPreferences.cs b/Jellyfin.Data/Entities/DisplayPreferences.cs
index 646961238..f0be65769 100644
--- a/Jellyfin.Data/Entities/DisplayPreferences.cs
+++ b/Jellyfin.Data/Entities/DisplayPreferences.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
diff --git a/Jellyfin.Data/Entities/HomeSection.cs b/Jellyfin.Data/Entities/HomeSection.cs
index e194aa537..8dd6e647e 100644
--- a/Jellyfin.Data/Entities/HomeSection.cs
+++ b/Jellyfin.Data/Entities/HomeSection.cs
@@ -1,4 +1,4 @@
-using System.ComponentModel.DataAnnotations.Schema;
+using System.ComponentModel.DataAnnotations.Schema;
using Jellyfin.Data.Enums;
namespace Jellyfin.Data.Entities
diff --git a/Jellyfin.Data/Entities/ImageInfo.cs b/Jellyfin.Data/Entities/ImageInfo.cs
index b5c7a1c12..935a53a26 100644
--- a/Jellyfin.Data/Entities/ImageInfo.cs
+++ b/Jellyfin.Data/Entities/ImageInfo.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
diff --git a/Jellyfin.Data/Entities/ItemDisplayPreferences.cs b/Jellyfin.Data/Entities/ItemDisplayPreferences.cs
index 948126d0a..93e6664ea 100644
--- a/Jellyfin.Data/Entities/ItemDisplayPreferences.cs
+++ b/Jellyfin.Data/Entities/ItemDisplayPreferences.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Jellyfin.Data.Enums;
diff --git a/Jellyfin.Data/Entities/Security/ApiKey.cs b/Jellyfin.Data/Entities/Security/ApiKey.cs
index 31d865d01..1fcbe0f5e 100644
--- a/Jellyfin.Data/Entities/Security/ApiKey.cs
+++ b/Jellyfin.Data/Entities/Security/ApiKey.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Globalization;
diff --git a/Jellyfin.Data/Enums/BaseItemKind.cs b/Jellyfin.Data/Enums/BaseItemKind.cs
index 6fac6c487..a0286c318 100644
--- a/Jellyfin.Data/Enums/BaseItemKind.cs
+++ b/Jellyfin.Data/Enums/BaseItemKind.cs
@@ -1,4 +1,4 @@
-namespace Jellyfin.Data.Enums
+namespace Jellyfin.Data.Enums
{
/// <summary>
/// The base item kind.
diff --git a/Jellyfin.Data/Enums/ChromecastVersion.cs b/Jellyfin.Data/Enums/ChromecastVersion.cs
index 2622e08ef..c9c8a4a62 100644
--- a/Jellyfin.Data/Enums/ChromecastVersion.cs
+++ b/Jellyfin.Data/Enums/ChromecastVersion.cs
@@ -1,4 +1,4 @@
-namespace Jellyfin.Data.Enums
+namespace Jellyfin.Data.Enums
{
/// <summary>
/// An enum representing the version of Chromecast to be used by clients.
diff --git a/Jellyfin.Data/Enums/HomeSectionType.cs b/Jellyfin.Data/Enums/HomeSectionType.cs
index 9bcd097dc..62da8c3ff 100644
--- a/Jellyfin.Data/Enums/HomeSectionType.cs
+++ b/Jellyfin.Data/Enums/HomeSectionType.cs
@@ -1,4 +1,4 @@
-namespace Jellyfin.Data.Enums
+namespace Jellyfin.Data.Enums
{
/// <summary>
/// An enum representing the different options for the home screen sections.
diff --git a/Jellyfin.Data/Enums/IndexingKind.cs b/Jellyfin.Data/Enums/IndexingKind.cs
index c0df07714..3967712b0 100644
--- a/Jellyfin.Data/Enums/IndexingKind.cs
+++ b/Jellyfin.Data/Enums/IndexingKind.cs
@@ -1,4 +1,4 @@
-namespace Jellyfin.Data.Enums
+namespace Jellyfin.Data.Enums
{
/// <summary>
/// An enum representing a type of indexing in a user's display preferences.
diff --git a/Jellyfin.Data/Enums/MediaType.cs b/Jellyfin.Data/Enums/MediaType.cs
index b014ff366..3e8471ea1 100644
--- a/Jellyfin.Data/Enums/MediaType.cs
+++ b/Jellyfin.Data/Enums/MediaType.cs
@@ -1,4 +1,4 @@
-namespace Jellyfin.Data.Enums;
+namespace Jellyfin.Data.Enums;
/// <summary>
/// Media types.
diff --git a/Jellyfin.Data/Enums/ScrollDirection.cs b/Jellyfin.Data/Enums/ScrollDirection.cs
index 9595eb490..29c50e2c4 100644
--- a/Jellyfin.Data/Enums/ScrollDirection.cs
+++ b/Jellyfin.Data/Enums/ScrollDirection.cs
@@ -1,4 +1,4 @@
-namespace Jellyfin.Data.Enums
+namespace Jellyfin.Data.Enums
{
/// <summary>
/// An enum representing the axis that should be scrolled.
diff --git a/Jellyfin.Data/Enums/SortOrder.cs b/Jellyfin.Data/Enums/SortOrder.cs
index 760a857f5..4151448e4 100644
--- a/Jellyfin.Data/Enums/SortOrder.cs
+++ b/Jellyfin.Data/Enums/SortOrder.cs
@@ -1,4 +1,4 @@
-namespace Jellyfin.Data.Enums
+namespace Jellyfin.Data.Enums
{
/// <summary>
/// An enum representing the sorting order.
diff --git a/Jellyfin.Data/Enums/SubtitlePlaybackMode.cs b/Jellyfin.Data/Enums/SubtitlePlaybackMode.cs
index ca41300ed..79693d321 100644
--- a/Jellyfin.Data/Enums/SubtitlePlaybackMode.cs
+++ b/Jellyfin.Data/Enums/SubtitlePlaybackMode.cs
@@ -1,4 +1,4 @@
-namespace Jellyfin.Data.Enums
+namespace Jellyfin.Data.Enums
{
/// <summary>
/// An enum representing a subtitle playback mode.
diff --git a/Jellyfin.Data/Events/System/PendingRestartEventArgs.cs b/Jellyfin.Data/Events/System/PendingRestartEventArgs.cs
index 2aa40a946..ef16f27c2 100644
--- a/Jellyfin.Data/Events/System/PendingRestartEventArgs.cs
+++ b/Jellyfin.Data/Events/System/PendingRestartEventArgs.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
namespace Jellyfin.Data.Events.System
{
diff --git a/Jellyfin.Data/Events/Users/UserCreatedEventArgs.cs b/Jellyfin.Data/Events/Users/UserCreatedEventArgs.cs
index 66f7c8d4f..b3b8d2831 100644
--- a/Jellyfin.Data/Events/Users/UserCreatedEventArgs.cs
+++ b/Jellyfin.Data/Events/Users/UserCreatedEventArgs.cs
@@ -1,4 +1,4 @@
-using Jellyfin.Data.Entities;
+using Jellyfin.Data.Entities;
namespace Jellyfin.Data.Events.Users
{
diff --git a/Jellyfin.Data/Events/Users/UserDeletedEventArgs.cs b/Jellyfin.Data/Events/Users/UserDeletedEventArgs.cs
index 0b9493375..d57c917c9 100644
--- a/Jellyfin.Data/Events/Users/UserDeletedEventArgs.cs
+++ b/Jellyfin.Data/Events/Users/UserDeletedEventArgs.cs
@@ -1,4 +1,4 @@
-using Jellyfin.Data.Entities;
+using Jellyfin.Data.Entities;
namespace Jellyfin.Data.Events.Users
{
diff --git a/Jellyfin.Data/Events/Users/UserLockedOutEventArgs.cs b/Jellyfin.Data/Events/Users/UserLockedOutEventArgs.cs
index cca3726dc..447594821 100644
--- a/Jellyfin.Data/Events/Users/UserLockedOutEventArgs.cs
+++ b/Jellyfin.Data/Events/Users/UserLockedOutEventArgs.cs
@@ -1,4 +1,4 @@
-using Jellyfin.Data.Entities;
+using Jellyfin.Data.Entities;
namespace Jellyfin.Data.Events.Users
{
diff --git a/Jellyfin.Data/Events/Users/UserPasswordChangedEventArgs.cs b/Jellyfin.Data/Events/Users/UserPasswordChangedEventArgs.cs
index 087ec9ab6..a235ccada 100644
--- a/Jellyfin.Data/Events/Users/UserPasswordChangedEventArgs.cs
+++ b/Jellyfin.Data/Events/Users/UserPasswordChangedEventArgs.cs
@@ -1,4 +1,4 @@
-using Jellyfin.Data.Entities;
+using Jellyfin.Data.Entities;
namespace Jellyfin.Data.Events.Users
{
diff --git a/Jellyfin.Data/Events/Users/UserUpdatedEventArgs.cs b/Jellyfin.Data/Events/Users/UserUpdatedEventArgs.cs
index 2b4e49cdf..780ace6ab 100644
--- a/Jellyfin.Data/Events/Users/UserUpdatedEventArgs.cs
+++ b/Jellyfin.Data/Events/Users/UserUpdatedEventArgs.cs
@@ -1,4 +1,4 @@
-using Jellyfin.Data.Entities;
+using Jellyfin.Data.Entities;
namespace Jellyfin.Data.Events.Users
{
diff --git a/Jellyfin.Data/Interfaces/IHasPermissions.cs b/Jellyfin.Data/Interfaces/IHasPermissions.cs
index 85ee12ad7..bf8ec9d88 100644
--- a/Jellyfin.Data/Interfaces/IHasPermissions.cs
+++ b/Jellyfin.Data/Interfaces/IHasPermissions.cs
@@ -1,4 +1,4 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
diff --git a/Jellyfin.Data/Jellyfin.Data.csproj b/Jellyfin.Data/Jellyfin.Data.csproj
index 3adca6ef2..e24e37740 100644
--- a/Jellyfin.Data/Jellyfin.Data.csproj
+++ b/Jellyfin.Data/Jellyfin.Data.csproj
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Library/LyricDownloadFailureLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Library/LyricDownloadFailureLogger.cs
index bd717b0af..0d52bb985 100644
--- a/Jellyfin.Server.Implementations/Events/Consumers/Library/LyricDownloadFailureLogger.cs
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Library/LyricDownloadFailureLogger.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Globalization;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Library/SubtitleDownloadFailureLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Library/SubtitleDownloadFailureLogger.cs
index 449f27be2..0a8c064a9 100644
--- a/Jellyfin.Server.Implementations/Events/Consumers/Library/SubtitleDownloadFailureLogger.cs
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Library/SubtitleDownloadFailureLogger.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Globalization;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationFailedLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationFailedLogger.cs
index b5f18d983..a4424c739 100644
--- a/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationFailedLogger.cs
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationFailedLogger.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Globalization;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationSucceededLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationSucceededLogger.cs
index 3f3a0dec5..e0ecef2a5 100644
--- a/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationSucceededLogger.cs
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationSucceededLogger.cs
@@ -1,4 +1,4 @@
-using System.Globalization;
+using System.Globalization;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Events;
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStartLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStartLogger.cs
index 8a33383e3..0ef929a99 100644
--- a/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStartLogger.cs
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStartLogger.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Globalization;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStopLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStopLogger.cs
index 4c2effc2e..7d452ea2f 100644
--- a/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStopLogger.cs
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStopLogger.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Globalization;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Session/SessionEndedLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Session/SessionEndedLogger.cs
index cf20946ec..77e7859c6 100644
--- a/Jellyfin.Server.Implementations/Events/Consumers/Session/SessionEndedLogger.cs
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Session/SessionEndedLogger.cs
@@ -1,4 +1,4 @@
-using System.Globalization;
+using System.Globalization;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Events;
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Session/SessionStartedLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Session/SessionStartedLogger.cs
index 6a0f29b09..141dc20ea 100644
--- a/Jellyfin.Server.Implementations/Events/Consumers/Session/SessionStartedLogger.cs
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Session/SessionStartedLogger.cs
@@ -1,4 +1,4 @@
-using System.Globalization;
+using System.Globalization;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Events;
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/System/PendingRestartNotifier.cs b/Jellyfin.Server.Implementations/Events/Consumers/System/PendingRestartNotifier.cs
index 2fa38dd71..970df3a9a 100644
--- a/Jellyfin.Server.Implementations/Events/Consumers/System/PendingRestartNotifier.cs
+++ b/Jellyfin.Server.Implementations/Events/Consumers/System/PendingRestartNotifier.cs
@@ -1,4 +1,4 @@
-using System.Threading;
+using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Events.System;
using MediaBrowser.Controller.Events;
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/System/TaskCompletedLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/System/TaskCompletedLogger.cs
index cbc9f3017..b0a9393eb 100644
--- a/Jellyfin.Server.Implementations/Events/Consumers/System/TaskCompletedLogger.cs
+++ b/Jellyfin.Server.Implementations/Events/Consumers/System/TaskCompletedLogger.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/System/TaskCompletedNotifier.cs b/Jellyfin.Server.Implementations/Events/Consumers/System/TaskCompletedNotifier.cs
index 0993c6df7..aebe74966 100644
--- a/Jellyfin.Server.Implementations/Events/Consumers/System/TaskCompletedNotifier.cs
+++ b/Jellyfin.Server.Implementations/Events/Consumers/System/TaskCompletedNotifier.cs
@@ -1,4 +1,4 @@
-using System.Threading;
+using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Events;
using MediaBrowser.Controller.Session;
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationCancelledNotifier.cs b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationCancelledNotifier.cs
index 1d790da6b..106025386 100644
--- a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationCancelledNotifier.cs
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationCancelledNotifier.cs
@@ -1,4 +1,4 @@
-using System.Threading;
+using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Events;
using MediaBrowser.Controller.Events.Updates;
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationFailedLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationFailedLogger.cs
index d71c298c5..0ae9b7f66 100644
--- a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationFailedLogger.cs
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationFailedLogger.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Globalization;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationFailedNotifier.cs b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationFailedNotifier.cs
index a1faf18fc..23135c0e6 100644
--- a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationFailedNotifier.cs
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationFailedNotifier.cs
@@ -1,4 +1,4 @@
-using System.Threading;
+using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Updates;
using MediaBrowser.Controller.Events;
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstalledLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstalledLogger.cs
index 8837172db..287ba578b 100644
--- a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstalledLogger.cs
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstalledLogger.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Globalization;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstalledNotifier.cs b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstalledNotifier.cs
index bd1a71404..76d7c62e9 100644
--- a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstalledNotifier.cs
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstalledNotifier.cs
@@ -1,4 +1,4 @@
-using System.Threading;
+using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Events;
using MediaBrowser.Controller.Events.Updates;
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallingNotifier.cs b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallingNotifier.cs
index b513ac64a..e343b33ec 100644
--- a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallingNotifier.cs
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallingNotifier.cs
@@ -1,4 +1,4 @@
-using System.Threading;
+using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Events;
using MediaBrowser.Controller.Events.Updates;
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUninstalledLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUninstalledLogger.cs
index eb7572ac6..2de207b15 100644
--- a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUninstalledLogger.cs
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUninstalledLogger.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Globalization;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUninstalledNotifier.cs b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUninstalledNotifier.cs
index 1fd7b9adf..ed487d8ae 100644
--- a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUninstalledNotifier.cs
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUninstalledNotifier.cs
@@ -1,4 +1,4 @@
-using System.Threading;
+using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Events;
using MediaBrowser.Controller.Events.Updates;
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUpdatedLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUpdatedLogger.cs
index 9ce16f774..08d6bf9c2 100644
--- a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUpdatedLogger.cs
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUpdatedLogger.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Globalization;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Users/UserCreatedLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Users/UserCreatedLogger.cs
index dc855cc36..a09c344f6 100644
--- a/Jellyfin.Server.Implementations/Events/Consumers/Users/UserCreatedLogger.cs
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Users/UserCreatedLogger.cs
@@ -1,4 +1,4 @@
-using System.Globalization;
+using System.Globalization;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Events.Users;
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Users/UserDeletedLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Users/UserDeletedLogger.cs
index c68a62c81..46da8044a 100644
--- a/Jellyfin.Server.Implementations/Events/Consumers/Users/UserDeletedLogger.cs
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Users/UserDeletedLogger.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Globalization;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Users/UserDeletedNotifier.cs b/Jellyfin.Server.Implementations/Events/Consumers/Users/UserDeletedNotifier.cs
index 303e88621..8eefc03db 100644
--- a/Jellyfin.Server.Implementations/Events/Consumers/Users/UserDeletedNotifier.cs
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Users/UserDeletedNotifier.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Globalization;
using System.Threading;
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Users/UserLockedOutLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Users/UserLockedOutLogger.cs
index a31f222ee..1d0d016a7 100644
--- a/Jellyfin.Server.Implementations/Events/Consumers/Users/UserLockedOutLogger.cs
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Users/UserLockedOutLogger.cs
@@ -1,4 +1,4 @@
-using System.Globalization;
+using System.Globalization;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Events.Users;
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Users/UserPasswordChangedLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Users/UserPasswordChangedLogger.cs
index dc8ecbf48..2b8f966a8 100644
--- a/Jellyfin.Server.Implementations/Events/Consumers/Users/UserPasswordChangedLogger.cs
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Users/UserPasswordChangedLogger.cs
@@ -1,4 +1,4 @@
-using System.Globalization;
+using System.Globalization;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Events.Users;
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Users/UserUpdatedNotifier.cs b/Jellyfin.Server.Implementations/Events/Consumers/Users/UserUpdatedNotifier.cs
index 9beb6f2f2..0af5cbb6e 100644
--- a/Jellyfin.Server.Implementations/Events/Consumers/Users/UserUpdatedNotifier.cs
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Users/UserUpdatedNotifier.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
diff --git a/Jellyfin.Server.Implementations/Events/EventManager.cs b/Jellyfin.Server.Implementations/Events/EventManager.cs
index f49ae8e27..39235a1f7 100644
--- a/Jellyfin.Server.Implementations/Events/EventManager.cs
+++ b/Jellyfin.Server.Implementations/Events/EventManager.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Threading.Tasks;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Events;
diff --git a/Jellyfin.Server.Implementations/Events/EventingServiceCollectionExtensions.cs b/Jellyfin.Server.Implementations/Events/EventingServiceCollectionExtensions.cs
index d1db6d3b4..87de4ed79 100644
--- a/Jellyfin.Server.Implementations/Events/EventingServiceCollectionExtensions.cs
+++ b/Jellyfin.Server.Implementations/Events/EventingServiceCollectionExtensions.cs
@@ -1,4 +1,4 @@
-using Jellyfin.Data.Events.System;
+using Jellyfin.Data.Events.System;
using Jellyfin.Data.Events.Users;
using Jellyfin.Server.Implementations.Events.Consumers.Library;
using Jellyfin.Server.Implementations.Events.Consumers.Security;
diff --git a/Jellyfin.Server.Implementations/Extensions/ServiceCollectionExtensions.cs b/Jellyfin.Server.Implementations/Extensions/ServiceCollectionExtensions.cs
index ff29d69b4..ddb393d67 100644
--- a/Jellyfin.Server.Implementations/Extensions/ServiceCollectionExtensions.cs
+++ b/Jellyfin.Server.Implementations/Extensions/ServiceCollectionExtensions.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.IO;
using MediaBrowser.Common.Configuration;
using Microsoft.EntityFrameworkCore;
diff --git a/Jellyfin.Server.Implementations/Migrations/20200514181226_AddActivityLog.Designer.cs b/Jellyfin.Server.Implementations/Migrations/20200514181226_AddActivityLog.Designer.cs
index 4be6c2faa..80fe784dd 100644
--- a/Jellyfin.Server.Implementations/Migrations/20200514181226_AddActivityLog.Designer.cs
+++ b/Jellyfin.Server.Implementations/Migrations/20200514181226_AddActivityLog.Designer.cs
@@ -1,4 +1,4 @@
-#pragma warning disable CS1591
+#pragma warning disable CS1591
// <auto-generated />
using System;
diff --git a/Jellyfin.Server.Implementations/Migrations/20200514181226_AddActivityLog.cs b/Jellyfin.Server.Implementations/Migrations/20200514181226_AddActivityLog.cs
index 5e0b454d8..002e5296e 100644
--- a/Jellyfin.Server.Implementations/Migrations/20200514181226_AddActivityLog.cs
+++ b/Jellyfin.Server.Implementations/Migrations/20200514181226_AddActivityLog.cs
@@ -1,4 +1,4 @@
-#pragma warning disable CS1591
+#pragma warning disable CS1591
#pragma warning disable SA1601
using System;
diff --git a/Jellyfin.Server.Implementations/Migrations/20200613202153_AddUsers.Designer.cs b/Jellyfin.Server.Implementations/Migrations/20200613202153_AddUsers.Designer.cs
index f3254734a..7aa4479b3 100644
--- a/Jellyfin.Server.Implementations/Migrations/20200613202153_AddUsers.Designer.cs
+++ b/Jellyfin.Server.Implementations/Migrations/20200613202153_AddUsers.Designer.cs
@@ -1,4 +1,4 @@
-#pragma warning disable CS1591
+#pragma warning disable CS1591
// <auto-generated />
using System;
diff --git a/Jellyfin.Server.Implementations/Migrations/20200613202153_AddUsers.cs b/Jellyfin.Server.Implementations/Migrations/20200613202153_AddUsers.cs
index 7e5a76850..706a97ba2 100644
--- a/Jellyfin.Server.Implementations/Migrations/20200613202153_AddUsers.cs
+++ b/Jellyfin.Server.Implementations/Migrations/20200613202153_AddUsers.cs
@@ -1,4 +1,4 @@
-#pragma warning disable CS1591
+#pragma warning disable CS1591
#pragma warning disable SA1601
using System;
diff --git a/Jellyfin.Server.Implementations/Migrations/20200728005145_AddDisplayPreferences.Designer.cs b/Jellyfin.Server.Implementations/Migrations/20200728005145_AddDisplayPreferences.Designer.cs
index 12d6faa8f..3860c851d 100644
--- a/Jellyfin.Server.Implementations/Migrations/20200728005145_AddDisplayPreferences.Designer.cs
+++ b/Jellyfin.Server.Implementations/Migrations/20200728005145_AddDisplayPreferences.Designer.cs
@@ -1,4 +1,4 @@
-#pragma warning disable CS1591
+#pragma warning disable CS1591
// <auto-generated />
using System;
diff --git a/Jellyfin.Server.Implementations/Migrations/20200728005145_AddDisplayPreferences.cs b/Jellyfin.Server.Implementations/Migrations/20200728005145_AddDisplayPreferences.cs
index 3009f0b61..8cd551642 100644
--- a/Jellyfin.Server.Implementations/Migrations/20200728005145_AddDisplayPreferences.cs
+++ b/Jellyfin.Server.Implementations/Migrations/20200728005145_AddDisplayPreferences.cs
@@ -1,4 +1,4 @@
-#pragma warning disable CS1591
+#pragma warning disable CS1591
#pragma warning disable SA1601
using System;
diff --git a/Jellyfin.Server.Implementations/Migrations/20200905220533_FixDisplayPreferencesIndex.Designer.cs b/Jellyfin.Server.Implementations/Migrations/20200905220533_FixDisplayPreferencesIndex.Designer.cs
index f1cc20805..1134f7aa4 100644
--- a/Jellyfin.Server.Implementations/Migrations/20200905220533_FixDisplayPreferencesIndex.Designer.cs
+++ b/Jellyfin.Server.Implementations/Migrations/20200905220533_FixDisplayPreferencesIndex.Designer.cs
@@ -1,4 +1,4 @@
-#pragma warning disable CS1591
+#pragma warning disable CS1591
// <auto-generated />
using System;
diff --git a/Jellyfin.Server.Implementations/Migrations/20200905220533_FixDisplayPreferencesIndex.cs b/Jellyfin.Server.Implementations/Migrations/20200905220533_FixDisplayPreferencesIndex.cs
index 33c5bb4ca..91d2b190d 100644
--- a/Jellyfin.Server.Implementations/Migrations/20200905220533_FixDisplayPreferencesIndex.cs
+++ b/Jellyfin.Server.Implementations/Migrations/20200905220533_FixDisplayPreferencesIndex.cs
@@ -1,4 +1,4 @@
-#pragma warning disable CS1591
+#pragma warning disable CS1591
#pragma warning disable SA1601
using Microsoft.EntityFrameworkCore.Migrations;
diff --git a/Jellyfin.Server.Implementations/Migrations/20201004171403_AddMaxActiveSessions.Designer.cs b/Jellyfin.Server.Implementations/Migrations/20201004171403_AddMaxActiveSessions.Designer.cs
index f134d363c..607310caa 100644
--- a/Jellyfin.Server.Implementations/Migrations/20201004171403_AddMaxActiveSessions.Designer.cs
+++ b/Jellyfin.Server.Implementations/Migrations/20201004171403_AddMaxActiveSessions.Designer.cs
@@ -1,4 +1,4 @@
-#pragma warning disable CS1591
+#pragma warning disable CS1591
// <auto-generated />
using System;
diff --git a/Jellyfin.Server.Implementations/Migrations/20201004171403_AddMaxActiveSessions.cs b/Jellyfin.Server.Implementations/Migrations/20201004171403_AddMaxActiveSessions.cs
index 10acb4def..e37b4e696 100644
--- a/Jellyfin.Server.Implementations/Migrations/20201004171403_AddMaxActiveSessions.cs
+++ b/Jellyfin.Server.Implementations/Migrations/20201004171403_AddMaxActiveSessions.cs
@@ -1,4 +1,4 @@
-#pragma warning disable CS1591
+#pragma warning disable CS1591
#pragma warning disable SA1601
using Microsoft.EntityFrameworkCore.Migrations;
diff --git a/Jellyfin.Server.Implementations/Migrations/20201204223655_AddCustomDisplayPreferences.Designer.cs b/Jellyfin.Server.Implementations/Migrations/20201204223655_AddCustomDisplayPreferences.Designer.cs
index ec65205d4..02c3fc753 100644
--- a/Jellyfin.Server.Implementations/Migrations/20201204223655_AddCustomDisplayPreferences.Designer.cs
+++ b/Jellyfin.Server.Implementations/Migrations/20201204223655_AddCustomDisplayPreferences.Designer.cs
@@ -1,4 +1,4 @@
-#pragma warning disable CS1591
+#pragma warning disable CS1591
// <auto-generated />
using System;
using Jellyfin.Server.Implementations;
diff --git a/Jellyfin.Server.Implementations/Migrations/20201204223655_AddCustomDisplayPreferences.cs b/Jellyfin.Server.Implementations/Migrations/20201204223655_AddCustomDisplayPreferences.cs
index fbc0bffa9..ce2b21d0c 100644
--- a/Jellyfin.Server.Implementations/Migrations/20201204223655_AddCustomDisplayPreferences.cs
+++ b/Jellyfin.Server.Implementations/Migrations/20201204223655_AddCustomDisplayPreferences.cs
@@ -1,4 +1,4 @@
-#pragma warning disable CS1591
+#pragma warning disable CS1591
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore.Migrations;
diff --git a/Jellyfin.Server.Implementations/Migrations/20210320181425_AddIndexesAndCollations.Designer.cs b/Jellyfin.Server.Implementations/Migrations/20210320181425_AddIndexesAndCollations.Designer.cs
index 45dad6be6..1cfd7112c 100644
--- a/Jellyfin.Server.Implementations/Migrations/20210320181425_AddIndexesAndCollations.Designer.cs
+++ b/Jellyfin.Server.Implementations/Migrations/20210320181425_AddIndexesAndCollations.Designer.cs
@@ -1,4 +1,4 @@
-#pragma warning disable CS1591
+#pragma warning disable CS1591
// <auto-generated />
using System;
diff --git a/Jellyfin.Server.Implementations/Migrations/20210320181425_AddIndexesAndCollations.cs b/Jellyfin.Server.Implementations/Migrations/20210320181425_AddIndexesAndCollations.cs
index 506e4ae66..3acd5e7b5 100644
--- a/Jellyfin.Server.Implementations/Migrations/20210320181425_AddIndexesAndCollations.cs
+++ b/Jellyfin.Server.Implementations/Migrations/20210320181425_AddIndexesAndCollations.cs
@@ -1,4 +1,4 @@
-#pragma warning disable CS1591
+#pragma warning disable CS1591
#pragma warning disable SA1601
using System;
diff --git a/Jellyfin.Server.Implementations/Migrations/20210407110544_NullableCustomPrefValue.Designer.cs b/Jellyfin.Server.Implementations/Migrations/20210407110544_NullableCustomPrefValue.Designer.cs
index eff84b457..ecf7af495 100644
--- a/Jellyfin.Server.Implementations/Migrations/20210407110544_NullableCustomPrefValue.Designer.cs
+++ b/Jellyfin.Server.Implementations/Migrations/20210407110544_NullableCustomPrefValue.Designer.cs
@@ -1,4 +1,4 @@
-#pragma warning disable CS1591
+#pragma warning disable CS1591
// <auto-generated />
using System;
using Jellyfin.Server.Implementations;
diff --git a/Jellyfin.Server.Implementations/Migrations/20210407110544_NullableCustomPrefValue.cs b/Jellyfin.Server.Implementations/Migrations/20210407110544_NullableCustomPrefValue.cs
index ade68612c..a6b169a61 100644
--- a/Jellyfin.Server.Implementations/Migrations/20210407110544_NullableCustomPrefValue.cs
+++ b/Jellyfin.Server.Implementations/Migrations/20210407110544_NullableCustomPrefValue.cs
@@ -1,4 +1,4 @@
-#pragma warning disable CS1591
+#pragma warning disable CS1591
// <auto-generated />
using Microsoft.EntityFrameworkCore.Migrations;
diff --git a/Jellyfin.Server.Implementations/Migrations/20210814002109_AddDevices.Designer.cs b/Jellyfin.Server.Implementations/Migrations/20210814002109_AddDevices.Designer.cs
index ad7c2dd2c..dccba6f77 100644
--- a/Jellyfin.Server.Implementations/Migrations/20210814002109_AddDevices.Designer.cs
+++ b/Jellyfin.Server.Implementations/Migrations/20210814002109_AddDevices.Designer.cs
@@ -1,4 +1,4 @@
-#pragma warning disable CS1591
+#pragma warning disable CS1591
// <auto-generated />
using System;
diff --git a/Jellyfin.Server.Implementations/Migrations/20210814002109_AddDevices.cs b/Jellyfin.Server.Implementations/Migrations/20210814002109_AddDevices.cs
index ac062317a..bf90044cb 100644
--- a/Jellyfin.Server.Implementations/Migrations/20210814002109_AddDevices.cs
+++ b/Jellyfin.Server.Implementations/Migrations/20210814002109_AddDevices.cs
@@ -1,4 +1,4 @@
-#pragma warning disable CS1591, SA1601
+#pragma warning disable CS1591, SA1601
using System;
using Microsoft.EntityFrameworkCore.Migrations;
diff --git a/Jellyfin.Server.Implementations/Migrations/20221022080052_AddIndexActivityLogsDateCreated.Designer.cs b/Jellyfin.Server.Implementations/Migrations/20221022080052_AddIndexActivityLogsDateCreated.Designer.cs
index f9497a3b6..e821c106e 100644
--- a/Jellyfin.Server.Implementations/Migrations/20221022080052_AddIndexActivityLogsDateCreated.Designer.cs
+++ b/Jellyfin.Server.Implementations/Migrations/20221022080052_AddIndexActivityLogsDateCreated.Designer.cs
@@ -1,4 +1,4 @@
-#pragma warning disable CS1591
+#pragma warning disable CS1591
// <auto-generated />
using System;
diff --git a/Jellyfin.Server.Implementations/Migrations/20221022080052_AddIndexActivityLogsDateCreated.cs b/Jellyfin.Server.Implementations/Migrations/20221022080052_AddIndexActivityLogsDateCreated.cs
index f09ad2709..9d5d7632b 100644
--- a/Jellyfin.Server.Implementations/Migrations/20221022080052_AddIndexActivityLogsDateCreated.cs
+++ b/Jellyfin.Server.Implementations/Migrations/20221022080052_AddIndexActivityLogsDateCreated.cs
@@ -1,4 +1,4 @@
-#pragma warning disable CS1591, SA1601
+#pragma warning disable CS1591, SA1601
using Microsoft.EntityFrameworkCore.Migrations;
diff --git a/Jellyfin.Server.Implementations/Migrations/20230526173516_RemoveEasyPassword.Designer.cs b/Jellyfin.Server.Implementations/Migrations/20230526173516_RemoveEasyPassword.Designer.cs
index 00ccd9f0f..360fa0376 100644
--- a/Jellyfin.Server.Implementations/Migrations/20230526173516_RemoveEasyPassword.Designer.cs
+++ b/Jellyfin.Server.Implementations/Migrations/20230526173516_RemoveEasyPassword.Designer.cs
@@ -1,4 +1,4 @@
-// <auto-generated />
+// <auto-generated />
using System;
using Jellyfin.Server.Implementations;
using Microsoft.EntityFrameworkCore;
diff --git a/Jellyfin.Server.Implementations/Migrations/20230526173516_RemoveEasyPassword.cs b/Jellyfin.Server.Implementations/Migrations/20230526173516_RemoveEasyPassword.cs
index 9496ff3c0..354d91c38 100644
--- a/Jellyfin.Server.Implementations/Migrations/20230526173516_RemoveEasyPassword.cs
+++ b/Jellyfin.Server.Implementations/Migrations/20230526173516_RemoveEasyPassword.cs
@@ -1,4 +1,4 @@
-using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
diff --git a/Jellyfin.Server.Implementations/Migrations/20230626233818_AddTrickplayInfos.Designer.cs b/Jellyfin.Server.Implementations/Migrations/20230626233818_AddTrickplayInfos.Designer.cs
index 28baf1992..17d33845f 100644
--- a/Jellyfin.Server.Implementations/Migrations/20230626233818_AddTrickplayInfos.Designer.cs
+++ b/Jellyfin.Server.Implementations/Migrations/20230626233818_AddTrickplayInfos.Designer.cs
@@ -1,4 +1,4 @@
-// <auto-generated />
+// <auto-generated />
using System;
using Jellyfin.Server.Implementations;
using Microsoft.EntityFrameworkCore;
diff --git a/Jellyfin.Server.Implementations/Migrations/20230626233818_AddTrickplayInfos.cs b/Jellyfin.Server.Implementations/Migrations/20230626233818_AddTrickplayInfos.cs
index 76b12de08..85f1b5b7d 100644
--- a/Jellyfin.Server.Implementations/Migrations/20230626233818_AddTrickplayInfos.cs
+++ b/Jellyfin.Server.Implementations/Migrations/20230626233818_AddTrickplayInfos.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
diff --git a/Jellyfin.Server.Implementations/Migrations/20230923170422_UserCastReceiver.Designer.cs b/Jellyfin.Server.Implementations/Migrations/20230923170422_UserCastReceiver.Designer.cs
index 2884d4256..4c0917669 100644
--- a/Jellyfin.Server.Implementations/Migrations/20230923170422_UserCastReceiver.Designer.cs
+++ b/Jellyfin.Server.Implementations/Migrations/20230923170422_UserCastReceiver.Designer.cs
@@ -1,4 +1,4 @@
-// <auto-generated />
+// <auto-generated />
using System;
using Jellyfin.Server.Implementations;
using Microsoft.EntityFrameworkCore;
diff --git a/Jellyfin.Server.Implementations/Migrations/20230923170422_UserCastReceiver.cs b/Jellyfin.Server.Implementations/Migrations/20230923170422_UserCastReceiver.cs
index f06410c15..5919e4665 100644
--- a/Jellyfin.Server.Implementations/Migrations/20230923170422_UserCastReceiver.cs
+++ b/Jellyfin.Server.Implementations/Migrations/20230923170422_UserCastReceiver.cs
@@ -1,4 +1,4 @@
-using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
diff --git a/Jellyfin.Server.Implementations/Migrations/20240729140605_AddMediaSegments.Designer.cs b/Jellyfin.Server.Implementations/Migrations/20240729140605_AddMediaSegments.Designer.cs
index c03cb4760..35a3cdad2 100644
--- a/Jellyfin.Server.Implementations/Migrations/20240729140605_AddMediaSegments.Designer.cs
+++ b/Jellyfin.Server.Implementations/Migrations/20240729140605_AddMediaSegments.Designer.cs
@@ -1,4 +1,4 @@
-// <auto-generated />
+// <auto-generated />
using System;
using Jellyfin.Server.Implementations;
using Microsoft.EntityFrameworkCore;
diff --git a/Jellyfin.Server.Implementations/Migrations/20240729140605_AddMediaSegments.cs b/Jellyfin.Server.Implementations/Migrations/20240729140605_AddMediaSegments.cs
index 24a8ffc42..18164d999 100644
--- a/Jellyfin.Server.Implementations/Migrations/20240729140605_AddMediaSegments.cs
+++ b/Jellyfin.Server.Implementations/Migrations/20240729140605_AddMediaSegments.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
diff --git a/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs b/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs
index cdeeb6d87..d70f7956a 100644
--- a/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs
+++ b/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs
@@ -1,4 +1,4 @@
-// <auto-generated />
+// <auto-generated />
using System;
using Jellyfin.Server.Implementations;
using Microsoft.EntityFrameworkCore;
diff --git a/Jellyfin.Server.Implementations/ModelBuilderExtensions.cs b/Jellyfin.Server.Implementations/ModelBuilderExtensions.cs
index e73a90cff..79ae1661a 100644
--- a/Jellyfin.Server.Implementations/ModelBuilderExtensions.cs
+++ b/Jellyfin.Server.Implementations/ModelBuilderExtensions.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using Jellyfin.Server.Implementations.ValueConverters;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
diff --git a/Jellyfin.Server.Implementations/Security/AuthenticationManager.cs b/Jellyfin.Server.Implementations/Security/AuthenticationManager.cs
index 07ac27e3c..1c9f54ab0 100644
--- a/Jellyfin.Server.Implementations/Security/AuthenticationManager.cs
+++ b/Jellyfin.Server.Implementations/Security/AuthenticationManager.cs
@@ -1,4 +1,4 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Jellyfin.Data.Entities.Security;
diff --git a/Jellyfin.Server.Implementations/Users/DefaultAuthenticationProvider.cs b/Jellyfin.Server.Implementations/Users/DefaultAuthenticationProvider.cs
index cb2d09a67..acada7aa4 100644
--- a/Jellyfin.Server.Implementations/Users/DefaultAuthenticationProvider.cs
+++ b/Jellyfin.Server.Implementations/Users/DefaultAuthenticationProvider.cs
@@ -1,9 +1,11 @@
using System;
using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Authentication;
using MediaBrowser.Model.Cryptography;
+using Microsoft.Extensions.Logging;
namespace Jellyfin.Server.Implementations.Users
{
@@ -12,14 +14,17 @@ namespace Jellyfin.Server.Implementations.Users
/// </summary>
public class DefaultAuthenticationProvider : IAuthenticationProvider, IRequiresResolvedUser
{
+ private readonly ILogger<DefaultAuthenticationProvider> _logger;
private readonly ICryptoProvider _cryptographyProvider;
/// <summary>
/// Initializes a new instance of the <see cref="DefaultAuthenticationProvider"/> class.
/// </summary>
+ /// <param name="logger">The logger.</param>
/// <param name="cryptographyProvider">The cryptography provider.</param>
- public DefaultAuthenticationProvider(ICryptoProvider cryptographyProvider)
+ public DefaultAuthenticationProvider(ILogger<DefaultAuthenticationProvider> logger, ICryptoProvider cryptographyProvider)
{
+ _logger = logger;
_cryptographyProvider = cryptographyProvider;
}
@@ -75,8 +80,10 @@ namespace Jellyfin.Server.Implementations.Users
}
// Migrate old hashes to the new default
- if (!string.Equals(readyHash.Id, _cryptographyProvider.DefaultHashMethod, StringComparison.Ordinal))
+ if (!string.Equals(readyHash.Id, _cryptographyProvider.DefaultHashMethod, StringComparison.Ordinal)
+ || int.Parse(readyHash.Parameters["iterations"], CultureInfo.InvariantCulture) != Constants.DefaultIterations)
{
+ _logger.LogInformation("Migrating password hash of {User} to the latest default", username);
ChangePassword(resolvedUser, password);
}
diff --git a/Jellyfin.Server.Implementations/Users/DeviceAccessHost.cs b/Jellyfin.Server.Implementations/Users/DeviceAccessHost.cs
index 634aea9f0..45b0a0853 100644
--- a/Jellyfin.Server.Implementations/Users/DeviceAccessHost.cs
+++ b/Jellyfin.Server.Implementations/Users/DeviceAccessHost.cs
@@ -1,4 +1,4 @@
-using System.Threading;
+using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
diff --git a/Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs b/Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs
index edc6aa173..e204a16a6 100644
--- a/Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs
+++ b/Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs
@@ -1,4 +1,4 @@
-#pragma warning disable CA1307
+#pragma warning disable CA1307
#pragma warning disable CA1309
using System;
diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs
index 5753e75c9..eb4bc2aff 100644
--- a/Jellyfin.Server.Implementations/Users/UserManager.cs
+++ b/Jellyfin.Server.Implementations/Users/UserManager.cs
@@ -1,4 +1,4 @@
-#pragma warning disable CA1307
+#pragma warning disable CA1307
using System;
using System.Collections.Concurrent;
@@ -384,7 +384,6 @@ namespace Jellyfin.Server.Implementations.Users
public async Task<User?> AuthenticateUser(
string username,
string password,
- string passwordSha1,
string remoteEndPoint,
bool isUserSession)
{
diff --git a/Jellyfin.Server.Implementations/ValueConverters/DateTimeKindValueConverter.cs b/Jellyfin.Server.Implementations/ValueConverters/DateTimeKindValueConverter.cs
index a9a18c823..2e585c92d 100644
--- a/Jellyfin.Server.Implementations/ValueConverters/DateTimeKindValueConverter.cs
+++ b/Jellyfin.Server.Implementations/ValueConverters/DateTimeKindValueConverter.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace Jellyfin.Server.Implementations.ValueConverters
diff --git a/Jellyfin.Server/Configuration/CorsPolicyProvider.cs b/Jellyfin.Server/Configuration/CorsPolicyProvider.cs
index b061be33b..8ef78d925 100644
--- a/Jellyfin.Server/Configuration/CorsPolicyProvider.cs
+++ b/Jellyfin.Server/Configuration/CorsPolicyProvider.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Threading.Tasks;
using MediaBrowser.Controller.Configuration;
using Microsoft.AspNetCore.Cors.Infrastructure;
diff --git a/Jellyfin.Server/Filters/FileRequestFilter.cs b/Jellyfin.Server/Filters/FileRequestFilter.cs
index bb5d6a412..86dbf7657 100644
--- a/Jellyfin.Server/Filters/FileRequestFilter.cs
+++ b/Jellyfin.Server/Filters/FileRequestFilter.cs
@@ -1,4 +1,4 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
using Jellyfin.Api.Attributes;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
diff --git a/Jellyfin.Server/Filters/FileResponseFilter.cs b/Jellyfin.Server/Filters/FileResponseFilter.cs
index 1a4559d26..cd0acadf3 100644
--- a/Jellyfin.Server/Filters/FileResponseFilter.cs
+++ b/Jellyfin.Server/Filters/FileResponseFilter.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Linq;
using Jellyfin.Api.Attributes;
using Microsoft.OpenApi.Models;
diff --git a/Jellyfin.Server/Filters/ParameterObsoleteFilter.cs b/Jellyfin.Server/Filters/ParameterObsoleteFilter.cs
index b9ce221f5..98a8dc0f1 100644
--- a/Jellyfin.Server/Filters/ParameterObsoleteFilter.cs
+++ b/Jellyfin.Server/Filters/ParameterObsoleteFilter.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Linq;
using Jellyfin.Api.Attributes;
using Microsoft.OpenApi.Models;
diff --git a/Jellyfin.Server/Filters/SecurityRequirementsOperationFilter.cs b/Jellyfin.Server/Filters/SecurityRequirementsOperationFilter.cs
index fb0bd817c..401392a63 100644
--- a/Jellyfin.Server/Filters/SecurityRequirementsOperationFilter.cs
+++ b/Jellyfin.Server/Filters/SecurityRequirementsOperationFilter.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
using Jellyfin.Api.Auth.DefaultAuthorizationPolicy;
diff --git a/Jellyfin.Server/Infrastructure/SymlinkFollowingPhysicalFileResultExecutor.cs b/Jellyfin.Server/Infrastructure/SymlinkFollowingPhysicalFileResultExecutor.cs
index fd68975ff..801026c54 100644
--- a/Jellyfin.Server/Infrastructure/SymlinkFollowingPhysicalFileResultExecutor.cs
+++ b/Jellyfin.Server/Infrastructure/SymlinkFollowingPhysicalFileResultExecutor.cs
@@ -1,4 +1,4 @@
-// The MIT License (MIT)
+// The MIT License (MIT)
//
// Copyright (c) .NET Foundation and Contributors
//
diff --git a/Jellyfin.Server/Migrations/PreStartupRoutines/CreateNetworkConfiguration.cs b/Jellyfin.Server/Migrations/PreStartupRoutines/CreateNetworkConfiguration.cs
index 2c2715526..139a6ec64 100644
--- a/Jellyfin.Server/Migrations/PreStartupRoutines/CreateNetworkConfiguration.cs
+++ b/Jellyfin.Server/Migrations/PreStartupRoutines/CreateNetworkConfiguration.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
diff --git a/Jellyfin.Server/Migrations/Routines/AddDefaultCastReceivers.cs b/Jellyfin.Server/Migrations/Routines/AddDefaultCastReceivers.cs
index 57a5c8a62..2047ec743 100644
--- a/Jellyfin.Server/Migrations/Routines/AddDefaultCastReceivers.cs
+++ b/Jellyfin.Server/Migrations/Routines/AddDefaultCastReceivers.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Model.System;
diff --git a/Jellyfin.Server/Migrations/Routines/AddDefaultPluginRepository.cs b/Jellyfin.Server/Migrations/Routines/AddDefaultPluginRepository.cs
index 9e12c2e6b..fc6b5d597 100644
--- a/Jellyfin.Server/Migrations/Routines/AddDefaultPluginRepository.cs
+++ b/Jellyfin.Server/Migrations/Routines/AddDefaultPluginRepository.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Model.Updates;
diff --git a/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs
index 249b39ae4..502a37cde 100644
--- a/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs
+++ b/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
diff --git a/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs
index 808c5e33b..7dcae5bd9 100644
--- a/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs
+++ b/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.IO;
using Emby.Server.Implementations.Data;
using Jellyfin.Data.Entities;
diff --git a/MediaBrowser.Common/Providers/ProviderIdParsers.cs b/MediaBrowser.Common/Providers/ProviderIdParsers.cs
index d569167b1..fb3d6cd75 100644
--- a/MediaBrowser.Common/Providers/ProviderIdParsers.cs
+++ b/MediaBrowser.Common/Providers/ProviderIdParsers.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Diagnostics.CodeAnalysis;
namespace MediaBrowser.Common.Providers
diff --git a/MediaBrowser.Controller/Channels/ChannelLatestMediaSearch.cs b/MediaBrowser.Controller/Channels/ChannelLatestMediaSearch.cs
index e02f42fa4..ebbe13763 100644
--- a/MediaBrowser.Controller/Channels/ChannelLatestMediaSearch.cs
+++ b/MediaBrowser.Controller/Channels/ChannelLatestMediaSearch.cs
@@ -1,4 +1,4 @@
-#nullable disable
+#nullable disable
#pragma warning disable CS1591
diff --git a/MediaBrowser.Controller/Channels/IDisableMediaSourceDisplay.cs b/MediaBrowser.Controller/Channels/IDisableMediaSourceDisplay.cs
index 0539b9048..51a5ca8bf 100644
--- a/MediaBrowser.Controller/Channels/IDisableMediaSourceDisplay.cs
+++ b/MediaBrowser.Controller/Channels/IDisableMediaSourceDisplay.cs
@@ -1,4 +1,4 @@
-namespace MediaBrowser.Controller.Channels
+namespace MediaBrowser.Controller.Channels
{
/// <summary>
/// Disable media source display.
diff --git a/MediaBrowser.Controller/Channels/IHasFolderAttributes.cs b/MediaBrowser.Controller/Channels/IHasFolderAttributes.cs
index 6c92785d2..39fac78d8 100644
--- a/MediaBrowser.Controller/Channels/IHasFolderAttributes.cs
+++ b/MediaBrowser.Controller/Channels/IHasFolderAttributes.cs
@@ -1,4 +1,4 @@
-#pragma warning disable CA1819, CS1591
+#pragma warning disable CA1819, CS1591
namespace MediaBrowser.Controller.Channels
{
diff --git a/MediaBrowser.Controller/Channels/ISupportsDelete.cs b/MediaBrowser.Controller/Channels/ISupportsDelete.cs
index 30798a4b2..0110bfa7a 100644
--- a/MediaBrowser.Controller/Channels/ISupportsDelete.cs
+++ b/MediaBrowser.Controller/Channels/ISupportsDelete.cs
@@ -1,4 +1,4 @@
-#pragma warning disable CS1591
+#pragma warning disable CS1591
using System.Threading;
using System.Threading.Tasks;
diff --git a/MediaBrowser.Controller/Channels/ISupportsLatestMedia.cs b/MediaBrowser.Controller/Channels/ISupportsLatestMedia.cs
index 8ecc68bab..1935ec0f5 100644
--- a/MediaBrowser.Controller/Channels/ISupportsLatestMedia.cs
+++ b/MediaBrowser.Controller/Channels/ISupportsLatestMedia.cs
@@ -1,4 +1,4 @@
-#pragma warning disable CS1591
+#pragma warning disable CS1591
using System.Collections.Generic;
using System.Threading;
diff --git a/MediaBrowser.Controller/Channels/ISupportsMediaProbe.cs b/MediaBrowser.Controller/Channels/ISupportsMediaProbe.cs
index bc7683125..dbd256a6a 100644
--- a/MediaBrowser.Controller/Channels/ISupportsMediaProbe.cs
+++ b/MediaBrowser.Controller/Channels/ISupportsMediaProbe.cs
@@ -1,4 +1,4 @@
-namespace MediaBrowser.Controller.Channels
+namespace MediaBrowser.Controller.Channels
{
/// <summary>
/// Channel supports media probe.
diff --git a/MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs b/MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs
index 2a7e6be0f..14dc64dab 100644
--- a/MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs
+++ b/MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.IO;
using System.Threading.Tasks;
diff --git a/MediaBrowser.Controller/ClientEvent/IClientEventLogger.cs b/MediaBrowser.Controller/ClientEvent/IClientEventLogger.cs
index ad8a1bd24..efb25457f 100644
--- a/MediaBrowser.Controller/ClientEvent/IClientEventLogger.cs
+++ b/MediaBrowser.Controller/ClientEvent/IClientEventLogger.cs
@@ -1,4 +1,4 @@
-using System.IO;
+using System.IO;
using System.Threading.Tasks;
namespace MediaBrowser.Controller.ClientEvent
diff --git a/MediaBrowser.Controller/Collections/CollectionCreatedEventArgs.cs b/MediaBrowser.Controller/Collections/CollectionCreatedEventArgs.cs
index 1797d15ea..456290f12 100644
--- a/MediaBrowser.Controller/Collections/CollectionCreatedEventArgs.cs
+++ b/MediaBrowser.Controller/Collections/CollectionCreatedEventArgs.cs
@@ -1,4 +1,4 @@
-#nullable disable
+#nullable disable
#pragma warning disable CS1591
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs
index 634f34681..125f8f225 100644
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ b/MediaBrowser.Controller/Entities/BaseItem.cs
@@ -1844,7 +1844,7 @@ namespace MediaBrowser.Controller.Entities
data.LastPlayedDate = datePlayed ?? data.LastPlayedDate ?? DateTime.UtcNow;
data.Played = true;
- UserDataManager.SaveUserData(user.Id, this, data, UserDataSaveReason.TogglePlayed, CancellationToken.None);
+ UserDataManager.SaveUserData(user, this, data, UserDataSaveReason.TogglePlayed, CancellationToken.None);
}
/// <summary>
@@ -1866,7 +1866,7 @@ namespace MediaBrowser.Controller.Entities
data.LastPlayedDate = null;
data.Played = false;
- UserDataManager.SaveUserData(user.Id, this, data, UserDataSaveReason.TogglePlayed, CancellationToken.None);
+ UserDataManager.SaveUserData(user, this, data, UserDataSaveReason.TogglePlayed, CancellationToken.None);
}
/// <summary>
diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs
index b2e5d7263..83c19a54e 100644
--- a/MediaBrowser.Controller/Entities/Folder.cs
+++ b/MediaBrowser.Controller/Entities/Folder.cs
@@ -1732,12 +1732,9 @@ namespace MediaBrowser.Controller.Entities
return;
}
- if (itemDto is not null)
+ if (itemDto is not null && fields.ContainsField(ItemFields.RecursiveItemCount))
{
- if (fields.ContainsField(ItemFields.RecursiveItemCount))
- {
- itemDto.RecursiveItemCount = GetRecursiveChildCount(user);
- }
+ itemDto.RecursiveItemCount = GetRecursiveChildCount(user);
}
if (SupportsPlayedStatus)
diff --git a/MediaBrowser.Controller/Entities/LinkedChildComparer.cs b/MediaBrowser.Controller/Entities/LinkedChildComparer.cs
index de8b16808..4f13ac61f 100644
--- a/MediaBrowser.Controller/Entities/LinkedChildComparer.cs
+++ b/MediaBrowser.Controller/Entities/LinkedChildComparer.cs
@@ -1,4 +1,4 @@
-#nullable disable
+#nullable disable
#pragma warning disable CS1591
diff --git a/MediaBrowser.Controller/Entities/LinkedChildType.cs b/MediaBrowser.Controller/Entities/LinkedChildType.cs
index d39e36ff2..3bd260a10 100644
--- a/MediaBrowser.Controller/Entities/LinkedChildType.cs
+++ b/MediaBrowser.Controller/Entities/LinkedChildType.cs
@@ -1,4 +1,4 @@
-namespace MediaBrowser.Controller.Entities
+namespace MediaBrowser.Controller.Entities
{
/// <summary>
/// The linked child type.
diff --git a/MediaBrowser.Controller/Entities/UserRootFolder.cs b/MediaBrowser.Controller/Entities/UserRootFolder.cs
index fc8a29763..a687adedd 100644
--- a/MediaBrowser.Controller/Entities/UserRootFolder.cs
+++ b/MediaBrowser.Controller/Entities/UserRootFolder.cs
@@ -76,7 +76,7 @@ namespace MediaBrowser.Controller.Entities
var result = UserViewManager.GetUserViews(new UserViewQuery
{
- UserId = query.User.Id,
+ User = query.User,
PresetViews = query.PresetViews
});
diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
index 3a1d0c070..2fda7ee6f 100644
--- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs
+++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
@@ -337,7 +337,7 @@ namespace MediaBrowser.Controller.Entities
{
Limit = query.Limit,
StartIndex = query.StartIndex,
- UserId = query.User.Id
+ User = query.User
},
parentFolders,
query.DtoOptions);
diff --git a/MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs b/MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs
index 6c58064ce..0aaf4fcd9 100644
--- a/MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs
+++ b/MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs
@@ -30,6 +30,11 @@ namespace MediaBrowser.Controller.Extensions
public const string FfmpegProbeSizeKey = "FFmpeg:probesize";
/// <summary>
+ /// The key for the skipping FFmpeg validation.
+ /// </summary>
+ public const string FfmpegSkipValidationKey = "FFmpeg:novalidation";
+
+ /// <summary>
/// The key for the FFmpeg analyze duration option.
/// </summary>
public const string FfmpegAnalyzeDurationKey = "FFmpeg:analyzeduration";
@@ -90,6 +95,14 @@ namespace MediaBrowser.Controller.Extensions
=> configuration[FfmpegAnalyzeDurationKey];
/// <summary>
+ /// Gets a value indicating whether the server should validate FFmpeg during startup.
+ /// </summary>
+ /// <param name="configuration">The configuration to read the setting from.</param>
+ /// <returns><c>true</c> if the server should validate FFmpeg during startup, otherwise <c>false</c>.</returns>
+ public static bool GetFFmpegSkipValidation(this IConfiguration configuration)
+ => configuration.GetValue<bool>(FfmpegSkipValidationKey);
+
+ /// <summary>
/// Gets a value indicating whether playlists should allow duplicate entries from the <see cref="IConfiguration"/>.
/// </summary>
/// <param name="configuration">The configuration to read the setting from.</param>
diff --git a/MediaBrowser.Controller/Library/IDirectStreamProvider.cs b/MediaBrowser.Controller/Library/IDirectStreamProvider.cs
index 96f8b7eba..2dad58f6e 100644
--- a/MediaBrowser.Controller/Library/IDirectStreamProvider.cs
+++ b/MediaBrowser.Controller/Library/IDirectStreamProvider.cs
@@ -1,4 +1,4 @@
-using System.IO;
+using System.IO;
namespace MediaBrowser.Controller.Library
{
diff --git a/MediaBrowser.Controller/Library/IUserDataManager.cs b/MediaBrowser.Controller/Library/IUserDataManager.cs
index 43cccfc65..f36fd393f 100644
--- a/MediaBrowser.Controller/Library/IUserDataManager.cs
+++ b/MediaBrowser.Controller/Library/IUserDataManager.cs
@@ -1,7 +1,3 @@
-#nullable disable
-
-#pragma warning disable CA1002, CA1707, CS1591
-
using System;
using System.Collections.Generic;
using System.Threading;
@@ -21,18 +17,16 @@ namespace MediaBrowser.Controller.Library
/// <summary>
/// Occurs when [user data saved].
/// </summary>
- event EventHandler<UserDataSaveEventArgs> UserDataSaved;
+ event EventHandler<UserDataSaveEventArgs>? UserDataSaved;
/// <summary>
/// Saves the user data.
/// </summary>
- /// <param name="userId">The user id.</param>
+ /// <param name="user">The user.</param>
/// <param name="item">The item.</param>
/// <param name="userData">The user data.</param>
/// <param name="reason">The reason.</param>
/// <param name="cancellationToken">The cancellation token.</param>
- void SaveUserData(Guid userId, BaseItem item, UserItemData userData, UserDataSaveReason reason, CancellationToken cancellationToken);
-
void SaveUserData(User user, BaseItem item, UserItemData userData, UserDataSaveReason reason, CancellationToken cancellationToken);
/// <summary>
@@ -44,10 +38,14 @@ namespace MediaBrowser.Controller.Library
/// <param name="reason">The reason.</param>
void SaveUserData(User user, BaseItem item, UpdateUserItemDataDto userDataDto, UserDataSaveReason reason);
+ /// <summary>
+ /// Gets the user data.
+ /// </summary>
+ /// <param name="user">User to use.</param>
+ /// <param name="item">Item to use.</param>
+ /// <returns>User data.</returns>
UserItemData GetUserData(User user, BaseItem item);
- UserItemData GetUserData(Guid userId, BaseItem item);
-
/// <summary>
/// Gets the user data dto.
/// </summary>
@@ -56,22 +54,15 @@ namespace MediaBrowser.Controller.Library
/// <returns>User data dto.</returns>
UserItemDataDto GetUserDataDto(BaseItem item, User user);
- UserItemDataDto GetUserDataDto(BaseItem item, BaseItemDto itemDto, User user, DtoOptions options);
-
/// <summary>
- /// Get all user data for the given user.
- /// </summary>
- /// <param name="userId">The user id.</param>
- /// <returns>The user item data.</returns>
- List<UserItemData> GetAllUserData(Guid userId);
-
- /// <summary>
- /// Save the all provided user data for the given user.
+ /// Gets the user data dto.
/// </summary>
- /// <param name="userId">The user id.</param>
- /// <param name="userData">The array of user data.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- void SaveAllUserData(Guid userId, UserItemData[] userData, CancellationToken cancellationToken);
+ /// <param name="item">Item to use.</param>
+ /// <param name="itemDto">Item dto to use.</param>
+ /// <param name="user">User to use.</param>
+ /// <param name="options">Dto options to use.</param>
+ /// <returns>User data dto.</returns>
+ UserItemDataDto GetUserDataDto(BaseItem item, BaseItemDto? itemDto, User user, DtoOptions options);
/// <summary>
/// Updates playstate for an item and returns true or false indicating if it was played to completion.
diff --git a/MediaBrowser.Controller/Library/IUserManager.cs b/MediaBrowser.Controller/Library/IUserManager.cs
index 6d6a532db..1c115be85 100644
--- a/MediaBrowser.Controller/Library/IUserManager.cs
+++ b/MediaBrowser.Controller/Library/IUserManager.cs
@@ -117,11 +117,10 @@ namespace MediaBrowser.Controller.Library
/// </summary>
/// <param name="username">The user.</param>
/// <param name="password">The password to use.</param>
- /// <param name="passwordSha1">Hash of password.</param>
/// <param name="remoteEndPoint">Remove endpoint to use.</param>
/// <param name="isUserSession">Specifies if a user session.</param>
/// <returns>User wrapped in awaitable task.</returns>
- Task<User?> AuthenticateUser(string username, string password, string passwordSha1, string remoteEndPoint, bool isUserSession);
+ Task<User?> AuthenticateUser(string username, string password, string remoteEndPoint, bool isUserSession);
/// <summary>
/// Starts the forgot password process.
diff --git a/MediaBrowser.Controller/LiveTv/ActiveRecordingInfo.cs b/MediaBrowser.Controller/LiveTv/ActiveRecordingInfo.cs
index 1a81a8a31..0e0f96291 100644
--- a/MediaBrowser.Controller/LiveTv/ActiveRecordingInfo.cs
+++ b/MediaBrowser.Controller/LiveTv/ActiveRecordingInfo.cs
@@ -1,4 +1,4 @@
-#nullable disable
+#nullable disable
#pragma warning disable CS1591
diff --git a/MediaBrowser.Controller/LiveTv/IListingsManager.cs b/MediaBrowser.Controller/LiveTv/IListingsManager.cs
index bbf569575..e2c0fbd67 100644
--- a/MediaBrowser.Controller/LiveTv/IListingsManager.cs
+++ b/MediaBrowser.Controller/LiveTv/IListingsManager.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
diff --git a/MediaBrowser.Controller/LiveTv/ITunerHostManager.cs b/MediaBrowser.Controller/LiveTv/ITunerHostManager.cs
index 3df6066f6..8247066cc 100644
--- a/MediaBrowser.Controller/LiveTv/ITunerHostManager.cs
+++ b/MediaBrowser.Controller/LiveTv/ITunerHostManager.cs
@@ -1,4 +1,4 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Dto;
diff --git a/MediaBrowser.Controller/MediaEncoding/BaseEncodingJobOptions.cs b/MediaBrowser.Controller/MediaEncoding/BaseEncodingJobOptions.cs
index 03ec6c658..f77186e25 100644
--- a/MediaBrowser.Controller/MediaEncoding/BaseEncodingJobOptions.cs
+++ b/MediaBrowser.Controller/MediaEncoding/BaseEncodingJobOptions.cs
@@ -1,4 +1,4 @@
-#nullable disable
+#nullable disable
#pragma warning disable CS1591
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index f330ed6ab..24cd141dc 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -65,6 +65,10 @@ namespace MediaBrowser.Controller.MediaEncoding
private readonly Version _minFFmpegVaapiH26xEncA53CcSei = new Version(6, 0);
private readonly Version _minFFmpegReadrateOption = new Version(5, 0);
private readonly Version _minFFmpegWorkingVtHwSurface = new Version(7, 0, 1);
+ private readonly Version _minFFmpegDisplayRotationOption = new Version(6, 0);
+ private readonly Version _minFFmpegAdvancedTonemapMode = new Version(7, 0, 1);
+ private readonly Version _minFFmpegAlteredVaVkInterop = new Version(7, 0, 1);
+ private readonly Version _minFFmpegQsvVppTonemapOption = new Version(7, 0, 1);
private static readonly Regex _validationRegex = new(ValidationRegex, RegexOptions.Compiled);
@@ -103,6 +107,9 @@ namespace MediaBrowser.Controller.MediaEncoding
"m4v",
};
+ private static readonly string[] _legacyTonemapModes = new[] { "max", "rgb" };
+ private static readonly string[] _advancedTonemapModes = new[] { "lum", "itp" };
+
// Set max transcoding channels for encoders that can't handle more than a set amount of channels
// AAC, FLAC, ALAC, libopus, libvorbis encoders all support at least 8 channels
private static readonly Dictionary<string, int> _audioTranscodeChannelLookup = new(StringComparer.OrdinalIgnoreCase)
@@ -231,6 +238,7 @@ namespace MediaBrowser.Controller.MediaEncoding
&& _mediaEncoder.SupportsFilter("tonemap_vaapi")
&& _mediaEncoder.SupportsFilter("procamp_vaapi")
&& _mediaEncoder.SupportsFilterWithOption(FilterOptionType.OverlayVaapiFrameSync)
+ && _mediaEncoder.SupportsFilter("transpose_vaapi")
&& _mediaEncoder.SupportsFilter("hwupload_vaapi");
}
@@ -248,6 +256,8 @@ namespace MediaBrowser.Controller.MediaEncoding
&& _mediaEncoder.SupportsFilter("scale_opencl")
&& _mediaEncoder.SupportsFilterWithOption(FilterOptionType.TonemapOpenclBt2390)
&& _mediaEncoder.SupportsFilterWithOption(FilterOptionType.OverlayOpenclFrameSync);
+
+ // Let transpose_opencl optional for the time being.
}
private bool IsCudaFullSupported()
@@ -258,6 +268,8 @@ namespace MediaBrowser.Controller.MediaEncoding
&& _mediaEncoder.SupportsFilterWithOption(FilterOptionType.TonemapCudaName)
&& _mediaEncoder.SupportsFilter("overlay_cuda")
&& _mediaEncoder.SupportsFilter("hwupload_cuda");
+
+ // Let transpose_cuda optional for the time being.
}
private bool IsVulkanFullSupported()
@@ -265,7 +277,9 @@ namespace MediaBrowser.Controller.MediaEncoding
return _mediaEncoder.SupportsHwaccel("vulkan")
&& _mediaEncoder.SupportsFilter("libplacebo")
&& _mediaEncoder.SupportsFilter("scale_vulkan")
- && _mediaEncoder.SupportsFilterWithOption(FilterOptionType.OverlayVulkanFrameSync);
+ && _mediaEncoder.SupportsFilterWithOption(FilterOptionType.OverlayVulkanFrameSync)
+ && _mediaEncoder.SupportsFilter("transpose_vulkan")
+ && _mediaEncoder.SupportsFilter("flip_vulkan");
}
private bool IsVideoToolboxFullSupported()
@@ -275,6 +289,8 @@ namespace MediaBrowser.Controller.MediaEncoding
&& _mediaEncoder.SupportsFilter("overlay_videotoolbox")
&& _mediaEncoder.SupportsFilter("tonemap_videotoolbox")
&& _mediaEncoder.SupportsFilter("scale_vt");
+
+ // Let transpose_vt optional for the time being.
}
private bool IsSwTonemapAvailable(EncodingJobInfo state, EncodingOptions options)
@@ -282,14 +298,12 @@ namespace MediaBrowser.Controller.MediaEncoding
if (state.VideoStream is null
|| !options.EnableTonemapping
|| GetVideoColorBitDepth(state) != 10
- || !_mediaEncoder.SupportsFilter("tonemapx")
- || !(string.Equals(state.VideoStream?.ColorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase) || string.Equals(state.VideoStream?.ColorTransfer, "arib-std-b67", StringComparison.OrdinalIgnoreCase)))
+ || !_mediaEncoder.SupportsFilter("tonemapx"))
{
return false;
}
- return state.VideoStream.VideoRange == VideoRange.HDR
- && state.VideoStream.VideoRangeType is VideoRangeType.HDR10 or VideoRangeType.HLG or VideoRangeType.DOVIWithHDR10 or VideoRangeType.DOVIWithHLG;
+ return state.VideoStream.VideoRange == VideoRange.HDR;
}
private bool IsHwTonemapAvailable(EncodingJobInfo state, EncodingOptions options)
@@ -335,7 +349,7 @@ namespace MediaBrowser.Controller.MediaEncoding
&& GetVideoColorBitDepth(state) == 10;
}
- private bool IsVaapiVppTonemapAvailable(EncodingJobInfo state, EncodingOptions options)
+ private bool IsIntelVppTonemapAvailable(EncodingJobInfo state, EncodingOptions options)
{
if (state.VideoStream is null
|| !options.EnableVppTonemapping
@@ -344,7 +358,14 @@ namespace MediaBrowser.Controller.MediaEncoding
return false;
}
- // Native VPP tonemapping may come to QSV in the future.
+ // prefer 'tonemap_vaapi' over 'vpp_qsv' on Linux for supporting Gen9/KBLx.
+ // 'vpp_qsv' requires VPL, which is only supported on Gen12/TGLx and newer.
+ if (OperatingSystem.IsWindows()
+ && string.Equals(options.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase)
+ && _mediaEncoder.EncoderVersion < _minFFmpegQsvVppTonemapOption)
+ {
+ return false;
+ }
return state.VideoStream.VideoRange == VideoRange.HDR
&& (state.VideoStream.VideoRangeType == VideoRangeType.HDR10
@@ -865,17 +886,23 @@ namespace MediaBrowser.Controller.MediaEncoding
renderNodePath);
}
- private string GetQsvDeviceArgs(string alias)
+ private string GetQsvDeviceArgs(string renderNodePath, string alias)
{
var arg = " -init_hw_device qsv=" + (alias ?? QsvAlias);
if (OperatingSystem.IsLinux())
{
// derive qsv from vaapi device
- return GetVaapiDeviceArgs(null, "iHD", "i915", null, VaapiAlias) + arg + "@" + VaapiAlias;
+ return GetVaapiDeviceArgs(renderNodePath, "iHD", "i915", null, VaapiAlias) + arg + "@" + VaapiAlias;
}
if (OperatingSystem.IsWindows())
{
+ // on Windows, the deviceIndex is an int
+ if (int.TryParse(renderNodePath, NumberStyles.Integer, CultureInfo.InvariantCulture, out int deviceIndex))
+ {
+ return GetD3d11vaDeviceArgs(deviceIndex, string.Empty, D3d11vaAlias) + arg + "@" + D3d11vaAlias;
+ }
+
// derive qsv from d3d11va device
return GetD3d11vaDeviceArgs(0, "0x8086", D3d11vaAlias) + arg + "@" + D3d11vaAlias;
}
@@ -1035,7 +1062,7 @@ namespace MediaBrowser.Controller.MediaEncoding
return string.Empty;
}
- args.Append(GetQsvDeviceArgs(QsvAlias));
+ args.Append(GetQsvDeviceArgs(options.QsvDevice, QsvAlias));
var filterDevArgs = GetFilterHwDeviceArgs(QsvAlias);
// child device used by qsv.
if (_mediaEncoder.SupportsHwaccel("vaapi") || _mediaEncoder.SupportsHwaccel("d3d11va"))
@@ -1147,9 +1174,6 @@ namespace MediaBrowser.Controller.MediaEncoding
args.Append(vidDecoder);
}
- // hw transpose filters should be added manually.
- args.Append(" -noautorotate");
-
return args.ToString().Trim();
}
@@ -1473,7 +1497,6 @@ namespace MediaBrowser.Controller.MediaEncoding
}
}
- // TODO: Perhaps also use original_size=1920x800 ??
return string.Format(
CultureInfo.InvariantCulture,
"subtitles=f='{0}'{1}{2}{3}{4}{5}",
@@ -1495,7 +1518,6 @@ namespace MediaBrowser.Controller.MediaEncoding
alphaParam,
sub2videoParam,
fontParam,
- // fallbackFontParam,
setPtsParam);
}
@@ -1648,7 +1670,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var doOclTonemap = _mediaEncoder.SupportsHwaccel("qsv")
&& IsVaapiSupported(state)
&& IsOpenclFullSupported()
- && !IsVaapiVppTonemapAvailable(state, encodingOptions)
+ && !IsIntelVppTonemapAvailable(state, encodingOptions)
&& IsHwTonemapAvailable(state, encodingOptions);
enableWaFori915Hang = isIntelDecoder && doOclTonemap;
@@ -1769,12 +1791,6 @@ namespace MediaBrowser.Controller.MediaEncoding
{
param += " -preset veryfast";
}
-
- // Only h264_qsv has look_ahead option
- if (string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase))
- {
- param += " -look_ahead 0";
- }
}
else if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) // h264 (h264_nvenc)
|| string.Equals(videoEncoder, "hevc_nvenc", StringComparison.OrdinalIgnoreCase) // hevc (hevc_nvenc)
@@ -2061,7 +2077,7 @@ namespace MediaBrowser.Controller.MediaEncoding
if (string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase))
{
- param += " -x264opts:0 subme=0:me_range=4:rc_lookahead=10:me=dia:no_chroma_me:8x8dct=0:partitions=none";
+ param += " -x264opts:0 subme=0:me_range=16:rc_lookahead=10:me=hex:open_gop=0";
}
if (string.Equals(videoEncoder, "libx265", StringComparison.OrdinalIgnoreCase))
@@ -2069,8 +2085,7 @@ namespace MediaBrowser.Controller.MediaEncoding
// libx265 only accept level option in -x265-params.
// level option may cause libx265 to fail.
// libx265 cannot adjust the given level, just throw an error.
- // TODO: set fine tuned params.
- param += " -x265-params:0 no-info=1";
+ param += " -x265-params:0 subme=3:merange=25:rc-lookahead=10:me=star:ctu=32:max-tu-size=32:min-cu-size=16:rskip=2:rskip-edge-threshold=2:no-sao=1:no-strong-intra-smoothing=1:no-scenecut=1:no-open-gop=1:no-info=1";
}
if (string.Equals(videoEncoder, "libsvtav1", StringComparison.OrdinalIgnoreCase)
@@ -2947,8 +2962,10 @@ namespace MediaBrowser.Controller.MediaEncoding
}
public static string GetHwScaleFilter(
+ string hwScalePrefix,
string hwScaleSuffix,
string videoFormat,
+ bool swapOutputWandH,
int? videoWidth,
int? videoHeight,
int? requestedWidth,
@@ -2970,8 +2987,11 @@ namespace MediaBrowser.Controller.MediaEncoding
|| !videoHeight.HasValue
|| outHeight.Value != videoHeight.Value;
- var arg1 = isSizeFixed ? ("=w=" + outWidth.Value + ":h=" + outHeight.Value) : string.Empty;
- var arg2 = isFormatFixed ? ("format=" + videoFormat) : string.Empty;
+ var swpOutW = swapOutputWandH ? outHeight.Value : outWidth.Value;
+ var swpOutH = swapOutputWandH ? outWidth.Value : outHeight.Value;
+
+ var arg1 = isSizeFixed ? $"=w={swpOutW}:h={swpOutH}" : string.Empty;
+ var arg2 = isFormatFixed ? $"format={videoFormat}" : string.Empty;
if (isFormatFixed)
{
arg2 = (isSizeFixed ? ':' : '=') + arg2;
@@ -2981,7 +3001,8 @@ namespace MediaBrowser.Controller.MediaEncoding
{
return string.Format(
CultureInfo.InvariantCulture,
- "scale_{0}{1}{2}",
+ "{0}_{1}{2}{3}",
+ hwScalePrefix ?? "scale",
hwScaleSuffix,
arg1,
arg2);
@@ -3010,7 +3031,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
return string.Format(
CultureInfo.InvariantCulture,
- @"scale=-1:{1}:fast_bilinear,scale,crop,pad=max({0}\,iw):max({1}\,ih):(ow-iw)/2:(oh-ih)/2:black@0,crop={0}:{1}",
+ @"scale,scale=-1:{1}:fast_bilinear,crop,pad=max({0}\,iw):max({1}\,ih):(ow-iw)/2:(oh-ih)/2:black@0,crop={0}:{1}",
outWidth.Value,
outHeight.Value);
}
@@ -3221,14 +3242,18 @@ namespace MediaBrowser.Controller.MediaEncoding
doubleRateDeint ? "1" : "0");
}
- public static string GetHwDeinterlaceFilter(EncodingJobInfo state, EncodingOptions options, string hwDeintSuffix)
+ public string GetHwDeinterlaceFilter(EncodingJobInfo state, EncodingOptions options, string hwDeintSuffix)
{
var doubleRateDeint = options.DeinterlaceDoubleRate && (state.VideoStream?.AverageFrameRate ?? 60) <= 30;
if (hwDeintSuffix.Contains("cuda", StringComparison.OrdinalIgnoreCase))
{
+ var useBwdif = string.Equals(options.DeinterlaceMethod, "bwdif", StringComparison.OrdinalIgnoreCase)
+ && _mediaEncoder.SupportsFilter("bwdif_cuda");
+
return string.Format(
CultureInfo.InvariantCulture,
- "yadif_cuda={0}:-1:0",
+ "{0}_cuda={1}:-1:0",
+ useBwdif ? "bwdif" : "yadif",
doubleRateDeint ? "1" : "0");
}
@@ -3268,26 +3293,45 @@ namespace MediaBrowser.Controller.MediaEncoding
if (string.Equals(hwTonemapSuffix, "vaapi", StringComparison.OrdinalIgnoreCase))
{
- args = "procamp_vaapi=b={1}:c={2},tonemap_vaapi=format={0}:p=bt709:t=bt709:m=bt709:extra_hw_frames=32";
+ var doVaVppProcamp = false;
+ var procampParams = string.Empty;
+ if (options.VppTonemappingBrightness != 0
+ && options.VppTonemappingBrightness >= -100
+ && options.VppTonemappingBrightness <= 100)
+ {
+ procampParams += $"=b={options.VppTonemappingBrightness}";
+ doVaVppProcamp = true;
+ }
+
+ if (options.VppTonemappingContrast > 1
+ && options.VppTonemappingContrast <= 10)
+ {
+ procampParams += doVaVppProcamp ? ":" : "=";
+ procampParams += $"c={options.VppTonemappingContrast}";
+ doVaVppProcamp = true;
+ }
+
+ args = "{0}tonemap_vaapi=format={1}:p=bt709:t=bt709:m=bt709:extra_hw_frames=32";
return string.Format(
CultureInfo.InvariantCulture,
args,
- videoFormat ?? "nv12",
- options.VppTonemappingBrightness,
- options.VppTonemappingContrast);
+ doVaVppProcamp ? $"procamp_vaapi{procampParams}," : string.Empty,
+ videoFormat ?? "nv12");
}
else
{
args = "tonemap_{0}=format={1}:p=bt709:t=bt709:m=bt709:tonemap={2}:peak={3}:desat={4}";
- if (string.Equals(options.TonemappingMode, "max", StringComparison.OrdinalIgnoreCase)
- || string.Equals(options.TonemappingMode, "rgb", StringComparison.OrdinalIgnoreCase))
+ var useLegacyTonemapModes = _mediaEncoder.EncoderVersion >= _minFFmpegOclCuTonemapMode
+ && _legacyTonemapModes.Contains(options.TonemappingMode, StringComparison.OrdinalIgnoreCase);
+
+ var useAdvancedTonemapModes = _mediaEncoder.EncoderVersion >= _minFFmpegAdvancedTonemapMode
+ && _advancedTonemapModes.Contains(options.TonemappingMode, StringComparison.OrdinalIgnoreCase);
+
+ if (useLegacyTonemapModes || useAdvancedTonemapModes)
{
- if (_mediaEncoder.EncoderVersion >= _minFFmpegOclCuTonemapMode)
- {
- args += ":tonemap_mode={5}";
- }
+ args += ":tonemap_mode={5}";
}
if (options.TonemappingParam != 0)
@@ -3359,15 +3403,7 @@ namespace MediaBrowser.Controller.MediaEncoding
algorithm = "clip";
}
- tonemapArg = ":tonemapping=" + algorithm;
-
- if (string.Equals(mode, "max", StringComparison.OrdinalIgnoreCase)
- || string.Equals(mode, "rgb", StringComparison.OrdinalIgnoreCase))
- {
- tonemapArg += ":tonemapping_mode=" + mode;
- }
-
- tonemapArg += ":peak_detect=0:color_primaries=bt709:color_trc=bt709:colorspace=bt709";
+ tonemapArg = ":tonemapping=" + algorithm + ":peak_detect=0:color_primaries=bt709:color_trc=bt709:colorspace=bt709";
if (string.Equals(range, "tv", StringComparison.OrdinalIgnoreCase)
|| string.Equals(range, "pc", StringComparison.OrdinalIgnoreCase))
@@ -3384,6 +3420,18 @@ namespace MediaBrowser.Controller.MediaEncoding
tonemapArg);
}
+ public string GetVideoTransposeDirection(EncodingJobInfo state)
+ {
+ return (state.VideoStream?.Rotation ?? 0) switch
+ {
+ 90 => "cclock",
+ 180 => "reversal",
+ -90 => "clock",
+ -180 => "reversal",
+ _ => string.Empty
+ };
+ }
+
/// <summary>
/// Gets the parameter of software filter chain.
/// </summary>
@@ -3413,11 +3461,17 @@ namespace MediaBrowser.Controller.MediaEncoding
var doDeintHevc = state.DeInterlace("h265", true) || state.DeInterlace("hevc", true);
var doDeintH2645 = doDeintH264 || doDeintHevc;
var doToneMap = IsSwTonemapAvailable(state, options);
+ var requireDoviReshaping = doToneMap && state.VideoStream.VideoRangeType == VideoRangeType.DOVI;
var hasSubs = state.SubtitleStream is not null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
var hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream;
var hasGraphicalSubs = hasSubs && !state.SubtitleStream.IsTextSubtitleStream;
+ var rotation = state.VideoStream?.Rotation ?? 0;
+ var swapWAndH = Math.Abs(rotation) == 90;
+ var swpInW = swapWAndH ? inH : inW;
+ var swpInH = swapWAndH ? inW : inH;
+
/* Make main filters for video stream */
var mainFilters = new List<string>();
@@ -3432,7 +3486,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
var outFormat = isSwDecoder ? "yuv420p" : "nv12";
- var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, inW, inH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH);
+ var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, swpInW, swpInH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH);
if (isVaapiEncoder)
{
outFormat = "nv12";
@@ -3445,11 +3499,13 @@ namespace MediaBrowser.Controller.MediaEncoding
// sw scale
mainFilters.Add(swScaleFilter);
- // sw tonemap <= TODO: finish dovi tone mapping
-
+ // sw tonemap
if (doToneMap)
{
- var tonemapArgs = $"tonemapx=tonemap={options.TonemappingAlgorithm}:desat={options.TonemappingDesat}:peak={options.TonemappingPeak}:t=bt709:m=bt709:p=bt709:format={outFormat}";
+ // tonemapx requires yuv420p10 input for dovi reshaping, let ffmpeg convert the frame when necessary
+ var tonemapFormat = requireDoviReshaping ? "yuv420p" : outFormat;
+
+ var tonemapArgs = $"tonemapx=tonemap={options.TonemappingAlgorithm}:desat={options.TonemappingDesat}:peak={options.TonemappingPeak}:t=bt709:m=bt709:p=bt709:format={tonemapFormat}";
if (options.TonemappingParam != 0)
{
@@ -3481,7 +3537,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
else if (hasGraphicalSubs)
{
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
subFilters.Add(subPreProcFilters);
overlayFilters.Add("overlay=eof_action=pass:repeatlast=0");
}
@@ -3555,6 +3611,13 @@ namespace MediaBrowser.Controller.MediaEncoding
&& (string.Equals(state.SubtitleStream.Codec, "ass", StringComparison.OrdinalIgnoreCase)
|| string.Equals(state.SubtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase));
+ var rotation = state.VideoStream?.Rotation ?? 0;
+ var tranposeDir = rotation == 0 ? string.Empty : GetVideoTransposeDirection(state);
+ var doCuTranspose = !string.IsNullOrEmpty(tranposeDir) && _mediaEncoder.SupportsFilter("transpose_cuda");
+ var swapWAndH = Math.Abs(rotation) == 90 && (isSwDecoder || (isNvDecoder && doCuTranspose));
+ var swpInW = swapWAndH ? inH : inW;
+ var swpInH = swapWAndH ? inW : inH;
+
/* Make main filters for video stream */
var mainFilters = new List<string>();
@@ -3571,10 +3634,10 @@ namespace MediaBrowser.Controller.MediaEncoding
}
var outFormat = doCuTonemap ? "yuv420p10le" : "yuv420p";
- var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, inW, inH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH);
+ var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, swpInW, swpInH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH);
// sw scale
mainFilters.Add(swScaleFilter);
- mainFilters.Add("format=" + outFormat);
+ mainFilters.Add($"format={outFormat}");
// sw => hw
if (doCuTonemap)
@@ -3593,8 +3656,14 @@ namespace MediaBrowser.Controller.MediaEncoding
mainFilters.Add(deintFilter);
}
+ // hw transpose
+ if (doCuTranspose)
+ {
+ mainFilters.Add($"transpose_cuda=dir={tranposeDir}");
+ }
+
var outFormat = doCuTonemap ? string.Empty : "yuv420p";
- var hwScaleFilter = GetHwScaleFilter("cuda", outFormat, inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var hwScaleFilter = GetHwScaleFilter("scale", "cuda", outFormat, false, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
// hw scale
mainFilters.Add(hwScaleFilter);
}
@@ -3644,7 +3713,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
subFilters.Add(subPreProcFilters);
subFilters.Add("format=yuva420p");
}
@@ -3654,7 +3723,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var subFramerate = hasAssSubs ? Math.Min(framerate ?? 25, 60) : 10;
// alphasrc=s=1280x720:r=10:start=0,format=yuva420p,subtitles,hwupload
- var alphaSrcFilter = GetAlphaSrcFilter(state, inW, inH, reqW, reqH, reqMaxW, reqMaxH, subFramerate);
+ var alphaSrcFilter = GetAlphaSrcFilter(state, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH, subFramerate);
var subTextSubtitlesFilter = GetTextSubtitlesFilter(state, true, true);
subFilters.Add(alphaSrcFilter);
subFilters.Add("format=yuva420p");
@@ -3669,7 +3738,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
subFilters.Add(subPreProcFilters);
overlayFilters.Add("overlay=eof_action=pass:repeatlast=0");
}
@@ -3745,6 +3814,14 @@ namespace MediaBrowser.Controller.MediaEncoding
&& (string.Equals(state.SubtitleStream.Codec, "ass", StringComparison.OrdinalIgnoreCase)
|| string.Equals(state.SubtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase));
+ var rotation = state.VideoStream?.Rotation ?? 0;
+ var tranposeDir = rotation == 0 ? string.Empty : GetVideoTransposeDirection(state);
+ var doOclTranspose = !string.IsNullOrEmpty(tranposeDir)
+ && _mediaEncoder.SupportsFilterWithOption(FilterOptionType.TransposeOpenclReversal);
+ var swapWAndH = Math.Abs(rotation) == 90 && (isSwDecoder || (isD3d11vaDecoder && doOclTranspose));
+ var swpInW = swapWAndH ? inH : inW;
+ var swpInH = swapWAndH ? inW : inH;
+
/* Make main filters for video stream */
var mainFilters = new List<string>();
@@ -3761,19 +3838,19 @@ namespace MediaBrowser.Controller.MediaEncoding
}
var outFormat = doOclTonemap ? "yuv420p10le" : "yuv420p";
- var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, inW, inH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH);
+ var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, swpInW, swpInH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH);
// sw scale
mainFilters.Add(swScaleFilter);
- mainFilters.Add("format=" + outFormat);
+ mainFilters.Add($"format={outFormat}");
// keep video at memory except ocl tonemap,
// since the overhead caused by hwupload >>> using sw filter.
// sw => hw
if (doOclTonemap)
{
- mainFilters.Add("hwupload=derive_device=d3d11va:extra_hw_frames=16");
+ mainFilters.Add("hwupload=derive_device=d3d11va:extra_hw_frames=24");
mainFilters.Add("format=d3d11");
- mainFilters.Add("hwmap=derive_device=opencl");
+ mainFilters.Add("hwmap=derive_device=opencl:mode=read");
}
}
@@ -3781,12 +3858,18 @@ namespace MediaBrowser.Controller.MediaEncoding
{
// INPUT d3d11 surface(vram)
// map from d3d11va to opencl via d3d11-opencl interop.
- mainFilters.Add("hwmap=derive_device=opencl");
+ mainFilters.Add("hwmap=derive_device=opencl:mode=read");
// hw deint <= TODO: finsh the 'yadif_opencl' filter
+ // hw transpose
+ if (doOclTranspose)
+ {
+ mainFilters.Add($"transpose_opencl=dir={tranposeDir}");
+ }
+
var outFormat = doOclTonemap ? string.Empty : "nv12";
- var hwScaleFilter = GetHwScaleFilter("opencl", outFormat, inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var hwScaleFilter = GetHwScaleFilter("scale", "opencl", outFormat, false, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
// hw scale
mainFilters.Add(hwScaleFilter);
}
@@ -3831,7 +3914,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
// OUTPUT d3d11(nv12) surface(vram)
// reverse-mapping via d3d11-opencl interop.
- mainFilters.Add("hwmap=derive_device=d3d11va:reverse=1");
+ mainFilters.Add("hwmap=derive_device=d3d11va:mode=write:reverse=1");
mainFilters.Add("format=d3d11");
}
@@ -3844,7 +3927,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
subFilters.Add(subPreProcFilters);
subFilters.Add("format=yuva420p");
}
@@ -3854,7 +3937,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var subFramerate = hasAssSubs ? Math.Min(framerate ?? 25, 60) : 10;
// alphasrc=s=1280x720:r=10:start=0,format=yuva420p,subtitles,hwupload
- var alphaSrcFilter = GetAlphaSrcFilter(state, inW, inH, reqW, reqH, reqMaxW, reqMaxH, subFramerate);
+ var alphaSrcFilter = GetAlphaSrcFilter(state, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH, subFramerate);
var subTextSubtitlesFilter = GetTextSubtitlesFilter(state, true, true);
subFilters.Add(alphaSrcFilter);
subFilters.Add("format=yuva420p");
@@ -3863,7 +3946,7 @@ namespace MediaBrowser.Controller.MediaEncoding
subFilters.Add("hwupload=derive_device=opencl");
overlayFilters.Add("overlay_opencl=eof_action=pass:repeatlast=0");
- overlayFilters.Add("hwmap=derive_device=d3d11va:reverse=1");
+ overlayFilters.Add("hwmap=derive_device=d3d11va:mode=write:reverse=1");
overlayFilters.Add("format=d3d11");
}
}
@@ -3871,7 +3954,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
subFilters.Add(subPreProcFilters);
overlayFilters.Add("overlay=eof_action=pass:repeatlast=0");
}
@@ -3958,7 +4041,9 @@ namespace MediaBrowser.Controller.MediaEncoding
var doDeintH264 = state.DeInterlace("h264", true) || state.DeInterlace("avc", true);
var doDeintHevc = state.DeInterlace("h265", true) || state.DeInterlace("hevc", true);
var doDeintH2645 = doDeintH264 || doDeintHevc;
- var doOclTonemap = IsHwTonemapAvailable(state, options);
+ var doVppTonemap = IsIntelVppTonemapAvailable(state, options);
+ var doOclTonemap = !doVppTonemap && IsHwTonemapAvailable(state, options);
+ var doTonemap = doVppTonemap || doOclTonemap;
var hasSubs = state.SubtitleStream is not null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
var hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream;
@@ -3967,10 +4052,17 @@ namespace MediaBrowser.Controller.MediaEncoding
&& (string.Equals(state.SubtitleStream.Codec, "ass", StringComparison.OrdinalIgnoreCase)
|| string.Equals(state.SubtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase));
+ var rotation = state.VideoStream?.Rotation ?? 0;
+ var tranposeDir = rotation == 0 ? string.Empty : GetVideoTransposeDirection(state);
+ var doVppTranspose = !string.IsNullOrEmpty(tranposeDir);
+ var swapWAndH = Math.Abs(rotation) == 90 && (isSwDecoder || ((isD3d11vaDecoder || isQsvDecoder) && doVppTranspose));
+ var swpInW = swapWAndH ? inH : inW;
+ var swpInH = swapWAndH ? inW : inH;
+
/* Make main filters for video stream */
var mainFilters = new List<string>();
- mainFilters.Add(GetOverwriteColorPropertiesParam(state, doOclTonemap));
+ mainFilters.Add(GetOverwriteColorPropertiesParam(state, doTonemap));
if (isSwDecoder)
{
@@ -3983,10 +4075,10 @@ namespace MediaBrowser.Controller.MediaEncoding
}
var outFormat = doOclTonemap ? "yuv420p10le" : (hasGraphicalSubs ? "yuv420p" : "nv12");
- var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, inW, inH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH);
+ var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, swpInW, swpInH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH);
// sw scale
mainFilters.Add(swScaleFilter);
- mainFilters.Add("format=" + outFormat);
+ mainFilters.Add($"format={outFormat}");
// keep video at memory except ocl tonemap,
// since the overhead caused by hwupload >>> using sw filter.
@@ -3998,8 +4090,44 @@ namespace MediaBrowser.Controller.MediaEncoding
}
else if (isD3d11vaDecoder || isQsvDecoder)
{
- var outFormat = doOclTonemap ? string.Empty : "nv12";
- var hwScaleFilter = GetHwScaleFilter("qsv", outFormat, inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var doVppProcamp = false;
+ var procampParams = string.Empty;
+ if (doVppTonemap)
+ {
+ if (options.VppTonemappingBrightness != 0
+ && options.VppTonemappingBrightness >= -100
+ && options.VppTonemappingBrightness <= 100)
+ {
+ procampParams += $":brightness={options.VppTonemappingBrightness}";
+ doVppProcamp = true;
+ }
+
+ if (options.VppTonemappingContrast > 1
+ && options.VppTonemappingContrast <= 10)
+ {
+ procampParams += $":contrast={options.VppTonemappingContrast}";
+ doVppProcamp = true;
+ }
+
+ procampParams += doVppProcamp ? ":procamp=1:async_depth=2" : string.Empty;
+ }
+
+ var outFormat = doOclTonemap ? (doVppTranspose ? "p010" : string.Empty) : "nv12";
+ outFormat = (doVppTonemap && doVppProcamp) ? "p010" : outFormat;
+
+ var swapOutputWandH = doVppTranspose && swapWAndH;
+ var hwScalePrefix = (doVppTranspose || doVppTonemap) ? "vpp" : "scale";
+ var hwScaleFilter = GetHwScaleFilter(hwScalePrefix, "qsv", outFormat, swapOutputWandH, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
+
+ if (!string.IsNullOrEmpty(hwScaleFilter) && doVppTranspose)
+ {
+ hwScaleFilter += $":transpose={tranposeDir}";
+ }
+
+ if (!string.IsNullOrEmpty(hwScaleFilter) && doVppTonemap)
+ {
+ hwScaleFilter += doVppProcamp ? procampParams : ":tonemap=1";
+ }
if (isD3d11vaDecoder)
{
@@ -4018,14 +4146,26 @@ namespace MediaBrowser.Controller.MediaEncoding
mainFilters.Add(deintFilter);
}
- // hw scale
+ // hw transpose & scale & tonemap(w/o procamp)
mainFilters.Add(hwScaleFilter);
+
+ // hw tonemap(w/ procamp)
+ if (doVppTonemap && doVppProcamp)
+ {
+ mainFilters.Add("vpp_qsv=tonemap=1:format=nv12:async_depth=2");
+ }
+
+ // force bt709 just in case vpp tonemap is not triggered or using MSDK instead of VPL.
+ if (doVppTonemap)
+ {
+ mainFilters.Add(GetOverwriteColorPropertiesParam(state, false));
+ }
}
if (doOclTonemap && isHwDecoder)
{
// map from qsv to opencl via qsv(d3d11)-opencl interop.
- mainFilters.Add("hwmap=derive_device=opencl");
+ mainFilters.Add("hwmap=derive_device=opencl:mode=read");
}
// hw tonemap
@@ -4069,7 +4209,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
// OUTPUT qsv(nv12) surface(vram)
// reverse-mapping via qsv(d3d11)-opencl interop.
- mainFilters.Add("hwmap=derive_device=qsv:reverse=1");
+ mainFilters.Add("hwmap=derive_device=qsv:mode=write:reverse=1");
mainFilters.Add("format=qsv");
}
@@ -4083,7 +4223,7 @@ namespace MediaBrowser.Controller.MediaEncoding
if (hasGraphicalSubs)
{
// overlay_qsv can handle overlay scaling, setup a smaller height to reduce transfer overhead
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, 1080);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, 1080);
subFilters.Add(subPreProcFilters);
subFilters.Add("format=bgra");
}
@@ -4093,7 +4233,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var subFramerate = hasAssSubs ? Math.Min(framerate ?? 25, 60) : 10;
// alphasrc=s=1280x720:r=10:start=0,format=bgra,subtitles,hwupload
- var alphaSrcFilter = GetAlphaSrcFilter(state, inW, inH, reqW, reqH, reqMaxW, 1080, subFramerate);
+ var alphaSrcFilter = GetAlphaSrcFilter(state, swpInW, swpInH, reqW, reqH, reqMaxW, 1080, subFramerate);
var subTextSubtitlesFilter = GetTextSubtitlesFilter(state, true, true);
subFilters.Add(alphaSrcFilter);
subFilters.Add("format=bgra");
@@ -4104,9 +4244,9 @@ namespace MediaBrowser.Controller.MediaEncoding
// default to 64 otherwise it will fail on certain iGPU.
subFilters.Add("hwupload=derive_device=qsv:extra_hw_frames=64");
- var (overlayW, overlayH) = GetFixedOutputSize(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var (overlayW, overlayH) = GetFixedOutputSize(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
var overlaySize = (overlayW.HasValue && overlayH.HasValue)
- ? (":w=" + overlayW.Value + ":h=" + overlayH.Value)
+ ? $":w={overlayW.Value}:h={overlayH.Value}"
: string.Empty;
var overlayQsvFilter = string.Format(
CultureInfo.InvariantCulture,
@@ -4119,7 +4259,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
subFilters.Add(subPreProcFilters);
overlayFilters.Add("overlay=eof_action=pass:repeatlast=0");
}
@@ -4152,7 +4292,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var doDeintH264 = state.DeInterlace("h264", true) || state.DeInterlace("avc", true);
var doDeintHevc = state.DeInterlace("h265", true) || state.DeInterlace("hevc", true);
- var doVaVppTonemap = IsVaapiVppTonemapAvailable(state, options);
+ var doVaVppTonemap = IsIntelVppTonemapAvailable(state, options);
var doOclTonemap = !doVaVppTonemap && IsHwTonemapAvailable(state, options);
var doTonemap = doVaVppTonemap || doOclTonemap;
var doDeintH2645 = doDeintH264 || doDeintHevc;
@@ -4164,6 +4304,13 @@ namespace MediaBrowser.Controller.MediaEncoding
&& (string.Equals(state.SubtitleStream.Codec, "ass", StringComparison.OrdinalIgnoreCase)
|| string.Equals(state.SubtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase));
+ var rotation = state.VideoStream?.Rotation ?? 0;
+ var tranposeDir = rotation == 0 ? string.Empty : GetVideoTransposeDirection(state);
+ var doVppTranspose = !string.IsNullOrEmpty(tranposeDir);
+ var swapWAndH = Math.Abs(rotation) == 90 && (isSwDecoder || ((isVaapiDecoder || isQsvDecoder) && doVppTranspose));
+ var swpInW = swapWAndH ? inH : inW;
+ var swpInH = swapWAndH ? inW : inH;
+
/* Make main filters for video stream */
var mainFilters = new List<string>();
@@ -4180,10 +4327,10 @@ namespace MediaBrowser.Controller.MediaEncoding
}
var outFormat = doOclTonemap ? "yuv420p10le" : (hasGraphicalSubs ? "yuv420p" : "nv12");
- var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, inW, inH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH);
+ var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, swpInW, swpInH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH);
// sw scale
mainFilters.Add(swScaleFilter);
- mainFilters.Add("format=" + outFormat);
+ mainFilters.Add($"format={outFormat}");
// keep video at memory except ocl tonemap,
// since the overhead caused by hwupload >>> using sw filter.
@@ -4195,24 +4342,39 @@ namespace MediaBrowser.Controller.MediaEncoding
}
else if (isVaapiDecoder || isQsvDecoder)
{
+ var hwFilterSuffix = isVaapiDecoder ? "vaapi" : "qsv";
+
// INPUT vaapi/qsv surface(vram)
// hw deint
if (doDeintH2645)
{
- var deintFilter = GetHwDeinterlaceFilter(state, options, isVaapiDecoder ? "vaapi" : "qsv");
+ var deintFilter = GetHwDeinterlaceFilter(state, options, hwFilterSuffix);
mainFilters.Add(deintFilter);
}
- var outFormat = doTonemap ? string.Empty : "nv12";
- var hwScaleFilter = GetHwScaleFilter(isVaapiDecoder ? "vaapi" : "qsv", outFormat, inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ // hw transpose(vaapi vpp)
+ if (isVaapiDecoder && doVppTranspose)
+ {
+ mainFilters.Add($"transpose_vaapi=dir={tranposeDir}");
+ }
- // allocate extra pool sizes for vaapi vpp
+ var outFormat = doOclTonemap ? ((isQsvDecoder && doVppTranspose) ? "p010" : string.Empty) : "nv12";
+ var swapOutputWandH = isQsvDecoder && doVppTranspose && swapWAndH;
+ var hwScalePrefix = (isQsvDecoder && doVppTranspose) ? "vpp" : "scale";
+ var hwScaleFilter = GetHwScaleFilter(hwScalePrefix, hwFilterSuffix, outFormat, swapOutputWandH, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
+
+ if (!string.IsNullOrEmpty(hwScaleFilter) && isQsvDecoder && doVppTranspose)
+ {
+ hwScaleFilter += $":transpose={tranposeDir}";
+ }
+
+ // allocate extra pool sizes for vaapi vpp scale
if (!string.IsNullOrEmpty(hwScaleFilter) && isVaapiDecoder)
{
hwScaleFilter += ":extra_hw_frames=24";
}
- // hw scale
+ // hw transpose(qsv vpp) & scale
mainFilters.Add(hwScaleFilter);
}
@@ -4240,7 +4402,7 @@ namespace MediaBrowser.Controller.MediaEncoding
if (doOclTonemap && isHwDecoder)
{
// map from qsv to opencl via qsv(vaapi)-opencl interop.
- mainFilters.Add("hwmap=derive_device=opencl");
+ mainFilters.Add("hwmap=derive_device=opencl:mode=read");
}
// ocl tonemap
@@ -4287,7 +4449,7 @@ namespace MediaBrowser.Controller.MediaEncoding
// OUTPUT qsv(nv12) surface(vram)
// reverse-mapping via qsv(vaapi)-opencl interop.
// add extra pool size to avoid the 'cannot allocate memory' error on hevc_qsv.
- mainFilters.Add("hwmap=derive_device=qsv:reverse=1:extra_hw_frames=16");
+ mainFilters.Add("hwmap=derive_device=qsv:mode=write:reverse=1:extra_hw_frames=16");
mainFilters.Add("format=qsv");
}
else if (isVaapiDecoder)
@@ -4307,7 +4469,7 @@ namespace MediaBrowser.Controller.MediaEncoding
if (hasGraphicalSubs)
{
// overlay_qsv can handle overlay scaling, setup a smaller height to reduce transfer overhead
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, 1080);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, 1080);
subFilters.Add(subPreProcFilters);
subFilters.Add("format=bgra");
}
@@ -4316,7 +4478,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var framerate = state.VideoStream?.RealFrameRate;
var subFramerate = hasAssSubs ? Math.Min(framerate ?? 25, 60) : 10;
- var alphaSrcFilter = GetAlphaSrcFilter(state, inW, inH, reqW, reqH, reqMaxW, 1080, subFramerate);
+ var alphaSrcFilter = GetAlphaSrcFilter(state, swpInW, swpInH, reqW, reqH, reqMaxW, 1080, subFramerate);
var subTextSubtitlesFilter = GetTextSubtitlesFilter(state, true, true);
subFilters.Add(alphaSrcFilter);
subFilters.Add("format=bgra");
@@ -4327,9 +4489,9 @@ namespace MediaBrowser.Controller.MediaEncoding
// default to 64 otherwise it will fail on certain iGPU.
subFilters.Add("hwupload=derive_device=qsv:extra_hw_frames=64");
- var (overlayW, overlayH) = GetFixedOutputSize(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var (overlayW, overlayH) = GetFixedOutputSize(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
var overlaySize = (overlayW.HasValue && overlayH.HasValue)
- ? (":w=" + overlayW.Value + ":h=" + overlayH.Value)
+ ? $":w={overlayW.Value}:h={overlayH.Value}"
: string.Empty;
var overlayQsvFilter = string.Format(
CultureInfo.InvariantCulture,
@@ -4342,7 +4504,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
subFilters.Add(subPreProcFilters);
overlayFilters.Add("overlay=eof_action=pass:repeatlast=0");
}
@@ -4441,7 +4603,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var doDeintH264 = state.DeInterlace("h264", true) || state.DeInterlace("avc", true);
var doDeintHevc = state.DeInterlace("h265", true) || state.DeInterlace("hevc", true);
- var doVaVppTonemap = isVaapiDecoder && IsVaapiVppTonemapAvailable(state, options);
+ var doVaVppTonemap = isVaapiDecoder && IsIntelVppTonemapAvailable(state, options);
var doOclTonemap = !doVaVppTonemap && IsHwTonemapAvailable(state, options);
var doTonemap = doVaVppTonemap || doOclTonemap;
var doDeintH2645 = doDeintH264 || doDeintHevc;
@@ -4453,6 +4615,13 @@ namespace MediaBrowser.Controller.MediaEncoding
&& (string.Equals(state.SubtitleStream.Codec, "ass", StringComparison.OrdinalIgnoreCase)
|| string.Equals(state.SubtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase));
+ var rotation = state.VideoStream?.Rotation ?? 0;
+ var tranposeDir = rotation == 0 ? string.Empty : GetVideoTransposeDirection(state);
+ var doVaVppTranspose = !string.IsNullOrEmpty(tranposeDir);
+ var swapWAndH = Math.Abs(rotation) == 90 && (isSwDecoder || (isVaapiDecoder && doVaVppTranspose));
+ var swpInW = swapWAndH ? inH : inW;
+ var swpInH = swapWAndH ? inW : inH;
+
/* Make main filters for video stream */
var mainFilters = new List<string>();
@@ -4469,10 +4638,10 @@ namespace MediaBrowser.Controller.MediaEncoding
}
var outFormat = doOclTonemap ? "yuv420p10le" : "nv12";
- var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, inW, inH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH);
+ var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, swpInW, swpInH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH);
// sw scale
mainFilters.Add(swScaleFilter);
- mainFilters.Add("format=" + outFormat);
+ mainFilters.Add($"format={outFormat}");
// keep video at memory except ocl tonemap,
// since the overhead caused by hwupload >>> using sw filter.
@@ -4492,8 +4661,14 @@ namespace MediaBrowser.Controller.MediaEncoding
mainFilters.Add(deintFilter);
}
+ // hw transpose
+ if (doVaVppTranspose)
+ {
+ mainFilters.Add($"transpose_vaapi=dir={tranposeDir}");
+ }
+
var outFormat = doTonemap ? string.Empty : "nv12";
- var hwScaleFilter = GetHwScaleFilter("vaapi", outFormat, inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var hwScaleFilter = GetHwScaleFilter("scale", "vaapi", outFormat, false, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
// allocate extra pool sizes for vaapi vpp
if (!string.IsNullOrEmpty(hwScaleFilter))
@@ -4515,7 +4690,7 @@ namespace MediaBrowser.Controller.MediaEncoding
if (doOclTonemap && isVaapiDecoder)
{
// map from vaapi to opencl via vaapi-opencl interop(Intel only).
- mainFilters.Add("hwmap=derive_device=opencl");
+ mainFilters.Add("hwmap=derive_device=opencl:mode=read");
}
// ocl tonemap
@@ -4529,7 +4704,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
// OUTPUT vaapi(nv12) surface(vram)
// reverse-mapping via vaapi-opencl interop.
- mainFilters.Add("hwmap=derive_device=vaapi:reverse=1");
+ mainFilters.Add("hwmap=derive_device=vaapi:mode=write:reverse=1");
mainFilters.Add("format=vaapi");
}
@@ -4580,7 +4755,7 @@ namespace MediaBrowser.Controller.MediaEncoding
if (hasGraphicalSubs)
{
// overlay_vaapi can handle overlay scaling, setup a smaller height to reduce transfer overhead
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, 1080);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, 1080);
subFilters.Add(subPreProcFilters);
subFilters.Add("format=bgra");
}
@@ -4589,7 +4764,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var framerate = state.VideoStream?.RealFrameRate;
var subFramerate = hasAssSubs ? Math.Min(framerate ?? 25, 60) : 10;
- var alphaSrcFilter = GetAlphaSrcFilter(state, inW, inH, reqW, reqH, reqMaxW, 1080, subFramerate);
+ var alphaSrcFilter = GetAlphaSrcFilter(state, swpInW, swpInH, reqW, reqH, reqMaxW, 1080, subFramerate);
var subTextSubtitlesFilter = GetTextSubtitlesFilter(state, true, true);
subFilters.Add(alphaSrcFilter);
subFilters.Add("format=bgra");
@@ -4598,9 +4773,9 @@ namespace MediaBrowser.Controller.MediaEncoding
subFilters.Add("hwupload=derive_device=vaapi");
- var (overlayW, overlayH) = GetFixedOutputSize(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var (overlayW, overlayH) = GetFixedOutputSize(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
var overlaySize = (overlayW.HasValue && overlayH.HasValue)
- ? (":w=" + overlayW.Value + ":h=" + overlayH.Value)
+ ? $":w={overlayW.Value}:h={overlayH.Value}"
: string.Empty;
var overlayVaapiFilter = string.Format(
CultureInfo.InvariantCulture,
@@ -4613,7 +4788,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
subFilters.Add(subPreProcFilters);
overlayFilters.Add("overlay=eof_action=pass:repeatlast=0");
@@ -4658,6 +4833,13 @@ namespace MediaBrowser.Controller.MediaEncoding
&& (string.Equals(state.SubtitleStream.Codec, "ass", StringComparison.OrdinalIgnoreCase)
|| string.Equals(state.SubtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase));
+ var rotation = state.VideoStream?.Rotation ?? 0;
+ var tranposeDir = rotation == 0 ? string.Empty : GetVideoTransposeDirection(state);
+ var doVkTranspose = isVaapiDecoder && !string.IsNullOrEmpty(tranposeDir);
+ var swapWAndH = Math.Abs(rotation) == 90 && (isSwDecoder || (isVaapiDecoder && doVkTranspose));
+ var swpInW = swapWAndH ? inH : inW;
+ var swpInH = swapWAndH ? inW : inH;
+
/* Make main filters for video stream */
var mainFilters = new List<string>();
@@ -4682,7 +4864,7 @@ namespace MediaBrowser.Controller.MediaEncoding
else
{
// sw scale
- var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, inW, inH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH);
+ var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, swpInW, swpInH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH);
mainFilters.Add(swScaleFilter);
mainFilters.Add("format=nv12");
}
@@ -4690,11 +4872,37 @@ namespace MediaBrowser.Controller.MediaEncoding
else if (isVaapiDecoder)
{
// INPUT vaapi surface(vram)
- if (doVkTonemap || hasSubs)
+ if (doVkTranspose || doVkTonemap || hasSubs)
{
// map from vaapi to vulkan/drm via interop (Polaris/gfx8+).
- mainFilters.Add("hwmap=derive_device=vulkan");
- mainFilters.Add("format=vulkan");
+ if (_mediaEncoder.EncoderVersion >= _minFFmpegAlteredVaVkInterop)
+ {
+ if (doVkTranspose || !_mediaEncoder.IsVaapiDeviceSupportVulkanDrmModifier)
+ {
+ // disable the indirect va-drm-vk mapping since it's no longer reliable.
+ mainFilters.Add("hwmap=derive_device=drm");
+ mainFilters.Add("format=drm_prime");
+ mainFilters.Add("hwmap=derive_device=vulkan");
+ mainFilters.Add("format=vulkan");
+
+ // workaround for libplacebo using the imported vulkan frame on gfx8.
+ if (!_mediaEncoder.IsVaapiDeviceSupportVulkanDrmModifier)
+ {
+ mainFilters.Add("scale_vulkan");
+ }
+ }
+ else if (doVkTonemap || hasSubs)
+ {
+ // non ad-hoc libplacebo also accepts drm_prime direct input.
+ mainFilters.Add("hwmap=derive_device=drm");
+ mainFilters.Add("format=drm_prime");
+ }
+ }
+ else // legacy va-vk mapping that works only in jellyfin-ffmpeg6
+ {
+ mainFilters.Add("hwmap=derive_device=vulkan");
+ mainFilters.Add("format=vulkan");
+ }
}
else
{
@@ -4706,16 +4914,30 @@ namespace MediaBrowser.Controller.MediaEncoding
}
// hw scale
- var hwScaleFilter = GetHwScaleFilter("vaapi", "nv12", inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var hwScaleFilter = GetHwScaleFilter("scale", "vaapi", "nv12", false, inW, inH, reqW, reqH, reqMaxW, reqMaxH);
mainFilters.Add(hwScaleFilter);
}
}
+ // vk transpose
+ if (doVkTranspose)
+ {
+ if (string.Equals(tranposeDir, "reversal", StringComparison.OrdinalIgnoreCase))
+ {
+ mainFilters.Add("flip_vulkan");
+ }
+ else
+ {
+ mainFilters.Add($"transpose_vulkan=dir={tranposeDir}");
+ }
+ }
+
// vk libplacebo
if (doVkTonemap || hasSubs)
{
- var libplaceboFilter = GetLibplaceboFilter(options, "bgra", doVkTonemap, inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var libplaceboFilter = GetLibplaceboFilter(options, "bgra", doVkTonemap, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
mainFilters.Add(libplaceboFilter);
+ mainFilters.Add("format=vulkan");
}
if (doVkTonemap && !hasSubs)
@@ -4758,7 +4980,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
subFilters.Add(subPreProcFilters);
subFilters.Add("format=bgra");
}
@@ -4767,7 +4989,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var framerate = state.VideoStream?.RealFrameRate;
var subFramerate = hasAssSubs ? Math.Min(framerate ?? 25, 60) : 10;
- var alphaSrcFilter = GetAlphaSrcFilter(state, inW, inH, reqW, reqH, reqMaxW, reqMaxH, subFramerate);
+ var alphaSrcFilter = GetAlphaSrcFilter(state, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH, subFramerate);
var subTextSubtitlesFilter = GetTextSubtitlesFilter(state, true, true);
subFilters.Add(alphaSrcFilter);
subFilters.Add("format=bgra");
@@ -4839,6 +5061,11 @@ namespace MediaBrowser.Controller.MediaEncoding
var hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream;
var hasGraphicalSubs = hasSubs && !state.SubtitleStream.IsTextSubtitleStream;
+ var rotation = state.VideoStream?.Rotation ?? 0;
+ var swapWAndH = Math.Abs(rotation) == 90 && isSwDecoder;
+ var swpInW = swapWAndH ? inH : inW;
+ var swpInH = swapWAndH ? inW : inH;
+
/* Make main filters for video stream */
var mainFilters = new List<string>();
@@ -4856,7 +5083,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
outFormat = doOclTonemap ? "yuv420p10le" : "nv12";
- var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, inW, inH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH);
+ var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, swpInW, swpInH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH);
// sw scale
mainFilters.Add(swScaleFilter);
mainFilters.Add("format=" + outFormat);
@@ -4880,7 +5107,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
outFormat = doOclTonemap ? string.Empty : "nv12";
- var hwScaleFilter = GetHwScaleFilter("vaapi", outFormat, inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var hwScaleFilter = GetHwScaleFilter("scale", "vaapi", outFormat, false, inW, inH, reqW, reqH, reqMaxW, reqMaxH);
// allocate extra pool sizes for vaapi vpp
if (!string.IsNullOrEmpty(hwScaleFilter))
@@ -4976,7 +5203,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
subFilters.Add(subPreProcFilters);
overlayFilters.Add("overlay=eof_action=pass:repeatlast=0");
@@ -5007,13 +5234,15 @@ namespace MediaBrowser.Controller.MediaEncoding
return (null, null, null);
}
+ // ReSharper disable once InconsistentNaming
var isMacOS = OperatingSystem.IsMacOS();
var vidDecoder = GetHardwareVideoDecoder(state, options) ?? string.Empty;
+ var isVtDecoder = vidDecoder.Contains("videotoolbox", StringComparison.OrdinalIgnoreCase);
var isVtEncoder = vidEncoder.Contains("videotoolbox", StringComparison.OrdinalIgnoreCase);
var isVtFullSupported = isMacOS && IsVideoToolboxFullSupported();
// legacy videotoolbox pipeline (disable hw filters)
- if (!isVtEncoder
+ if (!(isVtEncoder || isVtDecoder)
|| !isVtFullSupported
|| !_mediaEncoder.SupportsFilter("alphasrc"))
{
@@ -5030,6 +5259,9 @@ namespace MediaBrowser.Controller.MediaEncoding
string vidDecoder,
string vidEncoder)
{
+ var isVtEncoder = vidEncoder.Contains("videotoolbox", StringComparison.OrdinalIgnoreCase);
+ var isVtDecoder = vidDecoder.Contains("videotoolbox", StringComparison.OrdinalIgnoreCase);
+
var inW = state.VideoStream?.Width;
var inH = state.VideoStream?.Height;
var reqW = state.BaseRequest.Width;
@@ -5038,9 +5270,6 @@ namespace MediaBrowser.Controller.MediaEncoding
var reqMaxH = state.BaseRequest.MaxHeight;
var threeDFormat = state.MediaSource.Video3DFormat;
- var isVtEncoder = vidEncoder.Contains("videotoolbox", StringComparison.OrdinalIgnoreCase);
- var isVtDecoder = vidDecoder.Contains("videotoolbox", StringComparison.OrdinalIgnoreCase);
-
var doDeintH264 = state.DeInterlace("h264", true) || state.DeInterlace("avc", true);
var doDeintHevc = state.DeInterlace("h265", true) || state.DeInterlace("hevc", true);
var doDeintH2645 = doDeintH264 || doDeintHevc;
@@ -5048,6 +5277,13 @@ namespace MediaBrowser.Controller.MediaEncoding
var doMetalTonemap = !doVtTonemap && IsHwTonemapAvailable(state, options);
var usingHwSurface = isVtDecoder && (_mediaEncoder.EncoderVersion >= _minFFmpegWorkingVtHwSurface);
+ var rotation = state.VideoStream?.Rotation ?? 0;
+ var tranposeDir = rotation == 0 ? string.Empty : GetVideoTransposeDirection(state);
+ var doVtTranspose = !string.IsNullOrEmpty(tranposeDir) && _mediaEncoder.SupportsFilter("transpose_vt");
+ var swapWAndH = Math.Abs(rotation) == 90 && doVtTranspose;
+ var swpInW = swapWAndH ? inH : inW;
+ var swpInH = swapWAndH ? inW : inH;
+
var scaleFormat = string.Empty;
// Use P010 for Metal tone mapping, otherwise force an 8bit output.
if (!string.Equals(state.VideoStream.PixelFormat, "yuv420p", StringComparison.OrdinalIgnoreCase))
@@ -5065,7 +5301,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
}
- var hwScaleFilter = GetHwScaleFilter("vt", scaleFormat, inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var hwScaleFilter = GetHwScaleFilter("scale", "vt", scaleFormat, false, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
var hasSubs = state.SubtitleStream is not null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
var hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream;
@@ -5074,12 +5310,6 @@ namespace MediaBrowser.Controller.MediaEncoding
&& (string.Equals(state.SubtitleStream.Codec, "ass", StringComparison.OrdinalIgnoreCase)
|| string.Equals(state.SubtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase));
- if (!isVtEncoder)
- {
- // should not happen.
- return (null, null, null);
- }
-
/* Make main filters for video stream */
var mainFilters = new List<string>();
@@ -5090,6 +5320,12 @@ namespace MediaBrowser.Controller.MediaEncoding
mainFilters.Add(deintFilter);
}
+ // hw transpose
+ if (doVtTranspose)
+ {
+ mainFilters.Add($"transpose_vt=dir={tranposeDir}");
+ }
+
if (doVtTonemap)
{
const string VtTonemapArgs = "color_matrix=bt709:color_primaries=bt709:color_transfer=bt709";
@@ -5118,7 +5354,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
subFilters.Add(subPreProcFilters);
subFilters.Add("format=bgra");
}
@@ -5127,7 +5363,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var framerate = state.VideoStream?.RealFrameRate;
var subFramerate = hasAssSubs ? Math.Min(framerate ?? 25, 60) : 10;
- var alphaSrcFilter = GetAlphaSrcFilter(state, inW, inH, reqW, reqH, reqMaxW, reqMaxH, subFramerate);
+ var alphaSrcFilter = GetAlphaSrcFilter(state, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH, subFramerate);
var subTextSubtitlesFilter = GetTextSubtitlesFilter(state, true, true);
subFilters.Add(alphaSrcFilter);
subFilters.Add("format=bgra");
@@ -5140,6 +5376,12 @@ namespace MediaBrowser.Controller.MediaEncoding
if (usingHwSurface)
{
+ if (!isVtEncoder)
+ {
+ mainFilters.Add("hwdownload");
+ mainFilters.Add("format=nv12");
+ }
+
return (mainFilters, subFilters, overlayFilters);
}
@@ -5153,6 +5395,12 @@ namespace MediaBrowser.Controller.MediaEncoding
// this will pass-through automatically if in/out format matches.
mainFilters.Insert(0, "hwupload");
mainFilters.Insert(0, "format=nv12|p010le|videotoolbox_vld");
+
+ if (!isVtEncoder)
+ {
+ mainFilters.Add("hwdownload");
+ mainFilters.Add("format=nv12");
+ }
}
return (mainFilters, subFilters, overlayFilters);
@@ -5229,6 +5477,13 @@ namespace MediaBrowser.Controller.MediaEncoding
&& (string.Equals(state.SubtitleStream.Codec, "ass", StringComparison.OrdinalIgnoreCase)
|| string.Equals(state.SubtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase));
+ var rotation = state.VideoStream?.Rotation ?? 0;
+ var tranposeDir = rotation == 0 ? string.Empty : GetVideoTransposeDirection(state);
+ var doRkVppTranspose = !string.IsNullOrEmpty(tranposeDir);
+ var swapWAndH = Math.Abs(rotation) == 90 && (isSwDecoder || (isRkmppDecoder && doRkVppTranspose));
+ var swpInW = swapWAndH ? inH : inW;
+ var swpInH = swapWAndH ? inW : inH;
+
/* Make main filters for video stream */
var mainFilters = new List<string>();
@@ -5245,7 +5500,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
var outFormat = doOclTonemap ? "yuv420p10le" : (hasGraphicalSubs ? "yuv420p" : "nv12");
- var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, inW, inH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH);
+ var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, swpInW, swpInH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH);
if (!string.IsNullOrEmpty(swScaleFilter))
{
swScaleFilter += ":flags=fast_bilinear";
@@ -5253,7 +5508,7 @@ namespace MediaBrowser.Controller.MediaEncoding
// sw scale
mainFilters.Add(swScaleFilter);
- mainFilters.Add("format=" + outFormat);
+ mainFilters.Add($"format={outFormat}");
// keep video at memory except ocl tonemap,
// since the overhead caused by hwupload >>> using sw filter.
@@ -5268,21 +5523,29 @@ namespace MediaBrowser.Controller.MediaEncoding
// INPUT rkmpp/drm surface(gem/dma-heap)
var isFullAfbcPipeline = isDrmInDrmOut && !doOclTonemap;
+ var swapOutputWandH = doRkVppTranspose && swapWAndH;
var outFormat = doOclTonemap ? "p010" : "nv12";
- var hwScaleFilter = GetHwScaleFilter("rkrga", outFormat, inW, inH, reqW, reqH, reqMaxW, reqMaxH);
- var hwScaleFilter2 = GetHwScaleFilter("rkrga", string.Empty, inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var hwScalePrefix = doRkVppTranspose ? "vpp" : "scale";
+ var hwScaleFilter = GetHwScaleFilter(hwScalePrefix, "rkrga", outFormat, swapOutputWandH, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
+ var hwScaleFilter2 = GetHwScaleFilter(hwScalePrefix, "rkrga", string.Empty, swapOutputWandH, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
if (!hasSubs
+ || doRkVppTranspose
|| !isFullAfbcPipeline
|| !string.IsNullOrEmpty(hwScaleFilter2))
{
+ if (!string.IsNullOrEmpty(hwScaleFilter) && doRkVppTranspose)
+ {
+ hwScaleFilter += $":transpose={tranposeDir}";
+ }
+
// try enabling AFBC to save DDR bandwidth
if (!string.IsNullOrEmpty(hwScaleFilter) && isFullAfbcPipeline)
{
hwScaleFilter += ":afbc=1";
}
- // hw scale
+ // hw transpose & scale
mainFilters.Add(hwScaleFilter);
}
}
@@ -5353,7 +5616,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
subFilters.Add(subPreProcFilters);
subFilters.Add("format=bgra");
}
@@ -5363,7 +5626,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var subFramerate = hasAssSubs ? Math.Min(framerate ?? 25, 60) : 10;
// alphasrc=s=1280x720:r=10:start=0,format=bgra,subtitles,hwupload
- var alphaSrcFilter = GetAlphaSrcFilter(state, inW, inH, reqW, reqH, reqMaxW, reqMaxH, subFramerate);
+ var alphaSrcFilter = GetAlphaSrcFilter(state, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH, subFramerate);
var subTextSubtitlesFilter = GetTextSubtitlesFilter(state, true, true);
subFilters.Add(alphaSrcFilter);
subFilters.Add("format=bgra");
@@ -5380,7 +5643,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
subFilters.Add(subPreProcFilters);
overlayFilters.Add("overlay=eof_action=pass:repeatlast=0");
}
@@ -5795,6 +6058,11 @@ namespace MediaBrowser.Controller.MediaEncoding
// Disable the extra internal copy in nvdec. We already handle it in filter chain.
var nvdecNoInternalCopy = ffmpegVersion >= _minFFmpegHwaUnsafeOutput;
+ // Strip the display rotation side data from the transposed fmp4 output stream.
+ var stripRotationData = (state.VideoStream?.Rotation ?? 0) != 0
+ && ffmpegVersion >= _minFFmpegDisplayRotationOption;
+ var stripRotationDataArgs = stripRotationData ? " -display_rotation 0" : string.Empty;
+
if (bitDepth == 10 && isCodecAvailable)
{
if (string.Equals(videoCodec, "hevc", StringComparison.OrdinalIgnoreCase)
@@ -5819,13 +6087,13 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (isVaapiSupported && isCodecAvailable)
{
- return " -hwaccel vaapi" + (outputHwSurface ? " -hwaccel_output_format vaapi" : string.Empty)
+ return " -hwaccel vaapi" + (outputHwSurface ? " -hwaccel_output_format vaapi -noautorotate" + stripRotationDataArgs : string.Empty)
+ (profileMismatch ? " -hwaccel_flags +allow_profile_mismatch" : string.Empty) + (isAv1 ? " -c:v av1" : string.Empty);
}
if (isD3d11Supported && isCodecAvailable)
{
- return " -hwaccel d3d11va" + (outputHwSurface ? " -hwaccel_output_format d3d11" : string.Empty)
+ return " -hwaccel d3d11va" + (outputHwSurface ? " -hwaccel_output_format d3d11 -noautorotate" + stripRotationDataArgs : string.Empty)
+ (profileMismatch ? " -hwaccel_flags +allow_profile_mismatch" : string.Empty) + " -threads 2" + (isAv1 ? " -c:v av1" : string.Empty);
}
}
@@ -5833,7 +6101,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (isQsvSupported && isCodecAvailable)
{
- return " -hwaccel qsv" + (outputHwSurface ? " -hwaccel_output_format qsv" : string.Empty);
+ return " -hwaccel qsv" + (outputHwSurface ? " -hwaccel_output_format qsv -noautorotate" + stripRotationDataArgs : string.Empty);
}
}
}
@@ -5846,12 +6114,12 @@ namespace MediaBrowser.Controller.MediaEncoding
if (options.EnableEnhancedNvdecDecoder)
{
// set -threads 1 to nvdec decoder explicitly since it doesn't implement threading support.
- return " -hwaccel cuda" + (outputHwSurface ? " -hwaccel_output_format cuda" : string.Empty)
+ return " -hwaccel cuda" + (outputHwSurface ? " -hwaccel_output_format cuda -noautorotate" + stripRotationDataArgs : string.Empty)
+ (nvdecNoInternalCopy ? " -hwaccel_flags +unsafe_output" : string.Empty) + " -threads 1" + (isAv1 ? " -c:v av1" : string.Empty);
}
// cuvid decoder doesn't have threading issue.
- return " -hwaccel cuda" + (outputHwSurface ? " -hwaccel_output_format cuda" : string.Empty);
+ return " -hwaccel cuda" + (outputHwSurface ? " -hwaccel_output_format cuda -noautorotate" + stripRotationDataArgs : string.Empty);
}
}
@@ -5860,7 +6128,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (isD3d11Supported && isCodecAvailable)
{
- return " -hwaccel d3d11va" + (outputHwSurface ? " -hwaccel_output_format d3d11" : string.Empty)
+ return " -hwaccel d3d11va" + (outputHwSurface ? " -hwaccel_output_format d3d11 -noautorotate" + stripRotationDataArgs : string.Empty)
+ (profileMismatch ? " -hwaccel_flags +allow_profile_mismatch" : string.Empty) + (isAv1 ? " -c:v av1" : string.Empty);
}
}
@@ -5870,7 +6138,7 @@ namespace MediaBrowser.Controller.MediaEncoding
&& isVaapiSupported
&& isCodecAvailable)
{
- return " -hwaccel vaapi" + (outputHwSurface ? " -hwaccel_output_format vaapi" : string.Empty)
+ return " -hwaccel vaapi" + (outputHwSurface ? " -hwaccel_output_format vaapi -noautorotate" + stripRotationDataArgs : string.Empty)
+ (profileMismatch ? " -hwaccel_flags +allow_profile_mismatch" : string.Empty) + (isAv1 ? " -c:v av1" : string.Empty);
}
@@ -5879,7 +6147,7 @@ namespace MediaBrowser.Controller.MediaEncoding
&& isVideotoolboxSupported
&& isCodecAvailable)
{
- return " -hwaccel videotoolbox" + (outputHwSurface ? " -hwaccel_output_format videotoolbox_vld" : string.Empty);
+ return " -hwaccel videotoolbox" + (outputHwSurface ? " -hwaccel_output_format videotoolbox_vld" : string.Empty) + " -noautorotate" + stripRotationDataArgs;
}
// Rockchip rkmpp
@@ -5887,7 +6155,7 @@ namespace MediaBrowser.Controller.MediaEncoding
&& isRkmppSupported
&& isCodecAvailable)
{
- return " -hwaccel rkmpp" + (outputHwSurface ? " -hwaccel_output_format drm_prime" : string.Empty);
+ return " -hwaccel rkmpp" + (outputHwSurface ? " -hwaccel_output_format drm_prime -noautorotate" + stripRotationDataArgs : string.Empty);
}
return null;
diff --git a/MediaBrowser.Controller/MediaEncoding/FilterOptionType.cs b/MediaBrowser.Controller/MediaEncoding/FilterOptionType.cs
index b1d319d21..a2b6e1d73 100644
--- a/MediaBrowser.Controller/MediaEncoding/FilterOptionType.cs
+++ b/MediaBrowser.Controller/MediaEncoding/FilterOptionType.cs
@@ -33,6 +33,11 @@ namespace MediaBrowser.Controller.MediaEncoding
/// <summary>
/// The overlay_vulkan_framesync.
/// </summary>
- OverlayVulkanFrameSync = 5
+ OverlayVulkanFrameSync = 5,
+
+ /// <summary>
+ /// The transpose_opencl_reversal.
+ /// </summary>
+ TransposeOpenclReversal = 6
}
}
diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
index 038c6c7f6..c767b4a51 100644
--- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
+++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
@@ -66,6 +66,12 @@ namespace MediaBrowser.Controller.MediaEncoding
/// <summary>
/// Gets a value indicating whether the configured Vaapi device supports vulkan drm format modifier.
/// </summary>
+ /// <value><c>true</c> if the Vaapi device supports vulkan drm format modifier, <c>false</c> otherwise.</value>
+ bool IsVaapiDeviceSupportVulkanDrmModifier { get; }
+
+ /// <summary>
+ /// Gets a value indicating whether the configured Vaapi device supports vulkan drm interop via dma-buf.
+ /// </summary>
/// <value><c>true</c> if the Vaapi device supports vulkan drm interop, <c>false</c> otherwise.</value>
bool IsVaapiDeviceSupportVulkanDrmInterop { get; }
@@ -223,14 +229,8 @@ namespace MediaBrowser.Controller.MediaEncoding
/// <summary>
/// Sets the path to find FFmpeg.
/// </summary>
- void SetFFmpegPath();
-
- /// <summary>
- /// Updates the encoder path.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <param name="pathType">The type of path.</param>
- void UpdateEncoderPath(string path, string pathType);
+ /// <returns>bool indicates whether a valid ffmpeg is found.</returns>
+ bool SetFFmpegPath();
/// <summary>
/// Gets the primary playlist of .vob files.
diff --git a/MediaBrowser.Controller/MediaEncoding/TranscodingJob.cs b/MediaBrowser.Controller/MediaEncoding/TranscodingJob.cs
index 2b6540ea8..fefa66cdb 100644
--- a/MediaBrowser.Controller/MediaEncoding/TranscodingJob.cs
+++ b/MediaBrowser.Controller/MediaEncoding/TranscodingJob.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Diagnostics;
using System.Threading;
using MediaBrowser.Model.Dto;
diff --git a/MediaBrowser.Controller/MediaEncoding/TranscodingJobType.cs b/MediaBrowser.Controller/MediaEncoding/TranscodingJobType.cs
index c1bb387e1..fb0f0c069 100644
--- a/MediaBrowser.Controller/MediaEncoding/TranscodingJobType.cs
+++ b/MediaBrowser.Controller/MediaEncoding/TranscodingJobType.cs
@@ -1,4 +1,4 @@
-namespace MediaBrowser.Controller.MediaEncoding
+namespace MediaBrowser.Controller.MediaEncoding
{
/// <summary>
/// Enum TranscodingJobType.
diff --git a/MediaBrowser.Controller/MediaEncoding/TranscodingThrottler.cs b/MediaBrowser.Controller/MediaEncoding/TranscodingThrottler.cs
index b95e6ed51..1e7cd0b0a 100644
--- a/MediaBrowser.Controller/MediaEncoding/TranscodingThrottler.cs
+++ b/MediaBrowser.Controller/MediaEncoding/TranscodingThrottler.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
diff --git a/MediaBrowser.Controller/MediaSegements/IMediaSegmentManager.cs b/MediaBrowser.Controller/MediaSegements/IMediaSegmentManager.cs
index 4fcf084e1..67384f6f6 100644
--- a/MediaBrowser.Controller/MediaSegements/IMediaSegmentManager.cs
+++ b/MediaBrowser.Controller/MediaSegements/IMediaSegmentManager.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
diff --git a/MediaBrowser.Controller/Net/WebSocketListenerState.cs b/MediaBrowser.Controller/Net/WebSocketListenerState.cs
index 2410801d6..68f5c81be 100644
--- a/MediaBrowser.Controller/Net/WebSocketListenerState.cs
+++ b/MediaBrowser.Controller/Net/WebSocketListenerState.cs
@@ -1,4 +1,4 @@
-#nullable disable
+#nullable disable
#pragma warning disable CS1591
diff --git a/MediaBrowser.Controller/Net/WebSocketMessages/InboundWebSocketMessage.cs b/MediaBrowser.Controller/Net/WebSocketMessages/InboundWebSocketMessage.cs
index 8d6e821df..50d7dcaab 100644
--- a/MediaBrowser.Controller/Net/WebSocketMessages/InboundWebSocketMessage.cs
+++ b/MediaBrowser.Controller/Net/WebSocketMessages/InboundWebSocketMessage.cs
@@ -1,4 +1,4 @@
-namespace MediaBrowser.Controller.Net.WebSocketMessages;
+namespace MediaBrowser.Controller.Net.WebSocketMessages;
/// <summary>
/// Inbound websocket message.
diff --git a/MediaBrowser.Controller/Net/WebSocketMessages/InboundWebSocketMessageOfT.cs b/MediaBrowser.Controller/Net/WebSocketMessages/InboundWebSocketMessageOfT.cs
index 4da5e7d31..d0105bb8e 100644
--- a/MediaBrowser.Controller/Net/WebSocketMessages/InboundWebSocketMessageOfT.cs
+++ b/MediaBrowser.Controller/Net/WebSocketMessages/InboundWebSocketMessageOfT.cs
@@ -1,4 +1,4 @@
-#pragma warning disable SA1649 // File name must equal class name.
+#pragma warning disable SA1649 // File name must equal class name.
namespace MediaBrowser.Controller.Net.WebSocketMessages;
diff --git a/MediaBrowser.Controller/Net/WebSocketMessages/OutboundWebSocketMessage.cs b/MediaBrowser.Controller/Net/WebSocketMessages/OutboundWebSocketMessage.cs
index 178245851..a9d68e389 100644
--- a/MediaBrowser.Controller/Net/WebSocketMessages/OutboundWebSocketMessage.cs
+++ b/MediaBrowser.Controller/Net/WebSocketMessages/OutboundWebSocketMessage.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
namespace MediaBrowser.Controller.Net.WebSocketMessages;
diff --git a/MediaBrowser.Controller/Net/WebSocketMessages/OutboundWebSocketMessageOfT.cs b/MediaBrowser.Controller/Net/WebSocketMessages/OutboundWebSocketMessageOfT.cs
index cce331805..abc67c739 100644
--- a/MediaBrowser.Controller/Net/WebSocketMessages/OutboundWebSocketMessageOfT.cs
+++ b/MediaBrowser.Controller/Net/WebSocketMessages/OutboundWebSocketMessageOfT.cs
@@ -1,4 +1,4 @@
-#pragma warning disable SA1649 // File name must equal class name.
+#pragma warning disable SA1649 // File name must equal class name.
using System;
diff --git a/MediaBrowser.Controller/Providers/IExternalUrlProvider.cs b/MediaBrowser.Controller/Providers/IExternalUrlProvider.cs
index 86a180627..14f577170 100644
--- a/MediaBrowser.Controller/Providers/IExternalUrlProvider.cs
+++ b/MediaBrowser.Controller/Providers/IExternalUrlProvider.cs
@@ -1,4 +1,4 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
using MediaBrowser.Controller.Entities;
namespace MediaBrowser.Controller.Providers;
diff --git a/MediaBrowser.Controller/Providers/RefreshPriority.cs b/MediaBrowser.Controller/Providers/RefreshPriority.cs
index e4c39cea1..78304b56b 100644
--- a/MediaBrowser.Controller/Providers/RefreshPriority.cs
+++ b/MediaBrowser.Controller/Providers/RefreshPriority.cs
@@ -1,4 +1,4 @@
-namespace MediaBrowser.Controller.Providers
+namespace MediaBrowser.Controller.Providers
{
/// <summary>
/// Provider refresh priority.
diff --git a/MediaBrowser.Controller/Security/IAuthenticationManager.cs b/MediaBrowser.Controller/Security/IAuthenticationManager.cs
index 070ab7a85..79b2a2444 100644
--- a/MediaBrowser.Controller/Security/IAuthenticationManager.cs
+++ b/MediaBrowser.Controller/Security/IAuthenticationManager.cs
@@ -1,4 +1,4 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Threading.Tasks;
namespace MediaBrowser.Controller.Security
diff --git a/MediaBrowser.Controller/Streaming/ProgressiveFileStream.cs b/MediaBrowser.Controller/Streaming/ProgressiveFileStream.cs
index f44dc92d7..d75ef7ad3 100644
--- a/MediaBrowser.Controller/Streaming/ProgressiveFileStream.cs
+++ b/MediaBrowser.Controller/Streaming/ProgressiveFileStream.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
diff --git a/MediaBrowser.Controller/Streaming/StreamingRequestDto.cs b/MediaBrowser.Controller/Streaming/StreamingRequestDto.cs
index e47ef65f0..89eef28fa 100644
--- a/MediaBrowser.Controller/Streaming/StreamingRequestDto.cs
+++ b/MediaBrowser.Controller/Streaming/StreamingRequestDto.cs
@@ -1,4 +1,4 @@
-using MediaBrowser.Controller.MediaEncoding;
+using MediaBrowser.Controller.MediaEncoding;
namespace MediaBrowser.Controller.Streaming;
diff --git a/MediaBrowser.LocalMetadata/XmlProviderUtils.cs b/MediaBrowser.LocalMetadata/XmlProviderUtils.cs
index e247b8bb8..a7a390deb 100644
--- a/MediaBrowser.LocalMetadata/XmlProviderUtils.cs
+++ b/MediaBrowser.LocalMetadata/XmlProviderUtils.cs
@@ -1,4 +1,4 @@
-namespace MediaBrowser.LocalMetadata
+namespace MediaBrowser.LocalMetadata
{
/// <summary>
/// The xml provider utils.
diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
index 20a47da10..2b6ed8fa0 100644
--- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
@@ -110,27 +110,34 @@ namespace MediaBrowser.MediaEncoding.Encoder
// cuda
"scale_cuda",
"yadif_cuda",
+ "bwdif_cuda",
"tonemap_cuda",
"overlay_cuda",
+ "transpose_cuda",
"hwupload_cuda",
// opencl
"scale_opencl",
"tonemap_opencl",
"overlay_opencl",
+ "transpose_opencl",
// vaapi
"scale_vaapi",
"deinterlace_vaapi",
"tonemap_vaapi",
"procamp_vaapi",
"overlay_vaapi",
+ "transpose_vaapi",
"hwupload_vaapi",
// vulkan
"libplacebo",
"scale_vulkan",
"overlay_vulkan",
+ "transpose_vulkan",
+ "flip_vulkan",
// videotoolbox
"yadif_videotoolbox",
"scale_vt",
+ "transpose_vt",
"overlay_videotoolbox",
"tonemap_videotoolbox",
// rkrga
@@ -146,7 +153,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
{ 2, new string[] { "tonemap_opencl", "bt2390" } },
{ 3, new string[] { "overlay_opencl", "Action to take when encountering EOF from secondary input" } },
{ 4, new string[] { "overlay_vaapi", "Action to take when encountering EOF from secondary input" } },
- { 5, new string[] { "overlay_vulkan", "Action to take when encountering EOF from secondary input" } }
+ { 5, new string[] { "overlay_vulkan", "Action to take when encountering EOF from secondary input" } },
+ { 6, new string[] { "transpose_opencl", "rotate by half-turn" } }
};
// These are the library versions that corresponds to our minimum ffmpeg version 4.4 according to the version table below
diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs
index c5f500e76..2daeac734 100644
--- a/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs
@@ -42,7 +42,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
// If there's more than one we'll need to use the concat command
if (inputFiles.Count > 1)
{
- var files = string.Join("|", inputFiles.Select(NormalizePath));
+ var files = string.Join('|', inputFiles.Select(NormalizePath));
return string.Format(CultureInfo.InvariantCulture, "concat:\"{0}\"", files);
}
diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
index e8461e77f..764230feb 100644
--- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
@@ -12,6 +12,7 @@ using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using AsyncKeyedLock;
+using Jellyfin.Data.Enums;
using Jellyfin.Extensions;
using Jellyfin.Extensions.Json;
using Jellyfin.Extensions.Json.Converters;
@@ -79,8 +80,14 @@ namespace MediaBrowser.MediaEncoding.Encoder
private bool _isVaapiDeviceAmd = false;
private bool _isVaapiDeviceInteliHD = false;
private bool _isVaapiDeviceInteli965 = false;
+ private bool _isVaapiDeviceSupportVulkanDrmModifier = false;
private bool _isVaapiDeviceSupportVulkanDrmInterop = false;
+ private static string[] _vulkanImageDrmFmtModifierExts =
+ {
+ "VK_EXT_image_drm_format_modifier",
+ };
+
private static string[] _vulkanExternalMemoryDmaBufExts =
{
"VK_KHR_external_memory_fd",
@@ -141,34 +148,50 @@ namespace MediaBrowser.MediaEncoding.Encoder
public bool IsVaapiDeviceInteli965 => _isVaapiDeviceInteli965;
/// <inheritdoc />
+ public bool IsVaapiDeviceSupportVulkanDrmModifier => _isVaapiDeviceSupportVulkanDrmModifier;
+
+ /// <inheritdoc />
public bool IsVaapiDeviceSupportVulkanDrmInterop => _isVaapiDeviceSupportVulkanDrmInterop;
[GeneratedRegex(@"[^\/\\]+?(\.[^\/\\\n.]+)?$")]
private static partial Regex FfprobePathRegex();
/// <summary>
- /// Run at startup or if the user removes a Custom path from transcode page.
+ /// Run at startup to validate ffmpeg.
/// Sets global variables FFmpegPath.
- /// Precedence is: Config > CLI > $PATH.
+ /// Precedence is: CLI/Env var > Config > $PATH.
/// </summary>
- public void SetFFmpegPath()
+ /// <returns>bool indicates whether a valid ffmpeg is found.</returns>
+ public bool SetFFmpegPath()
{
+ var skipValidation = _config.GetFFmpegSkipValidation();
+ if (skipValidation)
+ {
+ _logger.LogWarning("FFmpeg: Skipping FFmpeg Validation due to FFmpeg:novalidation set to true");
+ return true;
+ }
+
// 1) Check if the --ffmpeg CLI switch has been given
var ffmpegPath = _startupOptionFFmpegPath;
+ string ffmpegPathSetMethodText = "command line or environment variable";
if (string.IsNullOrEmpty(ffmpegPath))
{
// 2) Custom path stored in config/encoding xml file under tag <EncoderAppPath> should be used as a fallback
ffmpegPath = _configurationManager.GetEncodingOptions().EncoderAppPath;
+ ffmpegPathSetMethodText = "encoding.xml config file";
if (string.IsNullOrEmpty(ffmpegPath))
{
// 3) Check "ffmpeg"
ffmpegPath = "ffmpeg";
+ ffmpegPathSetMethodText = "system $PATH";
}
}
if (!ValidatePath(ffmpegPath))
{
_ffmpegPath = null;
+ _logger.LogError("FFmpeg: Path set by {FfmpegPathSetMethodText} is invalid", ffmpegPathSetMethodText);
+ return false;
}
// Write the FFmpeg path to the config/encoding.xml file as <EncoderAppPathDisplay> so it appears in UI
@@ -206,6 +229,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
_isVaapiDeviceAmd = validator.CheckVaapiDeviceByDriverName("Mesa Gallium driver", options.VaapiDevice);
_isVaapiDeviceInteliHD = validator.CheckVaapiDeviceByDriverName("Intel iHD driver", options.VaapiDevice);
_isVaapiDeviceInteli965 = validator.CheckVaapiDeviceByDriverName("Intel i965 driver", options.VaapiDevice);
+ _isVaapiDeviceSupportVulkanDrmModifier = validator.CheckVulkanDrmDeviceByExtensionName(options.VaapiDevice, _vulkanImageDrmFmtModifierExts);
_isVaapiDeviceSupportVulkanDrmInterop = validator.CheckVulkanDrmDeviceByExtensionName(options.VaapiDevice, _vulkanExternalMemoryDmaBufExts);
if (_isVaapiDeviceAmd)
@@ -221,6 +245,11 @@ namespace MediaBrowser.MediaEncoding.Encoder
_logger.LogInformation("VAAPI device {RenderNodePath} is Intel GPU (i965)", options.VaapiDevice);
}
+ if (_isVaapiDeviceSupportVulkanDrmModifier)
+ {
+ _logger.LogInformation("VAAPI device {RenderNodePath} supports Vulkan DRM modifier", options.VaapiDevice);
+ }
+
if (_isVaapiDeviceSupportVulkanDrmInterop)
{
_logger.LogInformation("VAAPI device {RenderNodePath} supports Vulkan DRM interop", options.VaapiDevice);
@@ -229,65 +258,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
_logger.LogInformation("FFmpeg: {FfmpegPath}", _ffmpegPath ?? string.Empty);
- }
-
- /// <summary>
- /// Triggered from the Settings > Transcoding UI page when users submits Custom FFmpeg path to use.
- /// Only write the new path to xml if it exists. Do not perform validation checks on ffmpeg here.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <param name="pathType">The path type.</param>
- public void UpdateEncoderPath(string path, string pathType)
- {
- var config = _configurationManager.GetEncodingOptions();
-
- // Filesystem may not be case insensitive, but EncoderAppPathDisplay should always point to a valid file?
- if (string.IsNullOrEmpty(config.EncoderAppPath)
- && string.Equals(config.EncoderAppPathDisplay, path, StringComparison.OrdinalIgnoreCase))
- {
- _logger.LogDebug("Existing ffmpeg path is empty and the new path is the same as {EncoderAppPathDisplay}. Skipping", nameof(config.EncoderAppPathDisplay));
- return;
- }
-
- string newPath;
-
- _logger.LogInformation("Attempting to update encoder path to {Path}. pathType: {PathType}", path ?? string.Empty, pathType ?? string.Empty);
-
- if (!string.Equals(pathType, "custom", StringComparison.OrdinalIgnoreCase))
- {
- throw new ArgumentException("Unexpected pathType value");
- }
-
- if (string.IsNullOrWhiteSpace(path))
- {
- // User had cleared the custom path in UI
- newPath = string.Empty;
- }
- else
- {
- if (Directory.Exists(path))
- {
- // Given path is directory, so resolve down to filename
- newPath = GetEncoderPathFromDirectory(path, "ffmpeg");
- }
- else
- {
- newPath = path;
- }
-
- if (!new EncoderValidator(_logger, newPath).ValidateVersion())
- {
- throw new ResourceNotFoundException();
- }
- }
-
- // Write the new ffmpeg path to the xml as <EncoderAppPath>
- // This ensures its not lost on next startup
- config.EncoderAppPath = newPath;
- _configurationManager.SaveConfiguration("encoding", config);
-
- // Trigger SetFFmpegPath so we validate the new path and setup probe path
- SetFFmpegPath();
+ return !string.IsNullOrWhiteSpace(ffmpegPath);
}
/// <summary>
@@ -306,7 +277,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
bool rc = new EncoderValidator(_logger, path).ValidateVersion();
if (!rc)
{
- _logger.LogWarning("FFmpeg: Failed version check: {Path}", path);
+ _logger.LogError("FFmpeg: Failed version check: {Path}", path);
return false;
}
@@ -710,18 +681,18 @@ namespace MediaBrowser.MediaEncoding.Encoder
filters.Add("thumbnail=n=" + (useLargerBatchSize ? "50" : "24"));
}
- // Use SW tonemap on HDR10/HLG video stream only when the zscale or tonemapx filter is available.
+ // Use SW tonemap on HDR video stream only when the zscale or tonemapx filter is available.
+ // Only enable Dolby Vision tonemap when tonemapx is available
var enableHdrExtraction = false;
- if (string.Equals(videoStream?.ColorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase)
- || string.Equals(videoStream?.ColorTransfer, "arib-std-b67", StringComparison.OrdinalIgnoreCase))
+ if (videoStream?.VideoRange == VideoRange.HDR)
{
if (SupportsFilter("tonemapx"))
{
enableHdrExtraction = true;
filters.Add("tonemapx=tonemap=bt2390:desat=0:peak=100:t=bt709:m=bt709:p=bt709:format=yuv420p");
}
- else if (SupportsFilter("zscale"))
+ else if (SupportsFilter("zscale") && videoStream.VideoRangeType != VideoRangeType.DOVI)
{
enableHdrExtraction = true;
filters.Add("zscale=t=linear:npl=100,format=gbrpf32le,zscale=p=bt709,tonemap=tonemap=hable:desat=0:peak=100,zscale=t=bt709:m=bt709,format=yuv420p");
@@ -764,8 +735,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
using (var processWrapper = new ProcessWrapper(process, this))
{
- bool ranToCompletion;
-
using (await _thumbnailResourcePool.LockAsync(cancellationToken).ConfigureAwait(false))
{
StartProcess(processWrapper);
@@ -779,22 +748,18 @@ namespace MediaBrowser.MediaEncoding.Encoder
try
{
await process.WaitForExitAsync(TimeSpan.FromMilliseconds(timeoutMs)).ConfigureAwait(false);
- ranToCompletion = true;
}
- catch (OperationCanceledException)
+ catch (OperationCanceledException ex)
{
process.Kill(true);
- ranToCompletion = false;
+ throw new FfmpegException(string.Format(CultureInfo.InvariantCulture, "ffmpeg image extraction timed out for {0} after {1}ms", inputPath, timeoutMs), ex);
}
}
- var exitCode = ranToCompletion ? processWrapper.ExitCode ?? 0 : -1;
var file = _fileSystem.GetFileInfo(tempExtractPath);
- if (exitCode == -1 || !file.Exists || file.Length == 0)
+ if (processWrapper.ExitCode > 0 || !file.Exists || file.Length == 0)
{
- _logger.LogError("ffmpeg image extraction failed for {Path}", inputPath);
-
throw new FfmpegException(string.Format(CultureInfo.InvariantCulture, "ffmpeg image extraction failed for {0}", inputPath));
}
diff --git a/MediaBrowser.MediaEncoding/Probing/MediaStreamInfoSideData.cs b/MediaBrowser.MediaEncoding/Probing/MediaStreamInfoSideData.cs
index 5dbc438e4..0b5dd1d1b 100644
--- a/MediaBrowser.MediaEncoding/Probing/MediaStreamInfoSideData.cs
+++ b/MediaBrowser.MediaEncoding/Probing/MediaStreamInfoSideData.cs
@@ -69,5 +69,12 @@ namespace MediaBrowser.MediaEncoding.Probing
/// <value>The DvBlSignalCompatibilityId.</value>
[JsonPropertyName("dv_bl_signal_compatibility_id")]
public int? DvBlSignalCompatibilityId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the Rotation in degrees.
+ /// </summary>
+ /// <value>The Rotation.</value>
+ [JsonPropertyName("rotation")]
+ public int? Rotation { get; set; }
}
}
diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
index 5a5eb6e61..334796f58 100644
--- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
+++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
@@ -892,8 +892,12 @@ namespace MediaBrowser.MediaEncoding.Probing
stream.ElPresentFlag = data.ElPresentFlag;
stream.BlPresentFlag = data.BlPresentFlag;
stream.DvBlSignalCompatibilityId = data.DvBlSignalCompatibilityId;
+ }
- break;
+ // Parse video rotation metadata from side_data
+ else if (string.Equals(data.SideDataType, "Display Matrix", StringComparison.OrdinalIgnoreCase))
+ {
+ stream.Rotation = data.Rotation;
}
}
}
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs
index fd55db4ba..a79d801fb 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs
@@ -54,12 +54,23 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
break;
}
-
- _logger.LogError(
- "{ErrorCount} errors encountered while parsing '{FileExtension}' subtitle using the {SubtitleFormatParser} format parser",
- subtitleFormat.ErrorCount,
- fileExtension,
- subtitleFormat.Name);
+ else if (subtitleFormat.TryGetErrors(out var errors))
+ {
+ _logger.LogError(
+ "{ErrorCount} errors encountered while parsing '{FileExtension}' subtitle using the {SubtitleFormatParser} format parser, errors: {Errors}",
+ subtitleFormat.ErrorCount,
+ fileExtension,
+ subtitleFormat.Name,
+ errors);
+ }
+ else
+ {
+ _logger.LogError(
+ "{ErrorCount} errors encountered while parsing '{FileExtension}' subtitle using the {SubtitleFormatParser} format parser",
+ subtitleFormat.ErrorCount,
+ fileExtension,
+ subtitleFormat.Name);
+ }
}
if (subtitle.Paragraphs.Count == 0)
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleFormatExtensions.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleFormatExtensions.cs
new file mode 100644
index 000000000..88c2bf3db
--- /dev/null
+++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleFormatExtensions.cs
@@ -0,0 +1,29 @@
+using System.Diagnostics.CodeAnalysis;
+using Nikse.SubtitleEdit.Core.SubtitleFormats;
+
+namespace MediaBrowser.MediaEncoding.Subtitles;
+
+internal static class SubtitleFormatExtensions
+{
+ /// <summary>
+ /// Will try to find errors if supported by provider.
+ /// </summary>
+ /// <param name="format">The subtitle format.</param>
+ /// <param name="errors">The out errors value.</param>
+ /// <returns>True if errors are available for given format.</returns>
+ public static bool TryGetErrors(this SubtitleFormat format, [NotNullWhen(true)] out string? errors)
+ {
+ errors = format switch
+ {
+ SubStationAlpha ssa => ssa.Errors,
+ AdvancedSubStationAlpha assa => assa.Errors,
+ SubRip subRip => subRip.Errors,
+ MicroDvd microDvd => microDvd.Errors,
+ DCinemaSmpte2007 smpte2007 => smpte2007.Errors,
+ DCinemaSmpte2010 smpte2010 => smpte2010.Errors,
+ _ => null,
+ };
+
+ return !string.IsNullOrWhiteSpace(errors);
+ }
+}
diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs
index 9a192f584..4c5213d4e 100644
--- a/MediaBrowser.Model/Configuration/EncodingOptions.cs
+++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs
@@ -26,6 +26,7 @@ public class EncodingOptions
// This is a DRM device that is almost guaranteed to be there on every intel platform,
// plus it's the default one in ffmpeg if you don't specify anything
VaapiDevice = "/dev/dri/renderD128";
+ QsvDevice = string.Empty;
EnableTonemapping = false;
EnableVppTonemapping = false;
EnableVideoToolboxTonemapping = false;
@@ -137,6 +138,11 @@ public class EncodingOptions
public string VaapiDevice { get; set; }
/// <summary>
+ /// Gets or sets the QSV device.
+ /// </summary>
+ public string QsvDevice { get; set; }
+
+ /// <summary>
/// Gets or sets a value indicating whether tonemapping is enabled.
/// </summary>
public bool EnableTonemapping { get; set; }
diff --git a/MediaBrowser.Model/Cryptography/Constants.cs b/MediaBrowser.Model/Cryptography/Constants.cs
index f2ebb5d3d..a4cb62245 100644
--- a/MediaBrowser.Model/Cryptography/Constants.cs
+++ b/MediaBrowser.Model/Cryptography/Constants.cs
@@ -18,6 +18,6 @@ namespace MediaBrowser.Model.Cryptography
/// <summary>
/// The default amount of iterations for hashing passwords.
/// </summary>
- public const int DefaultIterations = 120000;
+ public const int DefaultIterations = 210000;
}
}
diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs
index 20e011745..a0e8c39be 100644
--- a/MediaBrowser.Model/Entities/MediaStream.cs
+++ b/MediaBrowser.Model/Entities/MediaStream.cs
@@ -124,6 +124,12 @@ namespace MediaBrowser.Model.Entities
public int? DvBlSignalCompatibilityId { get; set; }
/// <summary>
+ /// Gets or sets the Rotation in degrees.
+ /// </summary>
+ /// <value>The video rotation.</value>
+ public int? Rotation { get; set; }
+
+ /// <summary>
/// Gets or sets the comment.
/// </summary>
/// <value>The comment.</value>
diff --git a/MediaBrowser.Model/Extensions/EnumerableExtensions.cs b/MediaBrowser.Model/Extensions/EnumerableExtensions.cs
index c6d1f3900..8963bdb73 100644
--- a/MediaBrowser.Model/Extensions/EnumerableExtensions.cs
+++ b/MediaBrowser.Model/Extensions/EnumerableExtensions.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
using MediaBrowser.Model.Providers;
diff --git a/MediaBrowser.Model/Library/UserViewQuery.cs b/MediaBrowser.Model/Library/UserViewQuery.cs
index e20d6af49..643a1f9b1 100644
--- a/MediaBrowser.Model/Library/UserViewQuery.cs
+++ b/MediaBrowser.Model/Library/UserViewQuery.cs
@@ -1,6 +1,7 @@
#pragma warning disable CS1591
using System;
+using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
namespace MediaBrowser.Model.Library
@@ -14,10 +15,10 @@ namespace MediaBrowser.Model.Library
}
/// <summary>
- /// Gets or sets the user identifier.
+ /// Gets or sets the user.
/// </summary>
- /// <value>The user identifier.</value>
- public Guid UserId { get; set; }
+ /// <value>The user.</value>
+ public required User User { get; set; }
/// <summary>
/// Gets or sets a value indicating whether [include external content].
diff --git a/MediaBrowser.Model/LiveTv/ChannelMappingOptionsDto.cs b/MediaBrowser.Model/LiveTv/ChannelMappingOptionsDto.cs
index 3f9ecc8c8..cbd43c144 100644
--- a/MediaBrowser.Model/LiveTv/ChannelMappingOptionsDto.cs
+++ b/MediaBrowser.Model/LiveTv/ChannelMappingOptionsDto.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using MediaBrowser.Model.Dto;
diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj
index b4a30b5d5..9489fe190 100644
--- a/MediaBrowser.Model/MediaBrowser.Model.csproj
+++ b/MediaBrowser.Model/MediaBrowser.Model.csproj
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
<!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
<PropertyGroup>
diff --git a/MediaBrowser.Model/Net/MimeTypes.cs b/MediaBrowser.Model/Net/MimeTypes.cs
index 90035f18f..5d65b0f9b 100644
--- a/MediaBrowser.Model/Net/MimeTypes.cs
+++ b/MediaBrowser.Model/Net/MimeTypes.cs
@@ -1,6 +1,7 @@
#pragma warning disable CS1591
using System;
+using System.Collections.Frozen;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
@@ -26,7 +27,7 @@ namespace MediaBrowser.Model.Net
/// <summary>
/// Any extension in this list is considered a video file.
/// </summary>
- private static readonly HashSet<string> _videoFileExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
+ private static readonly FrozenSet<string> _videoFileExtensions = new[]
{
".3gp",
".asf",
@@ -57,90 +58,90 @@ namespace MediaBrowser.Model.Net
".webm",
".wmv",
".wtv",
- };
+ }.ToFrozenSet(StringComparer.OrdinalIgnoreCase);
/// <summary>
/// Used for extensions not in <see cref="Model.MimeTypes"/> or to override them.
/// </summary>
- private static readonly Dictionary<string, string> _mimeTypeLookup = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
+ private static readonly FrozenDictionary<string, string> _mimeTypeLookup = new KeyValuePair<string, string>[]
{
// Type application
- { ".azw3", "application/vnd.amazon.ebook" },
- { ".cb7", "application/x-cb7" },
- { ".cba", "application/x-cba" },
- { ".cbr", "application/vnd.comicbook-rar" },
- { ".cbt", "application/x-cbt" },
- { ".cbz", "application/vnd.comicbook+zip" },
+ new(".azw3", "application/vnd.amazon.ebook"),
+ new(".cb7", "application/x-cb7"),
+ new(".cba", "application/x-cba"),
+ new(".cbr", "application/vnd.comicbook-rar"),
+ new(".cbt", "application/x-cbt"),
+ new(".cbz", "application/vnd.comicbook+zip"),
// Type image
- { ".tbn", "image/jpeg" },
+ new(".tbn", "image/jpeg"),
// Type text
- { ".ass", "text/x-ssa" },
- { ".ssa", "text/x-ssa" },
- { ".edl", "text/plain" },
- { ".html", "text/html; charset=UTF-8" },
- { ".htm", "text/html; charset=UTF-8" },
+ new(".ass", "text/x-ssa"),
+ new(".ssa", "text/x-ssa"),
+ new(".edl", "text/plain"),
+ new(".html", "text/html; charset=UTF-8"),
+ new(".htm", "text/html; charset=UTF-8"),
// Type video
- { ".mpegts", "video/mp2t" },
+ new(".mpegts", "video/mp2t"),
// Type audio
- { ".aac", "audio/aac" },
- { ".ac3", "audio/ac3" },
- { ".ape", "audio/x-ape" },
- { ".dsf", "audio/dsf" },
- { ".dsp", "audio/dsp" },
- { ".flac", "audio/flac" },
- { ".m4b", "audio/mp4" },
- { ".mp3", "audio/mpeg" },
- { ".vorbis", "audio/vorbis" },
- { ".webma", "audio/webm" },
- { ".wv", "audio/x-wavpack" },
- { ".xsp", "audio/xsp" },
- };
-
- private static readonly Dictionary<string, string> _extensionLookup = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
+ new(".aac", "audio/aac"),
+ new(".ac3", "audio/ac3"),
+ new(".ape", "audio/x-ape"),
+ new(".dsf", "audio/dsf"),
+ new(".dsp", "audio/dsp"),
+ new(".flac", "audio/flac"),
+ new(".m4b", "audio/mp4"),
+ new(".mp3", "audio/mpeg"),
+ new(".vorbis", "audio/vorbis"),
+ new(".webma", "audio/webm"),
+ new(".wv", "audio/x-wavpack"),
+ new(".xsp", "audio/xsp"),
+ }.ToFrozenDictionary(pair => pair.Key, pair => pair.Value, StringComparer.OrdinalIgnoreCase);
+
+ private static readonly FrozenDictionary<string, string> _extensionLookup = new KeyValuePair<string, string>[]
{
// Type application
- { "application/vnd.comicbook-rar", ".cbr" },
- { "application/vnd.comicbook+zip", ".cbz" },
- { "application/x-cb7", ".cb7" },
- { "application/x-cba", ".cba" },
- { "application/x-cbr", ".cbr" },
- { "application/x-cbt", ".cbt" },
- { "application/x-cbz", ".cbz" },
- { "application/x-javascript", ".js" },
- { "application/xml", ".xml" },
- { "application/x-mpegURL", ".m3u8" },
+ new("application/vnd.comicbook-rar", ".cbr"),
+ new("application/vnd.comicbook+zip", ".cbz"),
+ new("application/x-cb7", ".cb7"),
+ new("application/x-cba", ".cba"),
+ new("application/x-cbr", ".cbr"),
+ new("application/x-cbt", ".cbt"),
+ new("application/x-cbz", ".cbz"),
+ new("application/x-javascript", ".js"),
+ new("application/xml", ".xml"),
+ new("application/x-mpegURL", ".m3u8"),
// Type audio
- { "audio/aac", ".aac" },
- { "audio/ac3", ".ac3" },
- { "audio/dsf", ".dsf" },
- { "audio/dsp", ".dsp" },
- { "audio/flac", ".flac" },
- { "audio/m4b", ".m4b" },
- { "audio/vorbis", ".vorbis" },
- { "audio/x-ape", ".ape" },
- { "audio/xsp", ".xsp" },
- { "audio/x-wavpack", ".wv" },
+ new("audio/aac", ".aac"),
+ new("audio/ac3", ".ac3"),
+ new("audio/dsf", ".dsf"),
+ new("audio/dsp", ".dsp"),
+ new("audio/flac", ".flac"),
+ new("audio/m4b", ".m4b"),
+ new("audio/vorbis", ".vorbis"),
+ new("audio/x-ape", ".ape"),
+ new("audio/xsp", ".xsp"),
+ new("audio/x-wavpack", ".wv"),
// Type image
- { "image/jpeg", ".jpg" },
- { "image/tiff", ".tiff" },
- { "image/x-png", ".png" },
- { "image/x-icon", ".ico" },
+ new("image/jpeg", ".jpg"),
+ new("image/tiff", ".tiff"),
+ new("image/x-png", ".png"),
+ new("image/x-icon", ".ico"),
// Type text
- { "text/plain", ".txt" },
- { "text/rtf", ".rtf" },
- { "text/x-ssa", ".ssa" },
+ new("text/plain", ".txt"),
+ new("text/rtf", ".rtf"),
+ new("text/x-ssa", ".ssa"),
// Type video
- { "video/vnd.mpeg.dash.mpd", ".mpd" },
- { "video/x-matroska", ".mkv" },
- };
+ new("video/vnd.mpeg.dash.mpd", ".mpd"),
+ new("video/x-matroska", ".mkv"),
+ }.ToFrozenDictionary(pair => pair.Key, pair => pair.Value, StringComparer.OrdinalIgnoreCase);
public static string GetMimeType(string path) => GetMimeType(path, "application/octet-stream");
diff --git a/MediaBrowser.Model/Querying/LatestItemsQuery.cs b/MediaBrowser.Model/Querying/LatestItemsQuery.cs
index d2d9f1f9a..251ff5d68 100644
--- a/MediaBrowser.Model/Querying/LatestItemsQuery.cs
+++ b/MediaBrowser.Model/Querying/LatestItemsQuery.cs
@@ -2,6 +2,7 @@
#pragma warning disable CS1591
using System;
+using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
using MediaBrowser.Model.Entities;
@@ -18,7 +19,7 @@ namespace MediaBrowser.Model.Querying
/// Gets or sets the user to localize search results for.
/// </summary>
/// <value>The user id.</value>
- public Guid UserId { get; set; }
+ public User User { get; set; }
/// <summary>
/// Gets or sets the parent id.
diff --git a/MediaBrowser.Model/Querying/NextUpQuery.cs b/MediaBrowser.Model/Querying/NextUpQuery.cs
index 35353e6fa..8dece28a0 100644
--- a/MediaBrowser.Model/Querying/NextUpQuery.cs
+++ b/MediaBrowser.Model/Querying/NextUpQuery.cs
@@ -1,7 +1,7 @@
-#nullable disable
#pragma warning disable CS1591
using System;
+using Jellyfin.Data.Entities;
using MediaBrowser.Model.Entities;
namespace MediaBrowser.Model.Querying
@@ -19,10 +19,10 @@ namespace MediaBrowser.Model.Querying
}
/// <summary>
- /// Gets or sets the user id.
+ /// Gets or sets the user.
/// </summary>
- /// <value>The user id.</value>
- public Guid UserId { get; set; }
+ /// <value>The user.</value>
+ public required User User { get; set; }
/// <summary>
/// Gets or sets the parent identifier.
@@ -49,24 +49,6 @@ namespace MediaBrowser.Model.Querying
public int? Limit { get; set; }
/// <summary>
- /// gets or sets the fields to return within the items, in addition to basic information.
- /// </summary>
- /// <value>The fields.</value>
- public ItemFields[] Fields { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether [enable images].
- /// </summary>
- /// <value><c>null</c> if [enable images] contains no value, <c>true</c> if [enable images]; otherwise, <c>false</c>.</value>
- public bool? EnableImages { get; set; }
-
- /// <summary>
- /// Gets or sets the image type limit.
- /// </summary>
- /// <value>The image type limit.</value>
- public int? ImageTypeLimit { get; set; }
-
- /// <summary>
/// Gets or sets the enable image types.
/// </summary>
/// <value>The enable image types.</value>
diff --git a/MediaBrowser.Model/Session/HardwareEncodingType.cs b/MediaBrowser.Model/Session/HardwareEncodingType.cs
index 058875cd3..cf424fef5 100644
--- a/MediaBrowser.Model/Session/HardwareEncodingType.cs
+++ b/MediaBrowser.Model/Session/HardwareEncodingType.cs
@@ -1,4 +1,4 @@
-namespace MediaBrowser.Model.Session
+namespace MediaBrowser.Model.Session
{
/// <summary>
/// Enum HardwareEncodingType.
diff --git a/MediaBrowser.Model/System/CastReceiverApplication.cs b/MediaBrowser.Model/System/CastReceiverApplication.cs
index 6a49a5cac..43c3f2eaa 100644
--- a/MediaBrowser.Model/System/CastReceiverApplication.cs
+++ b/MediaBrowser.Model/System/CastReceiverApplication.cs
@@ -1,4 +1,4 @@
-namespace MediaBrowser.Model.System;
+namespace MediaBrowser.Model.System;
/// <summary>
/// The cast receiver application model.
diff --git a/MediaBrowser.Model/Tasks/IConfigurableScheduledTask.cs b/MediaBrowser.Model/Tasks/IConfigurableScheduledTask.cs
index 6212d76f7..b4f99208d 100644
--- a/MediaBrowser.Model/Tasks/IConfigurableScheduledTask.cs
+++ b/MediaBrowser.Model/Tasks/IConfigurableScheduledTask.cs
@@ -1,7 +1,8 @@
-#pragma warning disable CS1591
-
namespace MediaBrowser.Model.Tasks
{
+ /// <summary>
+ /// Interface for configurable scheduled tasks.
+ /// </summary>
public interface IConfigurableScheduledTask
{
/// <summary>
@@ -16,6 +17,10 @@ namespace MediaBrowser.Model.Tasks
/// <value><c>true</c> if this instance is enabled; otherwise, <c>false</c>.</value>
bool IsEnabled { get; }
+ /// <summary>
+ /// Gets a value indicating whether this instance is logged.
+ /// </summary>
+ /// <value><c>true</c> if this instance is logged; otherwise, <c>false</c>.</value>
bool IsLogged { get; }
}
}
diff --git a/MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs b/MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs
index ca769e26b..dcf849fcd 100644
--- a/MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs
+++ b/MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs
@@ -1,5 +1,6 @@
#nullable disable
using System;
+using System.Collections.Generic;
using Jellyfin.Data.Events;
namespace MediaBrowser.Model.Tasks
@@ -60,7 +61,7 @@ namespace MediaBrowser.Model.Tasks
/// Gets or sets the triggers that define when the task will run.
/// </summary>
/// <value>The triggers.</value>
- TaskTriggerInfo[] Triggers { get; set; }
+ IReadOnlyList<TaskTriggerInfo> Triggers { get; set; }
/// <summary>
/// Gets the unique id.
diff --git a/MediaBrowser.Model/Tasks/ITaskManager.cs b/MediaBrowser.Model/Tasks/ITaskManager.cs
index 5b55667e8..6066bbde4 100644
--- a/MediaBrowser.Model/Tasks/ITaskManager.cs
+++ b/MediaBrowser.Model/Tasks/ITaskManager.cs
@@ -1,5 +1,3 @@
-#pragma warning disable CS1591
-
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
@@ -7,17 +5,26 @@ using Jellyfin.Data.Events;
namespace MediaBrowser.Model.Tasks
{
+ /// <summary>
+ /// Interface for the TaskManager class.
+ /// </summary>
public interface ITaskManager : IDisposable
{
+ /// <summary>
+ /// Event handler for task execution.
+ /// </summary>
event EventHandler<GenericEventArgs<IScheduledTaskWorker>>? TaskExecuting;
+ /// <summary>
+ /// Event handler for task completion.
+ /// </summary>
event EventHandler<TaskCompletionEventArgs>? TaskCompleted;
/// <summary>
/// Gets the list of Scheduled Tasks.
/// </summary>
/// <value>The scheduled tasks.</value>
- IScheduledTaskWorker[] ScheduledTasks { get; }
+ IReadOnlyList<IScheduledTaskWorker> ScheduledTasks { get; }
/// <summary>
/// Cancels if running and queue.
@@ -56,6 +63,10 @@ namespace MediaBrowser.Model.Tasks
void QueueScheduledTask<T>()
where T : IScheduledTask;
+ /// <summary>
+ /// Queues the scheduled task if it is not already running.
+ /// </summary>
+ /// <typeparam name="T">An implementation of <see cref="IScheduledTask" />.</typeparam>
void QueueIfNotRunning<T>()
where T : IScheduledTask;
@@ -72,10 +83,24 @@ namespace MediaBrowser.Model.Tasks
/// <param name="tasks">The tasks.</param>
void AddTasks(IEnumerable<IScheduledTask> tasks);
+ /// <summary>
+ /// Adds the tasks.
+ /// </summary>
+ /// <param name="task">The tasks.</param>
void Cancel(IScheduledTaskWorker task);
+ /// <summary>
+ /// Executes the tasks.
+ /// </summary>
+ /// <param name="task">The tasks.</param>
+ /// <param name="options">The options.</param>
+ /// <returns>The executed tasks.</returns>
Task Execute(IScheduledTaskWorker task, TaskOptions options);
+ /// <summary>
+ /// Executes the tasks.
+ /// </summary>
+ /// <typeparam name="T">An implementation of <see cref="IScheduledTask" />.</typeparam>
void Execute<T>()
where T : IScheduledTask;
}
diff --git a/MediaBrowser.Model/Tasks/TaskCompletionEventArgs.cs b/MediaBrowser.Model/Tasks/TaskCompletionEventArgs.cs
index 48950667e..ab2c0ada5 100644
--- a/MediaBrowser.Model/Tasks/TaskCompletionEventArgs.cs
+++ b/MediaBrowser.Model/Tasks/TaskCompletionEventArgs.cs
@@ -1,19 +1,33 @@
-#pragma warning disable CS1591
-
using System;
namespace MediaBrowser.Model.Tasks
{
+ /// <summary>
+ /// Class containing event arguments for task completion.
+ /// </summary>
public class TaskCompletionEventArgs : EventArgs
{
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TaskCompletionEventArgs"/> class.
+ /// </summary>
+ /// <param name="task">Instance of the <see cref="IScheduledTaskWorker"/> interface.</param>
+ /// <param name="result">The task result.</param>
public TaskCompletionEventArgs(IScheduledTaskWorker task, TaskResult result)
{
Task = task;
Result = result;
}
+ /// <summary>
+ /// Gets the task.
+ /// </summary>
+ /// <value>The task.</value>
public IScheduledTaskWorker Task { get; }
+ /// <summary>
+ /// Gets the result.
+ /// </summary>
+ /// <value>The result.</value>
public TaskResult Result { get; }
}
}
diff --git a/MediaBrowser.Model/Tasks/TaskInfo.cs b/MediaBrowser.Model/Tasks/TaskInfo.cs
index 16de0b121..8c8ddc597 100644
--- a/MediaBrowser.Model/Tasks/TaskInfo.cs
+++ b/MediaBrowser.Model/Tasks/TaskInfo.cs
@@ -1,5 +1,6 @@
#nullable disable
using System;
+using System.Collections.Generic;
namespace MediaBrowser.Model.Tasks
{
@@ -13,7 +14,7 @@ namespace MediaBrowser.Model.Tasks
/// </summary>
public TaskInfo()
{
- Triggers = Array.Empty<TaskTriggerInfo>();
+ Triggers = [];
}
/// <summary>
@@ -50,7 +51,7 @@ namespace MediaBrowser.Model.Tasks
/// Gets or sets the triggers.
/// </summary>
/// <value>The triggers.</value>
- public TaskTriggerInfo[] Triggers { get; set; }
+ public IReadOnlyList<TaskTriggerInfo> Triggers { get; set; }
/// <summary>
/// Gets or sets the description.
diff --git a/MediaBrowser.Model/Tasks/TaskOptions.cs b/MediaBrowser.Model/Tasks/TaskOptions.cs
index 3a221b878..799c0554b 100644
--- a/MediaBrowser.Model/Tasks/TaskOptions.cs
+++ b/MediaBrowser.Model/Tasks/TaskOptions.cs
@@ -1,9 +1,14 @@
-#pragma warning disable CS1591
-
namespace MediaBrowser.Model.Tasks
{
+ /// <summary>
+ /// Class containing options for tasks.
+ /// </summary>
public class TaskOptions
{
+ /// <summary>
+ /// Gets or sets the maximum runtime in ticks.
+ /// </summary>
+ /// <value>The ticks.</value>
public long? MaxRuntimeTicks { get; set; }
}
}
diff --git a/MediaBrowser.Model/Tasks/TaskTriggerInfo.cs b/MediaBrowser.Model/Tasks/TaskTriggerInfo.cs
index 1d8767dc1..63709557d 100644
--- a/MediaBrowser.Model/Tasks/TaskTriggerInfo.cs
+++ b/MediaBrowser.Model/Tasks/TaskTriggerInfo.cs
@@ -1,6 +1,4 @@
#nullable disable
-#pragma warning disable CS1591
-
using System;
namespace MediaBrowser.Model.Tasks
@@ -10,9 +8,24 @@ namespace MediaBrowser.Model.Tasks
/// </summary>
public class TaskTriggerInfo
{
+ /// <summary>
+ /// The daily trigger.
+ /// </summary>
public const string TriggerDaily = "DailyTrigger";
+
+ /// <summary>
+ /// The weekly trigger.
+ /// </summary>
public const string TriggerWeekly = "WeeklyTrigger";
+
+ /// <summary>
+ /// The interval trigger.
+ /// </summary>
public const string TriggerInterval = "IntervalTrigger";
+
+ /// <summary>
+ /// The startup trigger.
+ /// </summary>
public const string TriggerStartup = "StartupTrigger";
/// <summary>
diff --git a/MediaBrowser.Providers/Lyric/LrcLyricParser.cs b/MediaBrowser.Providers/Lyric/LrcLyricParser.cs
index 67b26e457..fffdf4887 100644
--- a/MediaBrowser.Providers/Lyric/LrcLyricParser.cs
+++ b/MediaBrowser.Providers/Lyric/LrcLyricParser.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
-using System.Globalization;
using System.IO;
using System.Linq;
using Jellyfin.Extensions;
@@ -20,7 +19,6 @@ public class LrcLyricParser : ILyricParser
private readonly LyricParser _lrcLyricParser;
private static readonly string[] _supportedMediaTypes = [".lrc", ".elrc"];
- private static readonly string[] _acceptedTimeFormats = ["HH:mm:ss", "H:mm:ss", "mm:ss", "m:ss"];
/// <summary>
/// Initializes a new instance of the <see cref="LrcLyricParser"/> class.
@@ -59,37 +57,7 @@ public class LrcLyricParser : ILyricParser
return null;
}
- List<LrcParser.Model.Lyric> sortedLyricData = lyricData.Lyrics.Where(x => x.TimeTags.Count > 0).OrderBy(x => x.TimeTags.First().Value).ToList();
-
- // Parse metadata rows
- var metaDataRows = lyricData.Lyrics
- .Where(x => x.TimeTags.Count == 0)
- .Where(x => x.Text.StartsWith('[') && x.Text.EndsWith(']'))
- .Select(x => x.Text)
- .ToList();
-
- var fileMetaData = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- foreach (string metaDataRow in metaDataRows)
- {
- var index = metaDataRow.IndexOf(':', StringComparison.OrdinalIgnoreCase);
- if (index == -1)
- {
- continue;
- }
-
- // Remove square bracket before field name, and after field value
- // Example 1: [au: 1hitsong]
- // Example 2: [ar: Calabrese]
- var metaDataFieldName = GetMetadataFieldName(metaDataRow, index);
- var metaDataFieldValue = GetMetadataValue(metaDataRow, index);
-
- if (string.IsNullOrEmpty(metaDataFieldName) || string.IsNullOrEmpty(metaDataFieldValue))
- {
- continue;
- }
-
- fileMetaData[metaDataFieldName] = metaDataFieldValue;
- }
+ List<LrcParser.Model.Lyric> sortedLyricData = lyricData.Lyrics.OrderBy(x => x.StartTime).ToList();
if (sortedLyricData.Count == 0)
{
@@ -100,99 +68,10 @@ public class LrcLyricParser : ILyricParser
for (int i = 0; i < sortedLyricData.Count; i++)
{
- var timeData = sortedLyricData[i].TimeTags.First().Value;
- if (timeData is null)
- {
- continue;
- }
-
- long ticks = TimeSpan.FromMilliseconds(timeData.Value).Ticks;
+ long ticks = TimeSpan.FromMilliseconds(sortedLyricData[i].StartTime).Ticks;
lyricList.Add(new LyricLine(sortedLyricData[i].Text.Trim(), ticks));
}
- if (fileMetaData.Count != 0)
- {
- // Map metaData values from LRC file to LyricMetadata properties
- LyricMetadata lyricMetadata = MapMetadataValues(fileMetaData);
-
- return new LyricDto { Metadata = lyricMetadata, Lyrics = lyricList };
- }
-
return new LyricDto { Lyrics = lyricList };
}
-
- /// <summary>
- /// Converts metadata from an LRC file to LyricMetadata properties.
- /// </summary>
- /// <param name="metaData">The metadata from the LRC file.</param>
- /// <returns>A lyricMetadata object with mapped property data.</returns>
- private static LyricMetadata MapMetadataValues(Dictionary<string, string> metaData)
- {
- LyricMetadata lyricMetadata = new();
-
- if (metaData.TryGetValue("ar", out var artist) && !string.IsNullOrEmpty(artist))
- {
- lyricMetadata.Artist = artist;
- }
-
- if (metaData.TryGetValue("al", out var album) && !string.IsNullOrEmpty(album))
- {
- lyricMetadata.Album = album;
- }
-
- if (metaData.TryGetValue("ti", out var title) && !string.IsNullOrEmpty(title))
- {
- lyricMetadata.Title = title;
- }
-
- if (metaData.TryGetValue("au", out var author) && !string.IsNullOrEmpty(author))
- {
- lyricMetadata.Author = author;
- }
-
- if (metaData.TryGetValue("length", out var length) && !string.IsNullOrEmpty(length))
- {
- if (DateTime.TryParseExact(length, _acceptedTimeFormats, null, DateTimeStyles.None, out var value))
- {
- lyricMetadata.Length = value.TimeOfDay.Ticks;
- }
- }
-
- if (metaData.TryGetValue("by", out var by) && !string.IsNullOrEmpty(by))
- {
- lyricMetadata.By = by;
- }
-
- if (metaData.TryGetValue("offset", out var offset) && !string.IsNullOrEmpty(offset))
- {
- if (int.TryParse(offset, out var value))
- {
- lyricMetadata.Offset = TimeSpan.FromMilliseconds(value).Ticks;
- }
- }
-
- if (metaData.TryGetValue("re", out var creator) && !string.IsNullOrEmpty(creator))
- {
- lyricMetadata.Creator = creator;
- }
-
- if (metaData.TryGetValue("ve", out var version) && !string.IsNullOrEmpty(version))
- {
- lyricMetadata.Version = version;
- }
-
- return lyricMetadata;
- }
-
- private static string GetMetadataFieldName(string metaDataRow, int index)
- {
- var metadataFieldName = metaDataRow.AsSpan(1, index - 1).Trim();
- return metadataFieldName.IsEmpty ? string.Empty : metadataFieldName.ToString();
- }
-
- private static string GetMetadataValue(string metaDataRow, int index)
- {
- var metadataValue = metaDataRow.AsSpan(index + 1, metaDataRow.Length - index - 2).Trim();
- return metadataValue.IsEmpty ? string.Empty : metadataValue.ToString();
- }
}
diff --git a/MediaBrowser.Providers/Lyric/LyricScheduledTask.cs b/MediaBrowser.Providers/Lyric/LyricScheduledTask.cs
index 1a4136504..89d71e172 100644
--- a/MediaBrowser.Providers/Lyric/LyricScheduledTask.cs
+++ b/MediaBrowser.Providers/Lyric/LyricScheduledTask.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
diff --git a/MediaBrowser.Providers/Plugins/AudioDb/Configuration/config.html b/MediaBrowser.Providers/Plugins/AudioDb/Configuration/config.html
index 2093effca..2d3e32f1b 100644
--- a/MediaBrowser.Providers/Plugins/AudioDb/Configuration/config.html
+++ b/MediaBrowser.Providers/Plugins/AudioDb/Configuration/config.html
@@ -1,4 +1,4 @@
-<!DOCTYPE html>
+<!DOCTYPE html>
<html>
<head>
<title>TheAudioDB</title>
diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/config.html b/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/config.html
index 24f2ac0ca..88c7c7d93 100644
--- a/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/config.html
+++ b/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/config.html
@@ -1,4 +1,4 @@
-<!DOCTYPE html>
+<!DOCTYPE html>
<html>
<head>
<title>MusicBrainz</title>
diff --git a/MediaBrowser.Providers/Plugins/Omdb/Configuration/config.html b/MediaBrowser.Providers/Plugins/Omdb/Configuration/config.html
index d00c1f9f8..e59c3cf5f 100644
--- a/MediaBrowser.Providers/Plugins/Omdb/Configuration/config.html
+++ b/MediaBrowser.Providers/Plugins/Omdb/Configuration/config.html
@@ -1,4 +1,4 @@
-<!DOCTYPE html>
+<!DOCTYPE html>
<html>
<head>
<title>OMDb</title>
diff --git a/MediaBrowser.Providers/Plugins/StudioImages/Configuration/PluginConfiguration.cs b/MediaBrowser.Providers/Plugins/StudioImages/Configuration/PluginConfiguration.cs
index 0bfab9824..7e3ec86e8 100644
--- a/MediaBrowser.Providers/Plugins/StudioImages/Configuration/PluginConfiguration.cs
+++ b/MediaBrowser.Providers/Plugins/StudioImages/Configuration/PluginConfiguration.cs
@@ -1,4 +1,4 @@
-using MediaBrowser.Model.Plugins;
+using MediaBrowser.Model.Plugins;
namespace MediaBrowser.Providers.Plugins.StudioImages.Configuration
{
diff --git a/MediaBrowser.Providers/Plugins/StudioImages/Configuration/config.html b/MediaBrowser.Providers/Plugins/StudioImages/Configuration/config.html
index 85ebe443f..5a4a3299b 100644
--- a/MediaBrowser.Providers/Plugins/StudioImages/Configuration/config.html
+++ b/MediaBrowser.Providers/Plugins/StudioImages/Configuration/config.html
@@ -1,4 +1,4 @@
-<!DOCTYPE html>
+<!DOCTYPE html>
<html>
<head>
<title>Studio Images</title>
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs b/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs
index d704a5f49..4916a95d9 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Globalization;
using System.Threading;
diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
index f2681500b..a8800431e 100644
--- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
+++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
@@ -306,12 +306,15 @@ namespace MediaBrowser.XbmcMetadata.Parsers
break;
case "watched":
var played = reader.ReadElementContentAsBoolean();
- if (!string.IsNullOrWhiteSpace(nfoConfiguration.UserId))
+ if (Guid.TryParse(nfoConfiguration.UserId, out var userId))
{
- var user = _userManager.GetUserById(Guid.Parse(nfoConfiguration.UserId));
- userData = _userDataManager.GetUserData(user, item);
- userData.Played = played;
- _userDataManager.SaveUserData(user, item, userData, UserDataSaveReason.Import, CancellationToken.None);
+ var user = _userManager.GetUserById(userId);
+ if (user is not null)
+ {
+ userData = _userDataManager.GetUserData(user, item);
+ userData.Played = played;
+ _userDataManager.SaveUserData(user, item, userData, UserDataSaveReason.Import, CancellationToken.None);
+ }
}
break;
@@ -320,9 +323,12 @@ namespace MediaBrowser.XbmcMetadata.Parsers
&& Guid.TryParse(nfoConfiguration.UserId, out var playCountUserId))
{
var user = _userManager.GetUserById(playCountUserId);
- userData = _userDataManager.GetUserData(user, item);
- userData.PlayCount = count;
- _userDataManager.SaveUserData(user, item, userData, UserDataSaveReason.Import, CancellationToken.None);
+ if (user is not null)
+ {
+ userData = _userDataManager.GetUserData(user, item);
+ userData.PlayCount = count;
+ _userDataManager.SaveUserData(user, item, userData, UserDataSaveReason.Import, CancellationToken.None);
+ }
}
break;
@@ -331,9 +337,12 @@ namespace MediaBrowser.XbmcMetadata.Parsers
&& Guid.TryParse(nfoConfiguration.UserId, out var lastPlayedUserId))
{
var user = _userManager.GetUserById(lastPlayedUserId);
- userData = _userDataManager.GetUserData(user, item);
- userData.LastPlayedDate = lastPlayed;
- _userDataManager.SaveUserData(user, item, userData, UserDataSaveReason.Import, CancellationToken.None);
+ if (user is not null)
+ {
+ userData = _userDataManager.GetUserData(user, item);
+ userData.LastPlayedDate = lastPlayed;
+ _userDataManager.SaveUserData(user, item, userData, UserDataSaveReason.Import, CancellationToken.None);
+ }
}
break;
diff --git a/fuzz/Emby.Server.Implementations.Fuzz/Program.cs b/fuzz/Emby.Server.Implementations.Fuzz/Program.cs
index 1571b5ab0..3133b6288 100644
--- a/fuzz/Emby.Server.Implementations.Fuzz/Program.cs
+++ b/fuzz/Emby.Server.Implementations.Fuzz/Program.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using AutoFixture;
using AutoFixture.AutoMoq;
using Emby.Server.Implementations.Data;
diff --git a/fuzz/Jellyfin.Api.Fuzz/Program.cs b/fuzz/Jellyfin.Api.Fuzz/Program.cs
index 6713322ac..c5eaf17e8 100644
--- a/fuzz/Jellyfin.Api.Fuzz/Program.cs
+++ b/fuzz/Jellyfin.Api.Fuzz/Program.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using Jellyfin.Api.Middleware;
using Microsoft.AspNetCore.Http;
diff --git a/global.json b/global.json
index 9db4b532c..dbf2988d5 100644
--- a/global.json
+++ b/global.json
@@ -1,4 +1,4 @@
-{
+{
"sdk": {
"version": "8.0.0",
"rollForward": "latestMinor"
diff --git a/src/Jellyfin.Extensions/GuidExtensions.cs b/src/Jellyfin.Extensions/GuidExtensions.cs
index 95c591a82..b30cdafb0 100644
--- a/src/Jellyfin.Extensions/GuidExtensions.cs
+++ b/src/Jellyfin.Extensions/GuidExtensions.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Diagnostics.CodeAnalysis;
namespace Jellyfin.Extensions;
diff --git a/src/Jellyfin.Extensions/Json/Converters/JsonBoolNumberConverter.cs b/src/Jellyfin.Extensions/Json/Converters/JsonBoolNumberConverter.cs
index c2543cf7c..f86bd17b5 100644
--- a/src/Jellyfin.Extensions/Json/Converters/JsonBoolNumberConverter.cs
+++ b/src/Jellyfin.Extensions/Json/Converters/JsonBoolNumberConverter.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Text.Json;
using System.Text.Json.Serialization;
diff --git a/src/Jellyfin.Extensions/Json/Converters/JsonBoolStringConverter.cs b/src/Jellyfin.Extensions/Json/Converters/JsonBoolStringConverter.cs
index 6895eadb8..db4f10092 100644
--- a/src/Jellyfin.Extensions/Json/Converters/JsonBoolStringConverter.cs
+++ b/src/Jellyfin.Extensions/Json/Converters/JsonBoolStringConverter.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Buffers;
using System.Buffers.Text;
using System.Text.Json;
diff --git a/src/Jellyfin.Extensions/Json/Converters/JsonCommaDelimitedArrayConverter.cs b/src/Jellyfin.Extensions/Json/Converters/JsonCommaDelimitedArrayConverter.cs
index 0d0cc2d06..ccbc296fd 100644
--- a/src/Jellyfin.Extensions/Json/Converters/JsonCommaDelimitedArrayConverter.cs
+++ b/src/Jellyfin.Extensions/Json/Converters/JsonCommaDelimitedArrayConverter.cs
@@ -1,4 +1,4 @@
-namespace Jellyfin.Extensions.Json.Converters
+namespace Jellyfin.Extensions.Json.Converters
{
/// <summary>
/// Convert comma delimited string to array of type.
diff --git a/src/Jellyfin.Extensions/Json/Converters/JsonCommaDelimitedArrayConverterFactory.cs b/src/Jellyfin.Extensions/Json/Converters/JsonCommaDelimitedArrayConverterFactory.cs
index cc9311a24..a95e493db 100644
--- a/src/Jellyfin.Extensions/Json/Converters/JsonCommaDelimitedArrayConverterFactory.cs
+++ b/src/Jellyfin.Extensions/Json/Converters/JsonCommaDelimitedArrayConverterFactory.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Text.Json;
using System.Text.Json.Serialization;
diff --git a/src/Jellyfin.Extensions/Json/Converters/JsonDateTimeConverter.cs b/src/Jellyfin.Extensions/Json/Converters/JsonDateTimeConverter.cs
index 8ae080b15..8248db0ee 100644
--- a/src/Jellyfin.Extensions/Json/Converters/JsonDateTimeConverter.cs
+++ b/src/Jellyfin.Extensions/Json/Converters/JsonDateTimeConverter.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Globalization;
using System.Text.Json;
using System.Text.Json.Serialization;
diff --git a/src/Jellyfin.Extensions/Json/Converters/JsonDelimitedArrayConverter.cs b/src/Jellyfin.Extensions/Json/Converters/JsonDelimitedArrayConverter.cs
index 17096c017..1466d3a71 100644
--- a/src/Jellyfin.Extensions/Json/Converters/JsonDelimitedArrayConverter.cs
+++ b/src/Jellyfin.Extensions/Json/Converters/JsonDelimitedArrayConverter.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.ComponentModel;
using System.Text.Json;
using System.Text.Json.Serialization;
diff --git a/src/Jellyfin.Extensions/Json/Converters/JsonFlagEnumConverter.cs b/src/Jellyfin.Extensions/Json/Converters/JsonFlagEnumConverter.cs
index 4fa91fa5e..6a0912546 100644
--- a/src/Jellyfin.Extensions/Json/Converters/JsonFlagEnumConverter.cs
+++ b/src/Jellyfin.Extensions/Json/Converters/JsonFlagEnumConverter.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Text.Json;
using System.Text.Json.Serialization;
diff --git a/src/Jellyfin.Extensions/Json/Converters/JsonFlagEnumConverterFactory.cs b/src/Jellyfin.Extensions/Json/Converters/JsonFlagEnumConverterFactory.cs
index b74caf345..7e870a747 100644
--- a/src/Jellyfin.Extensions/Json/Converters/JsonFlagEnumConverterFactory.cs
+++ b/src/Jellyfin.Extensions/Json/Converters/JsonFlagEnumConverterFactory.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;
diff --git a/src/Jellyfin.Extensions/Json/Converters/JsonNullableStructConverter.cs b/src/Jellyfin.Extensions/Json/Converters/JsonNullableStructConverter.cs
index 94004fa49..1b94782f2 100644
--- a/src/Jellyfin.Extensions/Json/Converters/JsonNullableStructConverter.cs
+++ b/src/Jellyfin.Extensions/Json/Converters/JsonNullableStructConverter.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Text.Json;
using System.Text.Json.Serialization;
diff --git a/src/Jellyfin.Extensions/Json/Converters/JsonNullableStructConverterFactory.cs b/src/Jellyfin.Extensions/Json/Converters/JsonNullableStructConverterFactory.cs
index e7749589a..e48496a12 100644
--- a/src/Jellyfin.Extensions/Json/Converters/JsonNullableStructConverterFactory.cs
+++ b/src/Jellyfin.Extensions/Json/Converters/JsonNullableStructConverterFactory.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Text.Json;
using System.Text.Json.Serialization;
diff --git a/src/Jellyfin.Extensions/Json/Converters/JsonPipeDelimitedArrayConverter.cs b/src/Jellyfin.Extensions/Json/Converters/JsonPipeDelimitedArrayConverter.cs
index 6e59fe464..55720ee4f 100644
--- a/src/Jellyfin.Extensions/Json/Converters/JsonPipeDelimitedArrayConverter.cs
+++ b/src/Jellyfin.Extensions/Json/Converters/JsonPipeDelimitedArrayConverter.cs
@@ -1,4 +1,4 @@
-namespace Jellyfin.Extensions.Json.Converters
+namespace Jellyfin.Extensions.Json.Converters
{
/// <summary>
/// Convert Pipe delimited string to array of type.
diff --git a/src/Jellyfin.Extensions/Json/Converters/JsonPipeDelimitedArrayConverterFactory.cs b/src/Jellyfin.Extensions/Json/Converters/JsonPipeDelimitedArrayConverterFactory.cs
index 579674f18..ae9e1f67a 100644
--- a/src/Jellyfin.Extensions/Json/Converters/JsonPipeDelimitedArrayConverterFactory.cs
+++ b/src/Jellyfin.Extensions/Json/Converters/JsonPipeDelimitedArrayConverterFactory.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Text.Json;
using System.Text.Json.Serialization;
diff --git a/src/Jellyfin.Extensions/Json/Converters/JsonStringConverter.cs b/src/Jellyfin.Extensions/Json/Converters/JsonStringConverter.cs
index 36b36c9b4..4280ee85e 100644
--- a/src/Jellyfin.Extensions/Json/Converters/JsonStringConverter.cs
+++ b/src/Jellyfin.Extensions/Json/Converters/JsonStringConverter.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Buffers;
using System.Text;
using System.Text.Json;
diff --git a/src/Jellyfin.Extensions/Json/Converters/JsonVersionConverter.cs b/src/Jellyfin.Extensions/Json/Converters/JsonVersionConverter.cs
index 51ffec1cb..183803ae8 100644
--- a/src/Jellyfin.Extensions/Json/Converters/JsonVersionConverter.cs
+++ b/src/Jellyfin.Extensions/Json/Converters/JsonVersionConverter.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Text.Json;
using System.Text.Json.Serialization;
diff --git a/src/Jellyfin.LiveTv/Configuration/LiveTvConfigurationExtensions.cs b/src/Jellyfin.LiveTv/Configuration/LiveTvConfigurationExtensions.cs
index f7888496f..b8e30a4a1 100644
--- a/src/Jellyfin.LiveTv/Configuration/LiveTvConfigurationExtensions.cs
+++ b/src/Jellyfin.LiveTv/Configuration/LiveTvConfigurationExtensions.cs
@@ -1,4 +1,4 @@
-using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.LiveTv;
diff --git a/src/Jellyfin.LiveTv/Extensions/LiveTvServiceCollectionExtensions.cs b/src/Jellyfin.LiveTv/Extensions/LiveTvServiceCollectionExtensions.cs
index 73729c950..ed72badbc 100644
--- a/src/Jellyfin.LiveTv/Extensions/LiveTvServiceCollectionExtensions.cs
+++ b/src/Jellyfin.LiveTv/Extensions/LiveTvServiceCollectionExtensions.cs
@@ -1,4 +1,4 @@
-using Jellyfin.LiveTv.Channels;
+using Jellyfin.LiveTv.Channels;
using Jellyfin.LiveTv.Guide;
using Jellyfin.LiveTv.IO;
using Jellyfin.LiveTv.Listings;
diff --git a/src/Jellyfin.LiveTv/Listings/ListingsManager.cs b/src/Jellyfin.LiveTv/Listings/ListingsManager.cs
index dfd376092..3df2d0d2c 100644
--- a/src/Jellyfin.LiveTv/Listings/ListingsManager.cs
+++ b/src/Jellyfin.LiveTv/Listings/ListingsManager.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
diff --git a/src/Jellyfin.LiveTv/TunerHosts/TunerHostManager.cs b/src/Jellyfin.LiveTv/TunerHosts/TunerHostManager.cs
index 473f00153..d67f77bc0 100644
--- a/src/Jellyfin.LiveTv/TunerHosts/TunerHostManager.cs
+++ b/src/Jellyfin.LiveTv/TunerHosts/TunerHostManager.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
diff --git a/src/Jellyfin.MediaEncoding.Hls/Cache/CacheDecorator.cs b/src/Jellyfin.MediaEncoding.Hls/Cache/CacheDecorator.cs
index 360a96290..127f4079c 100644
--- a/src/Jellyfin.MediaEncoding.Hls/Cache/CacheDecorator.cs
+++ b/src/Jellyfin.MediaEncoding.Hls/Cache/CacheDecorator.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
diff --git a/src/Jellyfin.MediaEncoding.Hls/Extensions/MediaEncodingHlsServiceCollectionExtensions.cs b/src/Jellyfin.MediaEncoding.Hls/Extensions/MediaEncodingHlsServiceCollectionExtensions.cs
index 8ed4edcea..5b0ea686d 100644
--- a/src/Jellyfin.MediaEncoding.Hls/Extensions/MediaEncodingHlsServiceCollectionExtensions.cs
+++ b/src/Jellyfin.MediaEncoding.Hls/Extensions/MediaEncodingHlsServiceCollectionExtensions.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using Jellyfin.MediaEncoding.Hls.Cache;
using Jellyfin.MediaEncoding.Hls.Extractors;
using Jellyfin.MediaEncoding.Hls.Playlist;
diff --git a/src/Jellyfin.MediaEncoding.Hls/Extractors/FfProbeKeyframeExtractor.cs b/src/Jellyfin.MediaEncoding.Hls/Extractors/FfProbeKeyframeExtractor.cs
index f86599a23..a8daeeb78 100644
--- a/src/Jellyfin.MediaEncoding.Hls/Extractors/FfProbeKeyframeExtractor.cs
+++ b/src/Jellyfin.MediaEncoding.Hls/Extractors/FfProbeKeyframeExtractor.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using Emby.Naming.Common;
diff --git a/src/Jellyfin.MediaEncoding.Hls/Extractors/MatroskaKeyframeExtractor.cs b/src/Jellyfin.MediaEncoding.Hls/Extractors/MatroskaKeyframeExtractor.cs
index 4bc537c0e..1100f8cd5 100644
--- a/src/Jellyfin.MediaEncoding.Hls/Extractors/MatroskaKeyframeExtractor.cs
+++ b/src/Jellyfin.MediaEncoding.Hls/Extractors/MatroskaKeyframeExtractor.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Diagnostics.CodeAnalysis;
using Jellyfin.MediaEncoding.Keyframes;
using Microsoft.Extensions.Logging;
diff --git a/src/Jellyfin.MediaEncoding.Hls/Playlist/CreateMainPlaylistRequest.cs b/src/Jellyfin.MediaEncoding.Hls/Playlist/CreateMainPlaylistRequest.cs
index 8572a5eae..21d9bb658 100644
--- a/src/Jellyfin.MediaEncoding.Hls/Playlist/CreateMainPlaylistRequest.cs
+++ b/src/Jellyfin.MediaEncoding.Hls/Playlist/CreateMainPlaylistRequest.cs
@@ -1,4 +1,4 @@
-namespace Jellyfin.MediaEncoding.Hls.Playlist;
+namespace Jellyfin.MediaEncoding.Hls.Playlist;
/// <summary>
/// Request class for the <see cref="IDynamicHlsPlaylistGenerator.CreateMainPlaylist(CreateMainPlaylistRequest)"/> method.
diff --git a/src/Jellyfin.MediaEncoding.Hls/Playlist/DynamicHlsPlaylistGenerator.cs b/src/Jellyfin.MediaEncoding.Hls/Playlist/DynamicHlsPlaylistGenerator.cs
index 07a6d6050..9a023d7ed 100644
--- a/src/Jellyfin.MediaEncoding.Hls/Playlist/DynamicHlsPlaylistGenerator.cs
+++ b/src/Jellyfin.MediaEncoding.Hls/Playlist/DynamicHlsPlaylistGenerator.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
diff --git a/src/Jellyfin.MediaEncoding.Hls/Playlist/IDynamicHlsPlaylistGenerator.cs b/src/Jellyfin.MediaEncoding.Hls/Playlist/IDynamicHlsPlaylistGenerator.cs
index 2626cb2dd..32432e3ac 100644
--- a/src/Jellyfin.MediaEncoding.Hls/Playlist/IDynamicHlsPlaylistGenerator.cs
+++ b/src/Jellyfin.MediaEncoding.Hls/Playlist/IDynamicHlsPlaylistGenerator.cs
@@ -1,4 +1,4 @@
-namespace Jellyfin.MediaEncoding.Hls.Playlist;
+namespace Jellyfin.MediaEncoding.Hls.Playlist;
/// <summary>
/// Generator for dynamic HLS playlists where the segment lengths aren't known in advance.
diff --git a/src/Jellyfin.MediaEncoding.Hls/ScheduledTasks/KeyframeExtractionScheduledTask.cs b/src/Jellyfin.MediaEncoding.Hls/ScheduledTasks/KeyframeExtractionScheduledTask.cs
index a7d52184b..caf6a2aae 100644
--- a/src/Jellyfin.MediaEncoding.Hls/ScheduledTasks/KeyframeExtractionScheduledTask.cs
+++ b/src/Jellyfin.MediaEncoding.Hls/ScheduledTasks/KeyframeExtractionScheduledTask.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
diff --git a/src/Jellyfin.MediaEncoding.Keyframes/FfProbe/FfProbeKeyframeExtractor.cs b/src/Jellyfin.MediaEncoding.Keyframes/FfProbe/FfProbeKeyframeExtractor.cs
index 720d987f1..570c0b9d0 100644
--- a/src/Jellyfin.MediaEncoding.Keyframes/FfProbe/FfProbeKeyframeExtractor.cs
+++ b/src/Jellyfin.MediaEncoding.Keyframes/FfProbe/FfProbeKeyframeExtractor.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
diff --git a/src/Jellyfin.MediaEncoding.Keyframes/FfTool/FfToolKeyframeExtractor.cs b/src/Jellyfin.MediaEncoding.Keyframes/FfTool/FfToolKeyframeExtractor.cs
index aaaca6fe1..89d94a061 100644
--- a/src/Jellyfin.MediaEncoding.Keyframes/FfTool/FfToolKeyframeExtractor.cs
+++ b/src/Jellyfin.MediaEncoding.Keyframes/FfTool/FfToolKeyframeExtractor.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
namespace Jellyfin.MediaEncoding.Keyframes.FfTool;
diff --git a/src/Jellyfin.MediaEncoding.Keyframes/KeyframeData.cs b/src/Jellyfin.MediaEncoding.Keyframes/KeyframeData.cs
index 06f9180e7..1c2177bfc 100644
--- a/src/Jellyfin.MediaEncoding.Keyframes/KeyframeData.cs
+++ b/src/Jellyfin.MediaEncoding.Keyframes/KeyframeData.cs
@@ -1,4 +1,4 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
namespace Jellyfin.MediaEncoding.Keyframes;
diff --git a/src/Jellyfin.MediaEncoding.Keyframes/Matroska/MatroskaConstants.cs b/src/Jellyfin.MediaEncoding.Keyframes/Matroska/MatroskaConstants.cs
index 0d5c2f34f..b20ed7cd4 100644
--- a/src/Jellyfin.MediaEncoding.Keyframes/Matroska/MatroskaConstants.cs
+++ b/src/Jellyfin.MediaEncoding.Keyframes/Matroska/MatroskaConstants.cs
@@ -1,4 +1,4 @@
-namespace Jellyfin.MediaEncoding.Keyframes.Matroska;
+namespace Jellyfin.MediaEncoding.Keyframes.Matroska;
/// <summary>
/// Constants for the Matroska identifiers.
diff --git a/src/Jellyfin.MediaEncoding.Keyframes/Matroska/MatroskaKeyframeExtractor.cs b/src/Jellyfin.MediaEncoding.Keyframes/Matroska/MatroskaKeyframeExtractor.cs
index 501b2bb17..5c5e90fbe 100644
--- a/src/Jellyfin.MediaEncoding.Keyframes/Matroska/MatroskaKeyframeExtractor.cs
+++ b/src/Jellyfin.MediaEncoding.Keyframes/Matroska/MatroskaKeyframeExtractor.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.IO;
using Jellyfin.MediaEncoding.Keyframes.Matroska.Extensions;
diff --git a/src/Jellyfin.MediaEncoding.Keyframes/Matroska/Models/Info.cs b/src/Jellyfin.MediaEncoding.Keyframes/Matroska/Models/Info.cs
index 415d6da00..b2179ddb9 100644
--- a/src/Jellyfin.MediaEncoding.Keyframes/Matroska/Models/Info.cs
+++ b/src/Jellyfin.MediaEncoding.Keyframes/Matroska/Models/Info.cs
@@ -1,4 +1,4 @@
-namespace Jellyfin.MediaEncoding.Keyframes.Matroska.Models;
+namespace Jellyfin.MediaEncoding.Keyframes.Matroska.Models;
/// <summary>
/// The matroska Info segment.
diff --git a/src/Jellyfin.MediaEncoding.Keyframes/Matroska/Models/SeekHead.cs b/src/Jellyfin.MediaEncoding.Keyframes/Matroska/Models/SeekHead.cs
index 95e4fd882..b2ac1952e 100644
--- a/src/Jellyfin.MediaEncoding.Keyframes/Matroska/Models/SeekHead.cs
+++ b/src/Jellyfin.MediaEncoding.Keyframes/Matroska/Models/SeekHead.cs
@@ -1,4 +1,4 @@
-namespace Jellyfin.MediaEncoding.Keyframes.Matroska.Models;
+namespace Jellyfin.MediaEncoding.Keyframes.Matroska.Models;
/// <summary>
/// The matroska SeekHead segment. All positions are relative to the Segment container.
diff --git a/src/Jellyfin.Networking/AutoDiscoveryHost.cs b/src/Jellyfin.Networking/AutoDiscoveryHost.cs
index ff0d179b9..65a7fdd40 100644
--- a/src/Jellyfin.Networking/AutoDiscoveryHost.cs
+++ b/src/Jellyfin.Networking/AutoDiscoveryHost.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
diff --git a/tests/Jellyfin.Api.Tests/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandlerTests.cs b/tests/Jellyfin.Api.Tests/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandlerTests.cs
index f4f661147..162a022f5 100644
--- a/tests/Jellyfin.Api.Tests/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandlerTests.cs
+++ b/tests/Jellyfin.Api.Tests/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandlerTests.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Net;
using System.Security.Claims;
diff --git a/tests/Jellyfin.Api.Tests/Auth/IgnoreSchedulePolicy/IgnoreScheduleHandlerTests.cs b/tests/Jellyfin.Api.Tests/Auth/IgnoreSchedulePolicy/IgnoreScheduleHandlerTests.cs
index 9cf8f8548..534d1863c 100644
--- a/tests/Jellyfin.Api.Tests/Auth/IgnoreSchedulePolicy/IgnoreScheduleHandlerTests.cs
+++ b/tests/Jellyfin.Api.Tests/Auth/IgnoreSchedulePolicy/IgnoreScheduleHandlerTests.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using AutoFixture;
diff --git a/tests/Jellyfin.Api.Tests/TestHelpers.cs b/tests/Jellyfin.Api.Tests/TestHelpers.cs
index 5eab3ae6f..12cf025bc 100644
--- a/tests/Jellyfin.Api.Tests/TestHelpers.cs
+++ b/tests/Jellyfin.Api.Tests/TestHelpers.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Globalization;
using System.Net;
diff --git a/tests/Jellyfin.Common.Tests/Providers/ProviderIdParserTests.cs b/tests/Jellyfin.Common.Tests/Providers/ProviderIdParserTests.cs
index ef9d31cc1..c869493ce 100644
--- a/tests/Jellyfin.Common.Tests/Providers/ProviderIdParserTests.cs
+++ b/tests/Jellyfin.Common.Tests/Providers/ProviderIdParserTests.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using MediaBrowser.Common.Providers;
using Xunit;
diff --git a/tests/Jellyfin.Controller.Tests/DirectoryServiceTests.cs b/tests/Jellyfin.Controller.Tests/DirectoryServiceTests.cs
index 83a023384..07b53bf74 100644
--- a/tests/Jellyfin.Controller.Tests/DirectoryServiceTests.cs
+++ b/tests/Jellyfin.Controller.Tests/DirectoryServiceTests.cs
@@ -1,4 +1,4 @@
-using System.Linq;
+using System.Linq;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.IO;
using Moq;
diff --git a/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonFlagEnumTests.cs b/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonFlagEnumTests.cs
index c8652b323..7a57cf795 100644
--- a/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonFlagEnumTests.cs
+++ b/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonFlagEnumTests.cs
@@ -1,4 +1,4 @@
-using System.Text.Json;
+using System.Text.Json;
using Jellyfin.Extensions.Json.Converters;
using MediaBrowser.Model.Session;
using Xunit;
diff --git a/tests/Jellyfin.LiveTv.Tests/Jellyfin.LiveTv.Tests.csproj b/tests/Jellyfin.LiveTv.Tests/Jellyfin.LiveTv.Tests.csproj
index f645f38c4..cf967b84c 100644
--- a/tests/Jellyfin.LiveTv.Tests/Jellyfin.LiveTv.Tests.csproj
+++ b/tests/Jellyfin.LiveTv.Tests/Jellyfin.LiveTv.Tests.csproj
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
diff --git a/tests/Jellyfin.MediaEncoding.Hls.Tests/Playlist/DynamicHlsPlaylistGeneratorTests.cs b/tests/Jellyfin.MediaEncoding.Hls.Tests/Playlist/DynamicHlsPlaylistGeneratorTests.cs
index bbacdcd62..fc969527e 100644
--- a/tests/Jellyfin.MediaEncoding.Hls.Tests/Playlist/DynamicHlsPlaylistGeneratorTests.cs
+++ b/tests/Jellyfin.MediaEncoding.Hls.Tests/Playlist/DynamicHlsPlaylistGeneratorTests.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using Jellyfin.MediaEncoding.Hls.Playlist;
using Jellyfin.MediaEncoding.Keyframes;
using Xunit;
diff --git a/tests/Jellyfin.MediaEncoding.Keyframes.Tests/FfProbe/FfProbeKeyframeExtractorTests.cs b/tests/Jellyfin.MediaEncoding.Keyframes.Tests/FfProbe/FfProbeKeyframeExtractorTests.cs
index e5debf8c0..b7515c815 100644
--- a/tests/Jellyfin.MediaEncoding.Keyframes.Tests/FfProbe/FfProbeKeyframeExtractorTests.cs
+++ b/tests/Jellyfin.MediaEncoding.Keyframes.Tests/FfProbe/FfProbeKeyframeExtractorTests.cs
@@ -1,4 +1,4 @@
-using System.IO;
+using System.IO;
using System.Text.Json;
using Xunit;
diff --git a/tests/Jellyfin.MediaEncoding.Keyframes.Tests/FfProbe/Test Data/keyframes.txt b/tests/Jellyfin.MediaEncoding.Keyframes.Tests/FfProbe/Test Data/keyframes.txt
index a6d1b420c..6628bbaab 100644
--- a/tests/Jellyfin.MediaEncoding.Keyframes.Tests/FfProbe/Test Data/keyframes.txt
+++ b/tests/Jellyfin.MediaEncoding.Keyframes.Tests/FfProbe/Test Data/keyframes.txt
@@ -1,4 +1,4 @@
-packet,0.000000,K_
+packet,0.000000,K_
packet,0.375000,__
packet,0.167000,__
packet,0.042000,__
diff --git a/tests/Jellyfin.MediaEncoding.Keyframes.Tests/FfProbe/Test Data/keyframes_result.json b/tests/Jellyfin.MediaEncoding.Keyframes.Tests/FfProbe/Test Data/keyframes_result.json
index 72bf552cf..cb244a92c 100644
--- a/tests/Jellyfin.MediaEncoding.Keyframes.Tests/FfProbe/Test Data/keyframes_result.json
+++ b/tests/Jellyfin.MediaEncoding.Keyframes.Tests/FfProbe/Test Data/keyframes_result.json
@@ -1 +1 @@
-{"TotalDuration":7063360000,"KeyframeTicks":[0,103850000,133880000,145150000,165580000,186440000,196450000,209790000,314060000,326990000,396230000,407070000,432520000,476310000,523020000,535540000,550550000,631050000,646480000,665670000,686520000,732400000,772020000,796210000,856690000,887970000,903820000,934270000,983070000,1056060000,1087750000,1187850000,1222050000,1251250000,1265430000,1305470000,1333830000,1345510000,1356770000,1368450000,1427260000,1460630000,1500670000,1540710000,1584500000,1607020000,1627880000,1639550000,1672090000,1685020000,1789290000,1883130000,1909820000,1931510000,1996580000,2017020000,2035370000,2051220000,2065400000,2085000000,2109190000,2120870000,2168420000,2253920000,2295210000,2374460000,2478730000,2582160000,2607190000,2697280000,2783610000,2825320000,2899560000,2929590000,2979230000,3017600000,3048880000,3073490000,3117700000,3141050000,3158160000,3200700000,3279530000,3299960000,3312890000,3332910000,3369200000,3379630000,3438440000,3459290000,3490990000,3533110000,3562730000,3600260000,3624040000,3672000000,3722050000,3753330000,3771270000,3875540000,3957290000,4016100000,4100350000,4114530000,4124540000,4157900000,4180430000,4200450000,4222550000,4252160000,4295960000,4309720000,4328070000,4340590000,4371450000,4400230000,4426920000,4489490000,4512010000,4531190000,4569570000,4599600000,4635460000,4660070000,4680930000,4729310000,4757670000,4777690000,4808550000,4824400000,4851100000,4864440000,4905320000,4955370000,4970380000,5074650000,5095090000,5109270000,5186010000,5204370000,5227720000,5242740000,5266930000,5342000000,5433760000,5447110000,5470470000,5520520000,5550550000,5565140000,5611020000,5642300000,5668160000,5711120000,5743240000,5762420000,5797460000,5817480000,5839170000,5855850000,5870870000,5904230000,5969300000,6056880000,6104850000,6152400000,6256250000,6295870000,6310050000,6325900000,6341750000,6356770000,6385960000,6426840000,6454780000,6469800000,6514420000,6549460000,6574480000,6602010000,6619530000,6654560000,6667080000,6690430000,6724630000,6762170000,6812220000,6849760000,6875200000,6912740000,6983230000,6994900000,7024930000]}
+{"TotalDuration":7063360000,"KeyframeTicks":[0,103850000,133880000,145150000,165580000,186440000,196450000,209790000,314060000,326990000,396230000,407070000,432520000,476310000,523020000,535540000,550550000,631050000,646480000,665670000,686520000,732400000,772020000,796210000,856690000,887970000,903820000,934270000,983070000,1056060000,1087750000,1187850000,1222050000,1251250000,1265430000,1305470000,1333830000,1345510000,1356770000,1368450000,1427260000,1460630000,1500670000,1540710000,1584500000,1607020000,1627880000,1639550000,1672090000,1685020000,1789290000,1883130000,1909820000,1931510000,1996580000,2017020000,2035370000,2051220000,2065400000,2085000000,2109190000,2120870000,2168420000,2253920000,2295210000,2374460000,2478730000,2582160000,2607190000,2697280000,2783610000,2825320000,2899560000,2929590000,2979230000,3017600000,3048880000,3073490000,3117700000,3141050000,3158160000,3200700000,3279530000,3299960000,3312890000,3332910000,3369200000,3379630000,3438440000,3459290000,3490990000,3533110000,3562730000,3600260000,3624040000,3672000000,3722050000,3753330000,3771270000,3875540000,3957290000,4016100000,4100350000,4114530000,4124540000,4157900000,4180430000,4200450000,4222550000,4252160000,4295960000,4309720000,4328070000,4340590000,4371450000,4400230000,4426920000,4489490000,4512010000,4531190000,4569570000,4599600000,4635460000,4660070000,4680930000,4729310000,4757670000,4777690000,4808550000,4824400000,4851100000,4864440000,4905320000,4955370000,4970380000,5074650000,5095090000,5109270000,5186010000,5204370000,5227720000,5242740000,5266930000,5342000000,5433760000,5447110000,5470470000,5520520000,5550550000,5565140000,5611020000,5642300000,5668160000,5711120000,5743240000,5762420000,5797460000,5817480000,5839170000,5855850000,5870870000,5904230000,5969300000,6056880000,6104850000,6152400000,6256250000,6295870000,6310050000,6325900000,6341750000,6356770000,6385960000,6426840000,6454780000,6469800000,6514420000,6549460000,6574480000,6602010000,6619530000,6654560000,6667080000,6690430000,6724630000,6762170000,6812220000,6849760000,6875200000,6912740000,6983230000,6994900000,7024930000]}
diff --git a/tests/Jellyfin.MediaEncoding.Keyframes.Tests/FfProbe/Test Data/keyframes_streamduration.txt b/tests/Jellyfin.MediaEncoding.Keyframes.Tests/FfProbe/Test Data/keyframes_streamduration.txt
index 36c9e5772..681f971e1 100644
--- a/tests/Jellyfin.MediaEncoding.Keyframes.Tests/FfProbe/Test Data/keyframes_streamduration.txt
+++ b/tests/Jellyfin.MediaEncoding.Keyframes.Tests/FfProbe/Test Data/keyframes_streamduration.txt
@@ -1,4 +1,4 @@
-packet,0.000000,K_
+packet,0.000000,K_
packet,0.375000,__
packet,0.167000,__
packet,0.042000,__
diff --git a/tests/Jellyfin.MediaEncoding.Keyframes.Tests/FfProbe/Test Data/keyframes_streamduration_result.json b/tests/Jellyfin.MediaEncoding.Keyframes.Tests/FfProbe/Test Data/keyframes_streamduration_result.json
index 05494ea4a..4bed34aa1 100644
--- a/tests/Jellyfin.MediaEncoding.Keyframes.Tests/FfProbe/Test Data/keyframes_streamduration_result.json
+++ b/tests/Jellyfin.MediaEncoding.Keyframes.Tests/FfProbe/Test Data/keyframes_streamduration_result.json
@@ -1 +1 @@
-{"TotalDuration":1000000000,"KeyframeTicks":[0,103850000,133880000,145150000,165580000,186440000,196450000,209790000,314060000,326990000,396230000,407070000,432520000,476310000,523020000,535540000,550550000,631050000,646480000,665670000,686520000,732400000,772020000,796210000,856690000,887970000,903820000,934270000,983070000]}
+{"TotalDuration":1000000000,"KeyframeTicks":[0,103850000,133880000,145150000,165580000,186440000,196450000,209790000,314060000,326990000,396230000,407070000,432520000,476310000,523020000,535540000,550550000,631050000,646480000,665670000,686520000,732400000,772020000,796210000,856690000,887970000,903820000,934270000,983070000]}
diff --git a/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs b/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs
index 612064190..df51d39cb 100644
--- a/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs
+++ b/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs
@@ -84,6 +84,7 @@ namespace Jellyfin.MediaEncoding.Tests.Probing
Assert.Equal(0, res.VideoStream.ElPresentFlag);
Assert.Equal(1, res.VideoStream.BlPresentFlag);
Assert.Equal(0, res.VideoStream.DvBlSignalCompatibilityId);
+ Assert.Equal(-180, res.VideoStream.Rotation);
var audio1 = res.MediaStreams[1];
Assert.Equal("eac3", audio1.Codec);
diff --git a/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_metadata.json b/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_metadata.json
index a49c68690..df41ab16e 100644
--- a/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_metadata.json
+++ b/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_metadata.json
@@ -59,6 +59,10 @@
"el_present_flag": 0,
"bl_present_flag": 1,
"dv_bl_signal_compatibility_id": 0
+ },
+ {
+ "side_data_type": "Display Matrix",
+ "rotation": -180
}
]
},
diff --git a/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj b/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj
index 112dd780e..7c2649448 100644
--- a/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj
+++ b/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
<!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
<PropertyGroup>
diff --git a/tests/Jellyfin.Naming.Tests/Music/MultiDiscAlbumTests.cs b/tests/Jellyfin.Naming.Tests/Music/MultiDiscAlbumTests.cs
index 471616797..6ef12e00b 100644
--- a/tests/Jellyfin.Naming.Tests/Music/MultiDiscAlbumTests.cs
+++ b/tests/Jellyfin.Naming.Tests/Music/MultiDiscAlbumTests.cs
@@ -1,4 +1,4 @@
-using Emby.Naming.Audio;
+using Emby.Naming.Audio;
using Emby.Naming.Common;
using Xunit;
diff --git a/tests/Jellyfin.Naming.Tests/TV/AbsoluteEpisodeNumberTests.cs b/tests/Jellyfin.Naming.Tests/TV/AbsoluteEpisodeNumberTests.cs
index e81c5152e..88102f85a 100644
--- a/tests/Jellyfin.Naming.Tests/TV/AbsoluteEpisodeNumberTests.cs
+++ b/tests/Jellyfin.Naming.Tests/TV/AbsoluteEpisodeNumberTests.cs
@@ -1,4 +1,4 @@
-using Emby.Naming.Common;
+using Emby.Naming.Common;
using Emby.Naming.TV;
using Xunit;
diff --git a/tests/Jellyfin.Naming.Tests/TV/DailyEpisodeTests.cs b/tests/Jellyfin.Naming.Tests/TV/DailyEpisodeTests.cs
index f2cd360e5..012ee4bfc 100644
--- a/tests/Jellyfin.Naming.Tests/TV/DailyEpisodeTests.cs
+++ b/tests/Jellyfin.Naming.Tests/TV/DailyEpisodeTests.cs
@@ -1,4 +1,4 @@
-using Emby.Naming.Common;
+using Emby.Naming.Common;
using Emby.Naming.TV;
using Xunit;
diff --git a/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberWithoutSeasonTests.cs b/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberWithoutSeasonTests.cs
index 1727b2247..7f2188a3e 100644
--- a/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberWithoutSeasonTests.cs
+++ b/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberWithoutSeasonTests.cs
@@ -1,4 +1,4 @@
-using Emby.Naming.Common;
+using Emby.Naming.Common;
using Emby.Naming.TV;
using Xunit;
diff --git a/tests/Jellyfin.Naming.Tests/TV/MultiEpisodeTests.cs b/tests/Jellyfin.Naming.Tests/TV/MultiEpisodeTests.cs
index 6d6591abf..b441e49b1 100644
--- a/tests/Jellyfin.Naming.Tests/TV/MultiEpisodeTests.cs
+++ b/tests/Jellyfin.Naming.Tests/TV/MultiEpisodeTests.cs
@@ -1,4 +1,4 @@
-using Emby.Naming.Common;
+using Emby.Naming.Common;
using Emby.Naming.TV;
using Xunit;
diff --git a/tests/Jellyfin.Naming.Tests/TV/SeasonNumberTests.cs b/tests/Jellyfin.Naming.Tests/TV/SeasonNumberTests.cs
index 94a953de3..ab825c9fa 100644
--- a/tests/Jellyfin.Naming.Tests/TV/SeasonNumberTests.cs
+++ b/tests/Jellyfin.Naming.Tests/TV/SeasonNumberTests.cs
@@ -1,4 +1,4 @@
-using Emby.Naming.Common;
+using Emby.Naming.Common;
using Emby.Naming.TV;
using Xunit;
diff --git a/tests/Jellyfin.Naming.Tests/Video/Format3DTests.cs b/tests/Jellyfin.Naming.Tests/Video/Format3DTests.cs
index fccce5bff..d42bd66a1 100644
--- a/tests/Jellyfin.Naming.Tests/Video/Format3DTests.cs
+++ b/tests/Jellyfin.Naming.Tests/Video/Format3DTests.cs
@@ -1,4 +1,4 @@
-using Emby.Naming.Common;
+using Emby.Naming.Common;
using Emby.Naming.Video;
using Xunit;
diff --git a/tests/Jellyfin.Naming.Tests/Video/StackTests.cs b/tests/Jellyfin.Naming.Tests/Video/StackTests.cs
index c95703f53..92060d452 100644
--- a/tests/Jellyfin.Naming.Tests/Video/StackTests.cs
+++ b/tests/Jellyfin.Naming.Tests/Video/StackTests.cs
@@ -1,4 +1,4 @@
-using System.Linq;
+using System.Linq;
using Emby.Naming.Common;
using Emby.Naming.Video;
using MediaBrowser.Model.IO;
diff --git a/tests/Jellyfin.Providers.Tests/Omdb/JsonOmdbConverterTests.cs b/tests/Jellyfin.Providers.Tests/Omdb/JsonOmdbConverterTests.cs
index 25900bc09..eed9eedc7 100644
--- a/tests/Jellyfin.Providers.Tests/Omdb/JsonOmdbConverterTests.cs
+++ b/tests/Jellyfin.Providers.Tests/Omdb/JsonOmdbConverterTests.cs
@@ -1,4 +1,4 @@
-using System.Text.Json;
+using System.Text.Json;
using System.Text.Json.Serialization;
using MediaBrowser.Providers.Plugins.Omdb;
using Xunit;
diff --git a/tests/Jellyfin.Providers.Tests/Tmdb/TmdbUtilsTests.cs b/tests/Jellyfin.Providers.Tests/Tmdb/TmdbUtilsTests.cs
index 0bfa330cb..6fd48a044 100644
--- a/tests/Jellyfin.Providers.Tests/Tmdb/TmdbUtilsTests.cs
+++ b/tests/Jellyfin.Providers.Tests/Tmdb/TmdbUtilsTests.cs
@@ -1,4 +1,4 @@
-using MediaBrowser.Providers.Plugins.Tmdb;
+using MediaBrowser.Providers.Plugins.Tmdb;
using Xunit;
namespace Jellyfin.Providers.Tests.Tmdb
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 9b6cb40b0..4f018ba69 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj
+++ b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
<!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
<PropertyGroup>
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs
index 0a4a836cb..0afbf7e63 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs
+++ b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
diff --git a/tests/Jellyfin.Server.Implementations.Tests/SessionManager/SessionManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/SessionManager/SessionManagerTests.cs
index e463d838e..9418edc5d 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/SessionManager/SessionManagerTests.cs
+++ b/tests/Jellyfin.Server.Implementations.Tests/SessionManager/SessionManagerTests.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
using MediaBrowser.Controller;
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Users/UserManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Users/UserManagerTests.cs
index 867dda29d..295f558fa 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Users/UserManagerTests.cs
+++ b/tests/Jellyfin.Server.Implementations.Tests/Users/UserManagerTests.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using Jellyfin.Server.Implementations.Users;
using Xunit;
diff --git a/tests/Jellyfin.Server.Integration.Tests/Controllers/BaseJellyfinTestController.cs b/tests/Jellyfin.Server.Integration.Tests/Controllers/BaseJellyfinTestController.cs
index 9db8689a7..cf2a10c6f 100644
--- a/tests/Jellyfin.Server.Integration.Tests/Controllers/BaseJellyfinTestController.cs
+++ b/tests/Jellyfin.Server.Integration.Tests/Controllers/BaseJellyfinTestController.cs
@@ -1,4 +1,4 @@
-using Jellyfin.Api;
+using Jellyfin.Api;
using Microsoft.AspNetCore.Mvc;
namespace Jellyfin.Server.Integration.Tests.Controllers
diff --git a/tests/Jellyfin.Server.Integration.Tests/Controllers/EncoderController.cs b/tests/Jellyfin.Server.Integration.Tests/Controllers/EncoderController.cs
index 3d4c27c95..14a26d795 100644
--- a/tests/Jellyfin.Server.Integration.Tests/Controllers/EncoderController.cs
+++ b/tests/Jellyfin.Server.Integration.Tests/Controllers/EncoderController.cs
@@ -1,4 +1,4 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
diff --git a/tests/Jellyfin.Server.Integration.Tests/Controllers/LiveTvControllerTests.cs b/tests/Jellyfin.Server.Integration.Tests/Controllers/LiveTvControllerTests.cs
new file mode 100644
index 000000000..dd971fa87
--- /dev/null
+++ b/tests/Jellyfin.Server.Integration.Tests/Controllers/LiveTvControllerTests.cs
@@ -0,0 +1,96 @@
+using System.Net;
+using System.Net.Http.Json;
+using System.Net.Mime;
+using System.Text;
+using System.Text.Json;
+using System.Threading.Tasks;
+using Jellyfin.Extensions.Json;
+using MediaBrowser.Model.LiveTv;
+using Xunit;
+
+namespace Jellyfin.Server.Integration.Tests.Controllers;
+
+public sealed class LiveTvControllerTests : IClassFixture<JellyfinApplicationFactory>
+{
+ private readonly JellyfinApplicationFactory _factory;
+ private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options;
+ private static string? _accessToken;
+
+ public LiveTvControllerTests(JellyfinApplicationFactory factory)
+ {
+ _factory = factory;
+ }
+
+ [Fact]
+ public async Task AddTunerHost_Unauthorized_ReturnsUnauthorized()
+ {
+ var client = _factory.CreateClient();
+
+ var body = new TunerHostInfo()
+ {
+ Type = "m3u",
+ Url = "Test Data/dummy.m3u8"
+ };
+
+ var response = await client.PostAsJsonAsync("/LiveTv/TunerHosts", body, _jsonOptions);
+
+ Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
+ }
+
+ [Fact]
+ public async Task AddTunerHost_Valid_ReturnsCorrectResponse()
+ {
+ var client = _factory.CreateClient();
+ client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client));
+
+ var body = new TunerHostInfo()
+ {
+ Type = "m3u",
+ Url = "Test Data/dummy.m3u8"
+ };
+
+ var response = await client.PostAsJsonAsync("/LiveTv/TunerHosts", body, _jsonOptions);
+
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ Assert.Equal(MediaTypeNames.Application.Json, response.Content.Headers.ContentType?.MediaType);
+ Assert.Equal(Encoding.UTF8.BodyName, response.Content.Headers.ContentType?.CharSet);
+ var responseBody = await response.Content.ReadFromJsonAsync<TunerHostInfo>();
+ Assert.NotNull(responseBody);
+ Assert.Equal(body.Type, responseBody.Type);
+ Assert.Equal(body.Url, responseBody.Url);
+ }
+
+ [Fact]
+ public async Task AddTunerHost_InvalidType_ReturnsNotFound()
+ {
+ var client = _factory.CreateClient();
+ client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client));
+
+ var body = new TunerHostInfo()
+ {
+ Type = "invalid",
+ Url = "Test Data/dummy.m3u8"
+ };
+
+ var response = await client.PostAsJsonAsync("/LiveTv/TunerHosts", body, _jsonOptions);
+
+ Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
+ }
+
+ [Fact]
+ public async Task AddTunerHost_InvalidUrl_ReturnsNotFound()
+ {
+ var client = _factory.CreateClient();
+ client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client));
+
+ var body = new TunerHostInfo()
+ {
+ Type = "m3u",
+ Url = "thisgoesnowhere"
+ };
+
+ var response = await client.PostAsJsonAsync("/LiveTv/TunerHosts", body, _jsonOptions);
+
+ Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
+ }
+}
diff --git a/tests/Jellyfin.Server.Integration.Tests/Controllers/PersonsControllerTests.cs b/tests/Jellyfin.Server.Integration.Tests/Controllers/PersonsControllerTests.cs
index 38c64547c..c673773ff 100644
--- a/tests/Jellyfin.Server.Integration.Tests/Controllers/PersonsControllerTests.cs
+++ b/tests/Jellyfin.Server.Integration.Tests/Controllers/PersonsControllerTests.cs
@@ -1,4 +1,4 @@
-using System.Net;
+using System.Net;
using System.Threading.Tasks;
using Xunit;
diff --git a/tests/Jellyfin.Server.Integration.Tests/Controllers/PlaystateControllerTests.cs b/tests/Jellyfin.Server.Integration.Tests/Controllers/PlaystateControllerTests.cs
index 9554d3ebc..c02eedb20 100644
--- a/tests/Jellyfin.Server.Integration.Tests/Controllers/PlaystateControllerTests.cs
+++ b/tests/Jellyfin.Server.Integration.Tests/Controllers/PlaystateControllerTests.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Net;
using System.Threading.Tasks;
using Xunit;
diff --git a/tests/Jellyfin.Server.Integration.Tests/Controllers/PluginsControllerTests.cs b/tests/Jellyfin.Server.Integration.Tests/Controllers/PluginsControllerTests.cs
new file mode 100644
index 000000000..547bfcc0f
--- /dev/null
+++ b/tests/Jellyfin.Server.Integration.Tests/Controllers/PluginsControllerTests.cs
@@ -0,0 +1,45 @@
+using System.Net;
+using System.Net.Http.Json;
+using System.Net.Mime;
+using System.Text;
+using System.Threading.Tasks;
+using Jellyfin.Extensions.Json;
+using MediaBrowser.Model.Plugins;
+using Xunit;
+
+namespace Jellyfin.Server.Integration.Tests.Controllers;
+
+public sealed class PluginsControllerTests : IClassFixture<JellyfinApplicationFactory>
+{
+ private readonly JellyfinApplicationFactory _factory;
+ private static string? _accessToken;
+
+ public PluginsControllerTests(JellyfinApplicationFactory factory)
+ {
+ _factory = factory;
+ }
+
+ [Fact]
+ public async Task GetPlugins_Unauthorized_ReturnsUnauthorized()
+ {
+ var client = _factory.CreateClient();
+
+ var response = await client.GetAsync("/Plugins");
+
+ Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
+ }
+
+ [Fact]
+ public async Task GetPlugins_Authorized_ReturnsCorrectResponse()
+ {
+ var client = _factory.CreateClient();
+ client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client));
+
+ var response = await client.GetAsync("/Plugins");
+
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ Assert.Equal(MediaTypeNames.Application.Json, response.Content.Headers.ContentType?.MediaType);
+ Assert.Equal(Encoding.UTF8.BodyName, response.Content.Headers.ContentType?.CharSet);
+ _ = await response.Content.ReadFromJsonAsync<PluginInfo[]>(JsonDefaults.Options);
+ }
+}
diff --git a/tests/Jellyfin.Server.Integration.Tests/Controllers/SessionControllerTests.cs b/tests/Jellyfin.Server.Integration.Tests/Controllers/SessionControllerTests.cs
index ab68884f9..c267d3dd3 100644
--- a/tests/Jellyfin.Server.Integration.Tests/Controllers/SessionControllerTests.cs
+++ b/tests/Jellyfin.Server.Integration.Tests/Controllers/SessionControllerTests.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Net;
using System.Threading.Tasks;
using Xunit;
diff --git a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj
index a5296d8c9..8228c0df7 100644
--- a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj
+++ b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj
@@ -18,6 +18,9 @@
</ItemGroup>
<ItemGroup>
+ <None Include="Test Data\**\*.*">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </None>
<!-- Don't run tests in parallel -->
<None Update="xunit.runner.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
diff --git a/tests/Jellyfin.Server.Integration.Tests/JellyfinApplicationFactory.cs b/tests/Jellyfin.Server.Integration.Tests/JellyfinApplicationFactory.cs
index a078eff77..78b32d278 100644
--- a/tests/Jellyfin.Server.Integration.Tests/JellyfinApplicationFactory.cs
+++ b/tests/Jellyfin.Server.Integration.Tests/JellyfinApplicationFactory.cs
@@ -47,6 +47,8 @@ namespace Jellyfin.Server.Integration.Tests
/// <inheritdoc/>
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
+ // Skip ffmpeg check for testing
+ Environment.SetEnvironmentVariable("JELLYFIN_FFMPEG__NOVALIDATION", "true");
// Specify the startup command line options
var commandLineOpts = new StartupOptions();
diff --git a/tests/Jellyfin.Server.Integration.Tests/Middleware/RobotsRedirectionMiddlewareTests.cs b/tests/Jellyfin.Server.Integration.Tests/Middleware/RobotsRedirectionMiddlewareTests.cs
index c8ad9d2a1..1ea79f7de 100644
--- a/tests/Jellyfin.Server.Integration.Tests/Middleware/RobotsRedirectionMiddlewareTests.cs
+++ b/tests/Jellyfin.Server.Integration.Tests/Middleware/RobotsRedirectionMiddlewareTests.cs
@@ -1,4 +1,4 @@
-using System.Net;
+using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Testing;
using Xunit;
diff --git a/tests/Jellyfin.Server.Integration.Tests/Test Data/dummy.m3u8 b/tests/Jellyfin.Server.Integration.Tests/Test Data/dummy.m3u8
new file mode 100644
index 000000000..7f60f38a6
--- /dev/null
+++ b/tests/Jellyfin.Server.Integration.Tests/Test Data/dummy.m3u8
@@ -0,0 +1 @@
+C:\Music
diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Location/MovieNfoLocationTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Location/MovieNfoLocationTests.cs
index 2f05c4ea2..b9599b9f0 100644
--- a/tests/Jellyfin.XbmcMetadata.Tests/Location/MovieNfoLocationTests.cs
+++ b/tests/Jellyfin.XbmcMetadata.Tests/Location/MovieNfoLocationTests.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Linq;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Providers;
diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicAlbumNfoProviderTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicAlbumNfoProviderTests.cs
index 8f276d03f..f815dfaa9 100644
--- a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicAlbumNfoProviderTests.cs
+++ b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicAlbumNfoProviderTests.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Threading;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Entities.Audio;
diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicVideoNfoParserTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicVideoNfoParserTests.cs
index bf887cab1..89c629466 100644
--- a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicVideoNfoParserTests.cs
+++ b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicVideoNfoParserTests.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Linq;
using System.Threading;
using MediaBrowser.Common.Configuration;
diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/SeasonNfoProviderTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/SeasonNfoProviderTests.cs
index e69ca996c..2cd9f8a1a 100644
--- a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/SeasonNfoProviderTests.cs
+++ b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/SeasonNfoProviderTests.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Linq;
using System.Threading;
using Jellyfin.Data.Enums;
diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Test Data/Dancing Queen.nfo b/tests/Jellyfin.XbmcMetadata.Tests/Test Data/Dancing Queen.nfo
index 29f19e1a0..7e2b48a6c 100644
--- a/tests/Jellyfin.XbmcMetadata.Tests/Test Data/Dancing Queen.nfo
+++ b/tests/Jellyfin.XbmcMetadata.Tests/Test Data/Dancing Queen.nfo
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<musicvideo>
<title>Dancing Queen</title>
<userrating>0</userrating>
diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Test Data/Radarr.nfo b/tests/Jellyfin.XbmcMetadata.Tests/Test Data/Radarr.nfo
index 43da4881c..989176ccf 100644
--- a/tests/Jellyfin.XbmcMetadata.Tests/Test Data/Radarr.nfo
+++ b/tests/Jellyfin.XbmcMetadata.Tests/Test Data/Radarr.nfo
@@ -1,2 +1,2 @@
-https://www.themoviedb.org/movie/583689
+https://www.themoviedb.org/movie/583689
https://www.imdb.com/title/tt4154796
diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Test Data/Season 01.nfo b/tests/Jellyfin.XbmcMetadata.Tests/Test Data/Season 01.nfo
index 91f0392f4..adbbb121c 100644
--- a/tests/Jellyfin.XbmcMetadata.Tests/Test Data/Season 01.nfo
+++ b/tests/Jellyfin.XbmcMetadata.Tests/Test Data/Season 01.nfo
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<season>
<plot />
<outline />
diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Test Data/The Best of 1980-1990.nfo b/tests/Jellyfin.XbmcMetadata.Tests/Test Data/The Best of 1980-1990.nfo
index 4ab8400d3..685150c7e 100644
--- a/tests/Jellyfin.XbmcMetadata.Tests/Test Data/The Best of 1980-1990.nfo
+++ b/tests/Jellyfin.XbmcMetadata.Tests/Test Data/The Best of 1980-1990.nfo
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<album>
<title>The Best of 1980-1990</title>
<musicbrainzalbumid>59b5a40b-e2fd-3f18-a218-e8c9aae12ab5</musicbrainzalbumid>
diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Test Data/The Bone Orchard.nfo b/tests/Jellyfin.XbmcMetadata.Tests/Test Data/The Bone Orchard.nfo
index cd275e4cf..718b83c44 100644
--- a/tests/Jellyfin.XbmcMetadata.Tests/Test Data/The Bone Orchard.nfo
+++ b/tests/Jellyfin.XbmcMetadata.Tests/Test Data/The Bone Orchard.nfo
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<episodedetails>
<title>The Bone Orchard</title>
<showtitle>American Gods</showtitle>