aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.ci/azure-pipelines-api-client.yml59
-rw-r--r--.ci/azure-pipelines.yml3
-rw-r--r--.github/workflows/automation.yml10
-rw-r--r--.github/workflows/label-commenter-config.yml43
-rw-r--r--.github/workflows/label-commenter.yml22
-rw-r--r--Emby.Dlna/ContentDirectory/ControlHandler.cs2
-rw-r--r--Emby.Dlna/Didl/DidlBuilder.cs35
-rw-r--r--Emby.Dlna/DlnaManager.cs95
-rw-r--r--Emby.Dlna/Main/DlnaEntryPoint.cs1
-rw-r--r--Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs1
-rw-r--r--Emby.Dlna/PlayTo/PlayToController.cs8
-rw-r--r--Emby.Dlna/PlayTo/PlayToManager.cs2
-rw-r--r--Emby.Dlna/PlayTo/SsdpHttpClient.cs1
-rw-r--r--Emby.Dlna/Ssdp/DeviceDiscovery.cs2
-rw-r--r--Emby.Naming/TV/EpisodeResolver.cs5
-rw-r--r--Emby.Server.Implementations/AppBase/ConfigurationHelper.cs1
-rw-r--r--Emby.Server.Implementations/ApplicationHost.cs18
-rw-r--r--Emby.Server.Implementations/Channels/ChannelManager.cs1
-rw-r--r--Emby.Server.Implementations/Collections/CollectionManager.cs54
-rw-r--r--Emby.Server.Implementations/ConfigurationOptions.cs1
-rw-r--r--Emby.Server.Implementations/Data/SqliteItemRepository.cs395
-rw-r--r--Emby.Server.Implementations/Dto/DtoService.cs15
-rw-r--r--Emby.Server.Implementations/Emby.Server.Implementations.csproj6
-rw-r--r--Emby.Server.Implementations/HttpServer/Security/AuthService.cs1
-rw-r--r--Emby.Server.Implementations/IO/ManagedFileSystem.cs61
-rw-r--r--Emby.Server.Implementations/IStartupOptions.cs1
-rw-r--r--Emby.Server.Implementations/Images/ArtistImageProvider.cs8
-rw-r--r--Emby.Server.Implementations/Images/DynamicImageProvider.cs1
-rw-r--r--Emby.Server.Implementations/Images/PlaylistImageProvider.cs4
-rw-r--r--Emby.Server.Implementations/Library/LibraryManager.cs93
-rw-r--r--Emby.Server.Implementations/Library/MediaSourceManager.cs25
-rw-r--r--Emby.Server.Implementations/Library/MusicManager.cs3
-rw-r--r--Emby.Server.Implementations/Library/PathExtensions.cs12
-rw-r--r--Emby.Server.Implementations/Library/ResolverHelper.cs59
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs5
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/ItemResolver.cs12
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs2
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/PhotoAlbumResolver.cs12
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs2
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs14
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs2
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs18
-rw-r--r--Emby.Server.Implementations/Library/SearchEngine.cs1
-rw-r--r--Emby.Server.Implementations/Library/UserDataManager.cs10
-rw-r--r--Emby.Server.Implementations/Library/UserViewManager.cs1
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs25
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs1
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs7
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs1
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvManager.cs15
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs12
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs3
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs1
-rw-r--r--Emby.Server.Implementations/Localization/LocalizationManager.cs4
-rw-r--r--Emby.Server.Implementations/MediaEncoder/EncodingManager.cs1
-rw-r--r--Emby.Server.Implementations/Playlists/PlaylistManager.cs2
-rw-r--r--Emby.Server.Implementations/Plugins/PluginManager.cs17
-rw-r--r--Emby.Server.Implementations/QuickConnect/QuickConnectManager.cs15
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs12
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs2
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs2
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs1
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs2
-rw-r--r--Emby.Server.Implementations/Session/SessionManager.cs7
-rw-r--r--Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs23
-rw-r--r--Emby.Server.Implementations/TV/TVSeriesManager.cs8
-rw-r--r--Jellyfin.Api/Controllers/DynamicHlsController.cs24
-rw-r--r--Jellyfin.Api/Controllers/ItemLookupController.cs91
-rw-r--r--Jellyfin.Api/Controllers/MoviesController.cs9
-rw-r--r--Jellyfin.Api/Controllers/PluginsController.cs8
-rw-r--r--Jellyfin.Api/Controllers/RemoteImageController.cs52
-rw-r--r--Jellyfin.Api/Controllers/SearchController.cs5
-rw-r--r--Jellyfin.Api/Controllers/SuggestionsController.cs2
-rw-r--r--Jellyfin.Api/Controllers/TvShowsController.cs2
-rw-r--r--Jellyfin.Api/Controllers/UniversalAudioController.cs6
-rw-r--r--Jellyfin.Api/Controllers/UserController.cs3
-rw-r--r--Jellyfin.Api/Controllers/VideoHlsController.cs24
-rw-r--r--Jellyfin.Api/Controllers/VideosController.cs33
-rw-r--r--Jellyfin.Api/Extensions/DtoExtensions.cs1
-rw-r--r--Jellyfin.Api/Helpers/AudioHelper.cs28
-rw-r--r--Jellyfin.Api/Helpers/DynamicHlsHelper.cs28
-rw-r--r--Jellyfin.Api/Helpers/MediaInfoHelper.cs2
-rw-r--r--Jellyfin.Api/Helpers/ProgressiveFileCopier.cs3
-rw-r--r--Jellyfin.Api/Helpers/StreamingHelpers.cs23
-rw-r--r--Jellyfin.Api/Helpers/TranscodingJobHelper.cs8
-rw-r--r--Jellyfin.Api/Jellyfin.Api.csproj4
-rw-r--r--Jellyfin.Api/Models/UserDtos/AuthenticateUserByName.cs5
-rw-r--r--Jellyfin.Data/Entities/AccessSchedule.cs8
-rw-r--r--Jellyfin.Data/Entities/ActivityLog.cs7
-rw-r--r--Jellyfin.Data/Entities/CustomItemDisplayPreferences.cs4
-rw-r--r--Jellyfin.Data/Entities/DisplayPreferences.cs12
-rw-r--r--Jellyfin.Data/Entities/Group.cs16
-rw-r--r--Jellyfin.Data/Entities/HomeSection.cs7
-rw-r--r--Jellyfin.Data/Entities/ImageInfo.cs8
-rw-r--r--Jellyfin.Data/Entities/ItemDisplayPreferences.cs4
-rw-r--r--Jellyfin.Data/Entities/Libraries/Artwork.cs8
-rw-r--r--Jellyfin.Data/Entities/Libraries/Book.cs8
-rw-r--r--Jellyfin.Data/Entities/Libraries/BookMetadata.cs6
-rw-r--r--Jellyfin.Data/Entities/Libraries/Chapter.cs8
-rw-r--r--Jellyfin.Data/Entities/Libraries/Collection.cs11
-rw-r--r--Jellyfin.Data/Entities/Libraries/CollectionItem.cs2
-rw-r--r--Jellyfin.Data/Entities/Libraries/Company.cs16
-rw-r--r--Jellyfin.Data/Entities/Libraries/CustomItem.cs8
-rw-r--r--Jellyfin.Data/Entities/Libraries/Episode.cs8
-rw-r--r--Jellyfin.Data/Entities/Libraries/Genre.cs6
-rw-r--r--Jellyfin.Data/Entities/Libraries/ItemMetadata.cs37
-rw-r--r--Jellyfin.Data/Entities/Libraries/Library.cs6
-rw-r--r--Jellyfin.Data/Entities/Libraries/LibraryItem.cs10
-rw-r--r--Jellyfin.Data/Entities/Libraries/MediaFile.cs12
-rw-r--r--Jellyfin.Data/Entities/Libraries/MediaFileStream.cs6
-rw-r--r--Jellyfin.Data/Entities/Libraries/MetadataProvider.cs6
-rw-r--r--Jellyfin.Data/Entities/Libraries/MetadataProviderId.cs6
-rw-r--r--Jellyfin.Data/Entities/Libraries/Movie.cs8
-rw-r--r--Jellyfin.Data/Entities/Libraries/MovieMetadata.cs6
-rw-r--r--Jellyfin.Data/Entities/Libraries/MusicAlbum.cs10
-rw-r--r--Jellyfin.Data/Entities/Libraries/MusicAlbumMetadata.cs6
-rw-r--r--Jellyfin.Data/Entities/Libraries/Person.cs16
-rw-r--r--Jellyfin.Data/Entities/Libraries/PersonRole.cs14
-rw-r--r--Jellyfin.Data/Entities/Libraries/Photo.cs8
-rw-r--r--Jellyfin.Data/Entities/Libraries/Rating.cs6
-rw-r--r--Jellyfin.Data/Entities/Libraries/RatingSource.cs6
-rw-r--r--Jellyfin.Data/Entities/Libraries/Release.cs16
-rw-r--r--Jellyfin.Data/Entities/Libraries/Season.cs10
-rw-r--r--Jellyfin.Data/Entities/Libraries/Series.cs11
-rw-r--r--Jellyfin.Data/Entities/Libraries/SeriesMetadata.cs7
-rw-r--r--Jellyfin.Data/Entities/Libraries/Track.cs8
-rw-r--r--Jellyfin.Data/Entities/Permission.cs16
-rw-r--r--Jellyfin.Data/Entities/Preference.cs15
-rw-r--r--Jellyfin.Data/Entities/User.cs44
-rw-r--r--Jellyfin.Data/Enums/HomeSectionType.cs7
-rw-r--r--Jellyfin.Networking/Configuration/NetworkConfiguration.cs1
-rw-r--r--Jellyfin.Networking/Configuration/NetworkConfigurationExtensions.cs1
-rw-r--r--Jellyfin.Networking/Manager/NetworkManager.cs2
-rw-r--r--Jellyfin.Server.Implementations/JellyfinDb.cs68
-rw-r--r--Jellyfin.Server.Implementations/Migrations/20210320181425_AddIndexesAndCollations.Designer.cs535
-rw-r--r--Jellyfin.Server.Implementations/Migrations/20210320181425_AddIndexesAndCollations.cs240
-rw-r--r--Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs33
-rw-r--r--Jellyfin.Server.Implementations/Users/UserManager.cs18
-rw-r--r--Jellyfin.Server/Filters/ParameterObsoleteFilter.cs1
-rw-r--r--Jellyfin.Server/Formatters/CssOutputFormatter.cs3
-rw-r--r--Jellyfin.Server/Middleware/IpBasedAccessValidationMiddleware.cs2
-rw-r--r--Jellyfin.Server/Middleware/LanFilteringMiddleware.cs3
-rw-r--r--Jellyfin.Server/Migrations/Routines/AddPeopleQueryIndex.cs6
-rw-r--r--Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs2
-rw-r--r--Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs1
-rw-r--r--Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs10
-rw-r--r--Jellyfin.Server/Program.cs2
-rw-r--r--Jellyfin.Server/Startup.cs14
-rw-r--r--Jellyfin.Server/StartupOptions.cs3
-rw-r--r--MediaBrowser.Common/Cryptography/PasswordHash.cs1
-rw-r--r--MediaBrowser.Common/Extensions/SplitStringExtensions.cs95
-rw-r--r--MediaBrowser.Common/Net/DefaultHttpClientHandler.cs19
-rw-r--r--MediaBrowser.Common/Net/INetworkManager.cs1
-rw-r--r--MediaBrowser.Common/Net/IPHost.cs5
-rw-r--r--MediaBrowser.Common/Plugins/BasePluginOfT.cs5
-rw-r--r--MediaBrowser.Controller/BaseItemManager/BaseItemManager.cs1
-rw-r--r--MediaBrowser.Controller/BaseItemManager/IBaseItemManager.cs1
-rw-r--r--MediaBrowser.Controller/Drawing/ImageHelper.cs3
-rw-r--r--MediaBrowser.Controller/Entities/AggregateFolder.cs3
-rw-r--r--MediaBrowser.Controller/Entities/BaseItem.cs28
-rw-r--r--MediaBrowser.Controller/Entities/BaseItemExtensions.cs20
-rw-r--r--MediaBrowser.Controller/Entities/CollectionFolder.cs6
-rw-r--r--MediaBrowser.Controller/Entities/Folder.cs27
-rw-r--r--MediaBrowser.Controller/Entities/Movies/BoxSet.cs3
-rw-r--r--MediaBrowser.Controller/Entities/PeopleHelper.cs18
-rw-r--r--MediaBrowser.Controller/Entities/Photo.cs3
-rw-r--r--MediaBrowser.Controller/Entities/TV/Episode.cs10
-rw-r--r--MediaBrowser.Controller/Entities/TV/Season.cs11
-rw-r--r--MediaBrowser.Controller/Entities/TV/Series.cs25
-rw-r--r--MediaBrowser.Controller/Entities/UserView.cs5
-rw-r--r--MediaBrowser.Controller/Entities/UserViewBuilder.cs10
-rw-r--r--MediaBrowser.Controller/Events/Updates/PluginUninstalledEventArgs.cs1
-rw-r--r--MediaBrowser.Controller/IServerApplicationHost.cs3
-rw-r--r--MediaBrowser.Controller/Library/ILibraryManager.cs9
-rw-r--r--MediaBrowser.Controller/Library/ItemResolveArgs.cs27
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs10
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs1
-rw-r--r--MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs1
-rw-r--r--MediaBrowser.Controller/MediaEncoding/JobLogger.cs1
-rw-r--r--MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs5
-rw-r--r--MediaBrowser.Controller/Persistence/IItemRepository.cs3
-rw-r--r--MediaBrowser.Controller/Playlists/Playlist.cs10
-rw-r--r--MediaBrowser.Controller/Providers/IRemoteImageProvider.cs1
-rw-r--r--MediaBrowser.Controller/Providers/IRemoteSearchProvider.cs1
-rw-r--r--MediaBrowser.Controller/Providers/ItemInfo.cs3
-rw-r--r--MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs5
-rw-r--r--MediaBrowser.Controller/Providers/MetadataResult.cs16
-rw-r--r--MediaBrowser.Controller/Resolvers/BaseItemResolver.cs12
-rw-r--r--MediaBrowser.Controller/Session/AuthenticationRequest.cs1
-rw-r--r--MediaBrowser.Controller/Subtitles/ISubtitleManager.cs1
-rw-r--r--MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs3
-rw-r--r--MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs6
-rw-r--r--MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs16
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs4
-rw-r--r--MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs1
-rw-r--r--MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs56
-rw-r--r--MediaBrowser.Model/Dlna/ContainerProfile.cs59
-rw-r--r--MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs85
-rw-r--r--MediaBrowser.Model/Dlna/DeviceProfile.cs149
-rw-r--r--MediaBrowser.Model/Dlna/DirectPlayProfile.cs8
-rw-r--r--MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs37
-rw-r--r--MediaBrowser.Model/Dlna/StreamBuilder.cs2
-rw-r--r--MediaBrowser.Model/Dlna/TranscodingProfile.cs22
-rw-r--r--MediaBrowser.Model/Dlna/UpnpDeviceInfo.cs2
-rw-r--r--MediaBrowser.Model/Dto/NameIdPair.cs2
-rw-r--r--MediaBrowser.Model/Entities/MediaStream.cs10
-rw-r--r--MediaBrowser.Model/Entities/ProviderIdsExtensions.cs5
-rw-r--r--MediaBrowser.Model/IO/FileSystemMetadata.cs6
-rw-r--r--MediaBrowser.Model/LiveTv/TunerHostInfo.cs3
-rw-r--r--MediaBrowser.Model/Notifications/NotificationOptions.cs11
-rw-r--r--MediaBrowser.Providers/Manager/MetadataService.cs60
-rw-r--r--MediaBrowser.Providers/Manager/ProviderManager.cs24
-rw-r--r--MediaBrowser.Providers/MediaBrowser.Providers.csproj2
-rw-r--r--MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs4
-rw-r--r--MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs2
-rw-r--r--MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs4
-rw-r--r--MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs4
-rw-r--r--MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs1
-rw-r--r--MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs1
-rw-r--r--MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs1
-rw-r--r--MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs1
-rw-r--r--MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs4
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs47
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs18
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs17
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs70
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs10
-rw-r--r--MediaBrowser.Providers/Subtitles/SubtitleManager.cs15
-rw-r--r--MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs102
-rw-r--r--RSSDP/DeviceAvailableEventArgs.cs2
-rw-r--r--RSSDP/SsdpCommunicationsServer.cs5
-rw-r--r--RSSDP/SsdpDeviceLocator.cs26
-rw-r--r--apiclient/.openapi-generator-ignore2
-rw-r--r--apiclient/templates/typescript/axios/generate.sh11
-rw-r--r--apiclient/templates/typescript/axios/package.mustache30
-rw-r--r--tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj6
-rw-r--r--tests/Jellyfin.Common.Tests/Json/JsonGuidConverterTests.cs1
-rw-r--r--tests/Jellyfin.Common.Tests/Json/JsonNullableGuidConverterTests.cs1
-rw-r--r--tests/Jellyfin.Dlna.Tests/DlnaManagerTests.cs131
-rw-r--r--tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj3
-rw-r--r--tests/Jellyfin.Model.Tests/Dlna/ContainerProfileTests.cs19
-rw-r--r--tests/Jellyfin.Model.Tests/Extensions/StringHelperTests.cs1
-rw-r--r--tests/Jellyfin.Naming.Tests/AudioBook/AudioBookListResolverTests.cs1
-rw-r--r--tests/Jellyfin.Naming.Tests/AudioBook/AudioBookResolverTests.cs1
-rw-r--r--tests/Jellyfin.Naming.Tests/Subtitles/SubtitleParserTests.cs1
-rw-r--r--tests/Jellyfin.Naming.Tests/Video/ExtraTests.cs1
-rw-r--r--tests/Jellyfin.Networking.Tests/NetworkParseTests.cs10
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Data/SqliteItemRepositoryTests.cs250
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs11
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj4
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Library/EpisodeResolverTest.cs11
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs1
-rw-r--r--tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj13
-rw-r--r--tests/Jellyfin.Server.Integration.Tests/OpenApiSpecTests.cs2
-rw-r--r--tests/Jellyfin.Server.Integration.Tests/TestAppHost.cs1
-rw-r--r--tests/Jellyfin.Server.Integration.Tests/xunit.runner.json4
-rw-r--r--tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj6
-rw-r--r--tests/Jellyfin.Server.Tests/ParseNetworkTests.cs1
-rw-r--r--tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs6
-rw-r--r--tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicArtistNfoParserTests.cs1
-rw-r--r--tests/Jellyfin.XbmcMetadata.Tests/Test Data/Justice League.nfo2
264 files changed, 2894 insertions, 2000 deletions
diff --git a/.ci/azure-pipelines-api-client.yml b/.ci/azure-pipelines-api-client.yml
deleted file mode 100644
index 0e944e6f4..000000000
--- a/.ci/azure-pipelines-api-client.yml
+++ /dev/null
@@ -1,59 +0,0 @@
-parameters:
- - name: LinuxImage
- type: string
- default: "ubuntu-latest"
- - name: GeneratorVersion
- type: string
- default: "5.0.1"
-
-jobs:
-- job: GenerateApiClients
- displayName: 'Generate Api Clients'
- condition: startsWith(variables['Build.SourceBranch'], 'refs/tags/v')
- dependsOn: Test
-
- pool:
- vmImage: "${{ parameters.LinuxImage }}"
-
- steps:
- - task: DownloadPipelineArtifact@2
- displayName: 'Download OpenAPI Spec Artifact'
- inputs:
- source: 'current'
- artifact: "OpenAPI Spec"
- path: "$(System.ArtifactsDirectory)/openapispec"
- runVersion: "latest"
-
- - task: CmdLine@2
- displayName: 'Download OpenApi Generator'
- inputs:
- script: "wget https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/${{ parameters.GeneratorVersion }}/openapi-generator-cli-${{ parameters.GeneratorVersion }}.jar -O openapi-generator-cli.jar"
-
-## Authenticate with npm registry
- - task: npmAuthenticate@0
- inputs:
- workingFile: ./.npmrc
- customEndpoint: 'jellyfin-bot for NPM'
-
-## Generate npm api client
- - task: CmdLine@2
- displayName: 'Build stable typescript axios client'
- inputs:
- script: "bash ./apiclient/templates/typescript/axios/generate.sh $(System.ArtifactsDirectory)"
-
-## Run npm install
- - task: Npm@1
- displayName: 'Install npm dependencies'
- inputs:
- command: install
- workingDir: ./apiclient/generated/typescript/axios
-
-## Publish npm packages
- - task: Npm@1
- displayName: 'Publish stable typescript axios client'
- inputs:
- command: custom
- customCommand: publish --access public
- publishRegistry: useExternalRegistry
- publishEndpoint: 'jellyfin-bot for NPM'
- workingDir: ./apiclient/generated/typescript/axios
diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml
index 6430503f9..c028b6e3e 100644
--- a/.ci/azure-pipelines.yml
+++ b/.ci/azure-pipelines.yml
@@ -61,6 +61,3 @@ jobs:
- ${{ if or(startsWith(variables['Build.SourceBranch'], 'refs/tags/v'), startsWith(variables['Build.SourceBranch'], 'refs/heads/master')) }}:
- template: azure-pipelines-package.yml
-
-- ${{ if or(startsWith(variables['Build.SourceBranch'], 'refs/tags/v'), startsWith(variables['Build.SourceBranch'], 'refs/heads/master')) }}:
- - template: azure-pipelines-api-client.yml
diff --git a/.github/workflows/automation.yml b/.github/workflows/automation.yml
index db34693cc..a203e6695 100644
--- a/.github/workflows/automation.yml
+++ b/.github/workflows/automation.yml
@@ -16,7 +16,7 @@ jobs:
label: stable backport
- name: Remove from 'Current Release' project
- uses: alex-page/github-project-automation-plus@v0.5.1
+ uses: alex-page/github-project-automation-plus@v0.7.1
if: (github.event.pull_request || github.event.issue.pull_request) && !steps.checkLabel.outputs.hasLabel
continue-on-error: true
with:
@@ -25,7 +25,7 @@ jobs:
repo-token: ${{ secrets.GH_TOKEN }}
- name: Add to 'Release Next' project
- uses: alex-page/github-project-automation-plus@v0.5.1
+ uses: alex-page/github-project-automation-plus@v0.7.1
if: (github.event.pull_request || github.event.issue.pull_request) && github.event.action == 'opened'
continue-on-error: true
with:
@@ -34,7 +34,7 @@ jobs:
repo-token: ${{ secrets.GH_TOKEN }}
- name: Add to 'Current Release' project
- uses: alex-page/github-project-automation-plus@v0.5.1
+ uses: alex-page/github-project-automation-plus@v0.7.1
if: (github.event.pull_request || github.event.issue.pull_request) && steps.checkLabel.outputs.hasLabel
continue-on-error: true
with:
@@ -48,7 +48,7 @@ jobs:
run: echo "::set-output name=number::$(curl -s ${{ github.event.issue.comments_url }} | jq '.[] | select(.author_association == "MEMBER") | .author_association' | wc -l)"
- name: Move issue to needs triage
- uses: alex-page/github-project-automation-plus@v0.5.1
+ uses: alex-page/github-project-automation-plus@v0.7.1
if: github.event.issue.pull_request == '' && github.event.comment.author_association == 'MEMBER' && steps.member_comments.outputs.number <= 1
continue-on-error: true
with:
@@ -57,7 +57,7 @@ jobs:
repo-token: ${{ secrets.GH_TOKEN }}
- name: Add issue to triage project
- uses: alex-page/github-project-automation-plus@v0.5.1
+ uses: alex-page/github-project-automation-plus@v0.7.1
if: github.event.issue.pull_request == '' && github.event.action == 'opened'
continue-on-error: true
with:
diff --git a/.github/workflows/label-commenter-config.yml b/.github/workflows/label-commenter-config.yml
new file mode 100644
index 000000000..78b75be43
--- /dev/null
+++ b/.github/workflows/label-commenter-config.yml
@@ -0,0 +1,43 @@
+comment:
+ header: Hello @{{ issue.user.login }}
+ footer: "\
+ ---\n\n
+ > This is an automated comment created by the [peaceiris/actions-label-commenter]. \
+ Responding to the bot or mentioning it won't have any effect.\n\n
+ [peaceiris/actions-label-commenter]: https://github.com/peaceiris/actions-label-commenter
+ "
+
+labels:
+ - name: stable backport
+ labeled:
+ pr:
+ body: |
+ This pull request has been tagged as a stable backport. It will be cherry-picked into the next stable point release.
+
+ Please observe the following:
+
+ * Any dependent PRs that this PR requires **must** be tagged for stable backporting as well.
+
+ * Any issue(s) this PR fixes or closes **should** target the current stable release or a previous stable release to which a fix has not yet entered the current stable release.
+
+ * This PR **must** be test cherry-picked against the current release branch (`release-X.Y.z` where X and Y are numbers). It must apply cleanly, or a diff of the expected change must be provided.
+
+ To do this, run the following commands from your local copy of the Jellyfin repository:
+
+ 1. `git checkout master`
+
+ 1. `git merge --no-ff <myPullRequestBranch>`
+
+ 1. `git log` -> `commit xxxxxxxxx`, grab hash
+
+ 1. `git checkout release-X.Y.z` replacing X and Y with the *current* stable version (e.g. `release-10.7.z`)
+
+ 1. `git cherry-pick -sx -m1 <hash>`
+
+ Ensure the `cherry-pick` applies cleanly. If it does not, fix any merge conflicts *preserving as much of the original code as possible*, and make note of the resulting diff.
+
+ Test your changes with a build to ensure they are successful. If not, adjust the diff accordingly.
+
+ **Do not** push your merges to either branch. Use `git reset --hard HEAD~1` to revert both branches to their original state.
+
+ Reply to this PR with a comment beginning "Cherry-pick test completed." and including the merge-conflict-fixing diff(s) if applicable.
diff --git a/.github/workflows/label-commenter.yml b/.github/workflows/label-commenter.yml
new file mode 100644
index 000000000..be9216cc1
--- /dev/null
+++ b/.github/workflows/label-commenter.yml
@@ -0,0 +1,22 @@
+name: Label Commenter
+
+on:
+ issues:
+ types:
+ - labeled
+ - unlabeled
+ pull_request_target:
+ types:
+ - labeled
+ - unlabeled
+
+jobs:
+ comment:
+ runs-on: ubuntu-20.04
+ steps:
+ - uses: actions/checkout@v2
+ with:
+ ref: master
+
+ - name: Label Commenter
+ uses: peaceiris/actions-label-commenter@v1
diff --git a/Emby.Dlna/ContentDirectory/ControlHandler.cs b/Emby.Dlna/ContentDirectory/ControlHandler.cs
index 713f95099..90ba601b4 100644
--- a/Emby.Dlna/ContentDirectory/ControlHandler.cs
+++ b/Emby.Dlna/ContentDirectory/ControlHandler.cs
@@ -1,5 +1,4 @@
using System;
-using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
@@ -7,7 +6,6 @@ using System.Linq;
using System.Text;
using System.Threading;
using System.Xml;
-using Emby.Dlna.Configuration;
using Emby.Dlna.Didl;
using Emby.Dlna.Service;
using Jellyfin.Data.Entities;
diff --git a/Emby.Dlna/Didl/DidlBuilder.cs b/Emby.Dlna/Didl/DidlBuilder.cs
index 8b50d47fb..c66bdbf22 100644
--- a/Emby.Dlna/Didl/DidlBuilder.cs
+++ b/Emby.Dlna/Didl/DidlBuilder.cs
@@ -208,7 +208,8 @@ namespace Emby.Dlna.Didl
var targetWidth = streamInfo.TargetWidth;
var targetHeight = streamInfo.TargetHeight;
- var contentFeatureList = new ContentFeatureBuilder(_profile).BuildVideoHeader(
+ var contentFeatureList = ContentFeatureBuilder.BuildVideoHeader(
+ _profile,
streamInfo.Container,
streamInfo.TargetVideoCodec.FirstOrDefault(),
streamInfo.TargetAudioCodec.FirstOrDefault(),
@@ -599,7 +600,8 @@ namespace Emby.Dlna.Didl
? MimeTypes.GetMimeType(filename)
: mediaProfile.MimeType;
- var contentFeatures = new ContentFeatureBuilder(_profile).BuildAudioHeader(
+ var contentFeatures = ContentFeatureBuilder.BuildAudioHeader(
+ _profile,
streamInfo.Container,
streamInfo.TargetAudioCodec.FirstOrDefault(),
targetAudioBitrate,
@@ -974,15 +976,28 @@ namespace Emby.Dlna.Didl
return;
}
- var albumartUrlInfo = GetImageUrl(imageInfo, _profile.MaxAlbumArtWidth, _profile.MaxAlbumArtHeight, "jpg");
+ // TODO: Remove these default values
+ var albumArtUrlInfo = GetImageUrl(
+ imageInfo,
+ _profile.MaxAlbumArtWidth ?? 10000,
+ _profile.MaxAlbumArtHeight ?? 10000,
+ "jpg");
writer.WriteStartElement("upnp", "albumArtURI", NsUpnp);
- writer.WriteAttributeString("dlna", "profileID", NsDlna, _profile.AlbumArtPn);
- writer.WriteString(albumartUrlInfo.url);
+ if (!string.IsNullOrEmpty(_profile.AlbumArtPn))
+ {
+ writer.WriteAttributeString("dlna", "profileID", NsDlna, _profile.AlbumArtPn);
+ }
+
+ writer.WriteString(albumArtUrlInfo.url);
writer.WriteFullEndElement();
- // TOOD: Remove these default values
- var iconUrlInfo = GetImageUrl(imageInfo, _profile.MaxIconWidth ?? 48, _profile.MaxIconHeight ?? 48, "jpg");
+ // TODO: Remove these default values
+ var iconUrlInfo = GetImageUrl(
+ imageInfo,
+ _profile.MaxIconWidth ?? 48,
+ _profile.MaxIconHeight ?? 48,
+ "jpg");
writer.WriteElementString("upnp", "icon", NsUpnp, iconUrlInfo.url);
if (!_profile.EnableAlbumArtInDidl)
@@ -1033,8 +1048,7 @@ namespace Emby.Dlna.Didl
var width = albumartUrlInfo.width ?? maxWidth;
var height = albumartUrlInfo.height ?? maxHeight;
- var contentFeatures = new ContentFeatureBuilder(_profile)
- .BuildImageHeader(format, width, height, imageInfo.IsDirectStream, org_Pn);
+ var contentFeatures = ContentFeatureBuilder.BuildImageHeader(_profile, format, width, height, imageInfo.IsDirectStream, org_Pn);
writer.WriteAttributeString(
"protocolInfo",
@@ -1206,8 +1220,7 @@ namespace Emby.Dlna.Didl
if (width.HasValue && height.HasValue)
{
- var newSize = DrawingUtils.Resize(
- new ImageDimensions(width.Value, height.Value), 0, 0, maxWidth, maxHeight);
+ var newSize = DrawingUtils.Resize(new ImageDimensions(width.Value, height.Value), 0, 0, maxWidth, maxHeight);
width = newSize.Width;
height = newSize.Height;
diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs
index 3417076dc..5f2be07cf 100644
--- a/Emby.Dlna/DlnaManager.cs
+++ b/Emby.Dlna/DlnaManager.cs
@@ -111,7 +111,7 @@ namespace Emby.Dlna
if (profile != null)
{
- _logger.LogDebug("Found matching device profile: {0}", profile.Name);
+ _logger.LogDebug("Found matching device profile: {ProfileName}", profile.Name);
}
else
{
@@ -138,80 +138,45 @@ namespace Emby.Dlna
_logger.LogInformation(builder.ToString());
}
- private bool IsMatch(DeviceIdentification deviceInfo, DeviceIdentification profileInfo)
+ /// <summary>
+ /// Attempts to match a device with a profile.
+ /// Rules:
+ /// - If the profile field has no value, the field matches irregardless of its contents.
+ /// - the profile field can be an exact match, or a reg exp.
+ /// </summary>
+ /// <param name="deviceInfo">The <see cref="DeviceIdentification"/> of the device.</param>
+ /// <param name="profileInfo">The <see cref="DeviceIdentification"/> of the profile.</param>
+ /// <returns><b>True</b> if they match.</returns>
+ public bool IsMatch(DeviceIdentification deviceInfo, DeviceIdentification profileInfo)
{
- if (!string.IsNullOrEmpty(profileInfo.FriendlyName))
- {
- if (deviceInfo.FriendlyName == null || !IsRegexOrSubstringMatch(deviceInfo.FriendlyName, profileInfo.FriendlyName))
- {
- return false;
- }
- }
-
- if (!string.IsNullOrEmpty(profileInfo.Manufacturer))
- {
- if (deviceInfo.Manufacturer == null || !IsRegexOrSubstringMatch(deviceInfo.Manufacturer, profileInfo.Manufacturer))
- {
- return false;
- }
- }
-
- if (!string.IsNullOrEmpty(profileInfo.ManufacturerUrl))
- {
- if (deviceInfo.ManufacturerUrl == null || !IsRegexOrSubstringMatch(deviceInfo.ManufacturerUrl, profileInfo.ManufacturerUrl))
- {
- return false;
- }
- }
-
- if (!string.IsNullOrEmpty(profileInfo.ModelDescription))
- {
- if (deviceInfo.ModelDescription == null || !IsRegexOrSubstringMatch(deviceInfo.ModelDescription, profileInfo.ModelDescription))
- {
- return false;
- }
- }
-
- if (!string.IsNullOrEmpty(profileInfo.ModelName))
- {
- if (deviceInfo.ModelName == null || !IsRegexOrSubstringMatch(deviceInfo.ModelName, profileInfo.ModelName))
- {
- return false;
- }
- }
-
- if (!string.IsNullOrEmpty(profileInfo.ModelNumber))
- {
- if (deviceInfo.ModelNumber == null || !IsRegexOrSubstringMatch(deviceInfo.ModelNumber, profileInfo.ModelNumber))
- {
- return false;
- }
- }
+ return IsRegexOrSubstringMatch(deviceInfo.FriendlyName, profileInfo.FriendlyName)
+ && IsRegexOrSubstringMatch(deviceInfo.Manufacturer, profileInfo.Manufacturer)
+ && IsRegexOrSubstringMatch(deviceInfo.ManufacturerUrl, profileInfo.ManufacturerUrl)
+ && IsRegexOrSubstringMatch(deviceInfo.ModelDescription, profileInfo.ModelDescription)
+ && IsRegexOrSubstringMatch(deviceInfo.ModelName, profileInfo.ModelName)
+ && IsRegexOrSubstringMatch(deviceInfo.ModelNumber, profileInfo.ModelNumber)
+ && IsRegexOrSubstringMatch(deviceInfo.ModelUrl, profileInfo.ModelUrl)
+ && IsRegexOrSubstringMatch(deviceInfo.SerialNumber, profileInfo.SerialNumber);
+ }
- if (!string.IsNullOrEmpty(profileInfo.ModelUrl))
+ private bool IsRegexOrSubstringMatch(string input, string pattern)
+ {
+ if (string.IsNullOrEmpty(pattern))
{
- if (deviceInfo.ModelUrl == null || !IsRegexOrSubstringMatch(deviceInfo.ModelUrl, profileInfo.ModelUrl))
- {
- return false;
- }
+ // In profile identification: An empty pattern matches anything.
+ return true;
}
- if (!string.IsNullOrEmpty(profileInfo.SerialNumber))
+ if (string.IsNullOrEmpty(input))
{
- if (deviceInfo.SerialNumber == null || !IsRegexOrSubstringMatch(deviceInfo.SerialNumber, profileInfo.SerialNumber))
- {
- return false;
- }
+ // The profile contains a value, and the device doesn't.
+ return false;
}
- return true;
- }
-
- private bool IsRegexOrSubstringMatch(string input, string pattern)
- {
try
{
- return input.Contains(pattern, StringComparison.OrdinalIgnoreCase) || Regex.IsMatch(input, pattern, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
+ return input.Equals(pattern, StringComparison.OrdinalIgnoreCase)
+ || Regex.IsMatch(input, pattern, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
}
catch (ArgumentException ex)
{
diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs
index d3e9a41ec..bdfe430cf 100644
--- a/Emby.Dlna/Main/DlnaEntryPoint.cs
+++ b/Emby.Dlna/Main/DlnaEntryPoint.cs
@@ -5,7 +5,6 @@ using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Net.Sockets;
-using System.Threading;
using System.Threading.Tasks;
using Emby.Dlna.PlayTo;
using Emby.Dlna.Ssdp;
diff --git a/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs b/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs
index 37840cd09..f3789a791 100644
--- a/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs
+++ b/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs
@@ -1,7 +1,6 @@
using System.Collections.Generic;
using Emby.Dlna.Common;
using Emby.Dlna.Service;
-using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.MediaReceiverRegistrar
{
diff --git a/Emby.Dlna/PlayTo/PlayToController.cs b/Emby.Dlna/PlayTo/PlayToController.cs
index 25ba18add..ee09cc65a 100644
--- a/Emby.Dlna/PlayTo/PlayToController.cs
+++ b/Emby.Dlna/PlayTo/PlayToController.cs
@@ -499,8 +499,8 @@ namespace Emby.Dlna.PlayTo
if (streamInfo.MediaType == DlnaProfileType.Audio)
{
- return new ContentFeatureBuilder(profile)
- .BuildAudioHeader(
+ return ContentFeatureBuilder.BuildAudioHeader(
+ profile,
streamInfo.Container,
streamInfo.TargetAudioCodec.FirstOrDefault(),
streamInfo.TargetAudioBitrate,
@@ -514,8 +514,8 @@ namespace Emby.Dlna.PlayTo
if (streamInfo.MediaType == DlnaProfileType.Video)
{
- var list = new ContentFeatureBuilder(profile)
- .BuildVideoHeader(
+ var list = ContentFeatureBuilder.BuildVideoHeader(
+ profile,
streamInfo.Container,
streamInfo.TargetVideoCodec.FirstOrDefault(),
streamInfo.TargetAudioCodec.FirstOrDefault(),
diff --git a/Emby.Dlna/PlayTo/PlayToManager.cs b/Emby.Dlna/PlayTo/PlayToManager.cs
index 8272e505a..f2f526221 100644
--- a/Emby.Dlna/PlayTo/PlayToManager.cs
+++ b/Emby.Dlna/PlayTo/PlayToManager.cs
@@ -188,7 +188,7 @@ namespace Emby.Dlna.PlayTo
_sessionManager.UpdateDeviceName(sessionInfo.Id, deviceName);
- string serverAddress = _appHost.GetSmartApiUrl(info.LocalIpAddress);
+ string serverAddress = _appHost.GetSmartApiUrl(info.RemoteIpAddress);
controller = new PlayToController(
sessionInfo,
diff --git a/Emby.Dlna/PlayTo/SsdpHttpClient.cs b/Emby.Dlna/PlayTo/SsdpHttpClient.cs
index e750f5bbc..d9f1ce490 100644
--- a/Emby.Dlna/PlayTo/SsdpHttpClient.cs
+++ b/Emby.Dlna/PlayTo/SsdpHttpClient.cs
@@ -2,7 +2,6 @@
using System;
using System.Globalization;
-using System.IO;
using System.Net.Http;
using System.Net.Mime;
using System.Text;
diff --git a/Emby.Dlna/Ssdp/DeviceDiscovery.cs b/Emby.Dlna/Ssdp/DeviceDiscovery.cs
index d13871add..d9c6a93c7 100644
--- a/Emby.Dlna/Ssdp/DeviceDiscovery.cs
+++ b/Emby.Dlna/Ssdp/DeviceDiscovery.cs
@@ -104,7 +104,7 @@ namespace Emby.Dlna.Ssdp
{
Location = e.DiscoveredDevice.DescriptionLocation,
Headers = headers,
- LocalIpAddress = e.LocalIpAddress
+ RemoteIpAddress = e.RemoteIpAddress
});
DeviceDiscoveredInternal?.Invoke(this, args);
diff --git a/Emby.Naming/TV/EpisodeResolver.cs b/Emby.Naming/TV/EpisodeResolver.cs
index f7df58786..c63aec64e 100644
--- a/Emby.Naming/TV/EpisodeResolver.cs
+++ b/Emby.Naming/TV/EpisodeResolver.cs
@@ -68,6 +68,11 @@ namespace Emby.Naming.TV
var parsingResult = new EpisodePathParser(_options)
.Parse(path, isDirectory, isNamed, isOptimistic, supportsAbsoluteNumbers, fillExtendedInfo);
+ if (!parsingResult.Success && !isStub)
+ {
+ return null;
+ }
+
return new EpisodeInfo(path)
{
Container = container,
diff --git a/Emby.Server.Implementations/AppBase/ConfigurationHelper.cs b/Emby.Server.Implementations/AppBase/ConfigurationHelper.cs
index 3f7076383..29bac6634 100644
--- a/Emby.Server.Implementations/AppBase/ConfigurationHelper.cs
+++ b/Emby.Server.Implementations/AppBase/ConfigurationHelper.cs
@@ -3,7 +3,6 @@
using System;
using System.IO;
using System.Linq;
-using MediaBrowser.Common.Extensions;
using MediaBrowser.Model.Serialization;
namespace Emby.Server.Implementations.AppBase
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index 0512adf10..75d8fc113 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -335,10 +335,7 @@ namespace Emby.Server.Implementations
{
get
{
- if (_deviceId == null)
- {
- _deviceId = new DeviceId(ApplicationPaths, LoggerFactory);
- }
+ _deviceId ??= new DeviceId(ApplicationPaths, LoggerFactory);
return _deviceId.Value;
}
@@ -370,10 +367,7 @@ namespace Emby.Server.Implementations
/// <returns>System.Object.</returns>
protected object CreateInstanceSafe(Type type)
{
- if (_creatingInstances == null)
- {
- _creatingInstances = new List<Type>();
- }
+ _creatingInstances ??= new List<Type>();
if (_creatingInstances.IndexOf(type) != -1)
{
@@ -607,12 +601,8 @@ namespace Emby.Server.Implementations
ServiceCollection.AddSingleton<IAuthenticationRepository, AuthenticationRepository>();
- // TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required
- ServiceCollection.AddTransient(provider => new Lazy<IDtoService>(provider.GetRequiredService<IDtoService>));
-
- // TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required
- ServiceCollection.AddTransient(provider => new Lazy<EncodingHelper>(provider.GetRequiredService<EncodingHelper>));
ServiceCollection.AddSingleton<IMediaEncoder, MediaBrowser.MediaEncoding.Encoder.MediaEncoder>();
+ ServiceCollection.AddSingleton<EncodingHelper>();
// TODO: Refactor to eliminate the circular dependencies here so that Lazy<T> isn't required
ServiceCollection.AddTransient(provider => new Lazy<ILibraryMonitor>(provider.GetRequiredService<ILibraryMonitor>));
@@ -677,8 +667,6 @@ namespace Emby.Server.Implementations
ServiceCollection.AddSingleton<ISubtitleEncoder, MediaBrowser.MediaEncoding.Subtitles.SubtitleEncoder>();
- ServiceCollection.AddSingleton<EncodingHelper>();
-
ServiceCollection.AddSingleton<IAttachmentExtractor, MediaBrowser.MediaEncoding.Attachments.AttachmentExtractor>();
ServiceCollection.AddSingleton<TranscodingJobHelper>();
diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs
index 87ebe960a..7324b0ee9 100644
--- a/Emby.Server.Implementations/Channels/ChannelManager.cs
+++ b/Emby.Server.Implementations/Channels/ChannelManager.cs
@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
-using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs
index db532ce5b..1b85a9d4b 100644
--- a/Emby.Server.Implementations/Collections/CollectionManager.cs
+++ b/Emby.Server.Implementations/Collections/CollectionManager.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
-using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
@@ -8,11 +7,9 @@ using System.Threading.Tasks;
using Jellyfin.Data.Entities;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Collections;
-using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
@@ -124,7 +121,7 @@ namespace Emby.Server.Implementations.Collections
private IEnumerable<BoxSet> GetCollections(User user)
{
- var folder = GetCollectionsFolder(false).Result;
+ var folder = GetCollectionsFolder(false).GetAwaiter().GetResult();
return folder == null
? Enumerable.Empty<BoxSet>()
@@ -319,11 +316,11 @@ namespace Emby.Server.Implementations.Collections
{
var results = new Dictionary<Guid, BaseItem>();
- var allBoxsets = GetCollections(user).ToList();
+ var allBoxSets = GetCollections(user).ToList();
foreach (var item in items)
{
- if (!(item is ISupportsBoxSetGrouping))
+ if (item is not ISupportsBoxSetGrouping)
{
results[item.Id] = item;
}
@@ -331,34 +328,45 @@ namespace Emby.Server.Implementations.Collections
{
var itemId = item.Id;
- var currentBoxSets = allBoxsets
- .Where(i => i.ContainsLinkedChildByItemId(itemId))
- .ToList();
-
- if (currentBoxSets.Count > 0)
+ var itemIsInBoxSet = false;
+ foreach (var boxSet in allBoxSets)
{
- foreach (var boxset in currentBoxSets)
+ if (!boxSet.ContainsLinkedChildByItemId(itemId))
{
- results[boxset.Id] = boxset;
+ continue;
}
+
+ itemIsInBoxSet = true;
+
+ results.TryAdd(boxSet.Id, boxSet);
+ }
+
+ // skip any item that is in a box set
+ if (itemIsInBoxSet)
+ {
+ continue;
}
- else
+
+ var alreadyInResults = false;
+ // this is kind of a performance hack because only Video has alternate versions that should be in a box set?
+ if (item is Video video)
{
- var alreadyInResults = false;
- foreach (var child in item.GetMediaSources(true))
+ foreach (var childId in video.GetLocalAlternateVersionIds())
{
- if (Guid.TryParse(child.Id, out var id) && results.ContainsKey(id))
+ if (!results.ContainsKey(childId))
{
- alreadyInResults = true;
- break;
+ continue;
}
- }
- if (!alreadyInResults)
- {
- results[item.Id] = item;
+ alreadyInResults = true;
+ break;
}
}
+
+ if (!alreadyInResults)
+ {
+ results[itemId] = item;
+ }
}
}
diff --git a/Emby.Server.Implementations/ConfigurationOptions.cs b/Emby.Server.Implementations/ConfigurationOptions.cs
index cd9dbb1bd..01dc728c1 100644
--- a/Emby.Server.Implementations/ConfigurationOptions.cs
+++ b/Emby.Server.Implementations/ConfigurationOptions.cs
@@ -1,5 +1,4 @@
using System.Collections.Generic;
-using Emby.Server.Implementations.HttpServer;
using static MediaBrowser.Controller.Extensions.ConfigurationExtensions;
namespace Emby.Server.Implementations
diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
index 694805ebe..72c9d82ad 100644
--- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
@@ -1,6 +1,7 @@
#pragma warning disable CS1591
using System;
+using System.Buffers.Text;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
@@ -502,7 +503,7 @@ namespace Emby.Server.Implementations.Data
using (var saveImagesStatement = base.PrepareStatement(db, "Update TypedBaseItems set Images=@Images where guid=@Id"))
{
saveImagesStatement.TryBind("@Id", item.Id.ToByteArray());
- saveImagesStatement.TryBind("@Images", SerializeImages(item));
+ saveImagesStatement.TryBind("@Images", SerializeImages(item.ImageInfos));
saveImagesStatement.MoveNext();
}
@@ -897,8 +898,8 @@ namespace Emby.Server.Implementations.Data
saveItemStatement.TryBind("@ExternalSeriesId", item.ExternalSeriesId);
saveItemStatement.TryBind("@Tagline", item.Tagline);
- saveItemStatement.TryBind("@ProviderIds", SerializeProviderIds(item));
- saveItemStatement.TryBind("@Images", SerializeImages(item));
+ saveItemStatement.TryBind("@ProviderIds", SerializeProviderIds(item.ProviderIds));
+ saveItemStatement.TryBind("@Images", SerializeImages(item.ImageInfos));
if (item.ProductionLocations.Length > 0)
{
@@ -968,10 +969,10 @@ namespace Emby.Server.Implementations.Data
saveItemStatement.MoveNext();
}
- private static string SerializeProviderIds(BaseItem item)
+ internal static string SerializeProviderIds(Dictionary<string, string> providerIds)
{
StringBuilder str = new StringBuilder();
- foreach (var i in item.ProviderIds)
+ foreach (var i in providerIds)
{
// Ideally we shouldn't need this IsNullOrWhiteSpace check,
// but we're seeing some cases of bad data slip through
@@ -995,35 +996,25 @@ namespace Emby.Server.Implementations.Data
return str.ToString();
}
- private static void DeserializeProviderIds(string value, BaseItem item)
+ internal static void DeserializeProviderIds(string value, IHasProviderIds item)
{
if (string.IsNullOrWhiteSpace(value))
{
return;
}
- if (item.ProviderIds.Count > 0)
+ foreach (var part in value.SpanSplit('|'))
{
- return;
- }
-
- var parts = value.Split('|', StringSplitOptions.RemoveEmptyEntries);
-
- foreach (var part in parts)
- {
- var idParts = part.Split('=');
-
- if (idParts.Length == 2)
+ var providerDelimiterIndex = part.IndexOf('=');
+ if (providerDelimiterIndex != -1 && providerDelimiterIndex == part.LastIndexOf('='))
{
- item.SetProviderId(idParts[0], idParts[1]);
+ item.SetProviderId(part.Slice(0, providerDelimiterIndex).ToString(), part.Slice(providerDelimiterIndex + 1).ToString());
}
}
}
- private string SerializeImages(BaseItem item)
+ internal string SerializeImages(ItemImageInfo[] images)
{
- var images = item.ImageInfos;
-
if (images.Length == 0)
{
return null;
@@ -1045,21 +1036,15 @@ namespace Emby.Server.Implementations.Data
return str.ToString();
}
- private void DeserializeImages(string value, BaseItem item)
+ internal ItemImageInfo[] DeserializeImages(string value)
{
if (string.IsNullOrWhiteSpace(value))
{
- return;
- }
-
- if (item.ImageInfos.Length > 0)
- {
- return;
+ return Array.Empty<ItemImageInfo>();
}
- var parts = value.Split('|' , StringSplitOptions.RemoveEmptyEntries);
var list = new List<ItemImageInfo>();
- foreach (var part in parts)
+ foreach (var part in value.SpanSplit('|'))
{
var image = ItemImageInfoFromValueString(part);
@@ -1069,15 +1054,14 @@ namespace Emby.Server.Implementations.Data
}
}
- item.ImageInfos = list.ToArray();
+ return list.ToArray();
}
- public void AppendItemImageInfo(StringBuilder bldr, ItemImageInfo image)
+ private void AppendItemImageInfo(StringBuilder bldr, ItemImageInfo image)
{
const char Delimiter = '*';
var path = image.Path ?? string.Empty;
- var hash = image.BlurHash ?? string.Empty;
bldr.Append(GetPathToSave(path))
.Append(Delimiter)
@@ -1087,48 +1071,105 @@ namespace Emby.Server.Implementations.Data
.Append(Delimiter)
.Append(image.Width)
.Append(Delimiter)
- .Append(image.Height)
- .Append(Delimiter)
- // Replace delimiters with other characters.
- // This can be removed when we migrate to a proper DB.
- .Append(hash.Replace('*', '/').Replace('|', '\\'));
+ .Append(image.Height);
+
+ var hash = image.BlurHash;
+ if (!string.IsNullOrEmpty(hash))
+ {
+ bldr.Append(Delimiter)
+ // Replace delimiters with other characters.
+ // This can be removed when we migrate to a proper DB.
+ .Append(hash.Replace('*', '/').Replace('|', '\\'));
+ }
}
- public ItemImageInfo ItemImageInfoFromValueString(string value)
+ internal ItemImageInfo ItemImageInfoFromValueString(ReadOnlySpan<char> value)
{
- var parts = value.Split('*', StringSplitOptions.None);
+ var nextSegment = value.IndexOf('*');
+ if (nextSegment == -1)
+ {
+ return null;
+ }
- if (parts.Length < 3)
+ ReadOnlySpan<char> path = value[..nextSegment];
+ value = value[(nextSegment + 1)..];
+ nextSegment = value.IndexOf('*');
+ if (nextSegment == -1)
{
return null;
}
- var image = new ItemImageInfo();
+ ReadOnlySpan<char> dateModified = value[..nextSegment];
+ value = value[(nextSegment + 1)..];
+ nextSegment = value.IndexOf('*');
+ if (nextSegment == -1)
+ {
+ nextSegment = value.Length;
+ }
+
+ ReadOnlySpan<char> imageType = value[..nextSegment];
- image.Path = RestorePath(parts[0]);
+ var image = new ItemImageInfo
+ {
+ Path = RestorePath(path.ToString())
+ };
- if (long.TryParse(parts[1], NumberStyles.Any, CultureInfo.InvariantCulture, out var ticks))
+ if (long.TryParse(dateModified, NumberStyles.Any, CultureInfo.InvariantCulture, out var ticks))
{
image.DateModified = new DateTime(ticks, DateTimeKind.Utc);
}
- if (Enum.TryParse(parts[2], true, out ImageType type))
+ if (Enum.TryParse(imageType.ToString(), true, out ImageType type))
{
image.Type = type;
}
- if (parts.Length >= 5)
+ // Optional parameters: width*height*blurhash
+ if (nextSegment + 1 < value.Length - 1)
{
- if (int.TryParse(parts[3], NumberStyles.Integer, CultureInfo.InvariantCulture, out var width)
- && int.TryParse(parts[4], NumberStyles.Integer, CultureInfo.InvariantCulture, out var height))
+ value = value[(nextSegment + 1)..];
+ nextSegment = value.IndexOf('*');
+ if (nextSegment == -1 || nextSegment == value.Length)
+ {
+ return image;
+ }
+
+ ReadOnlySpan<char> widthSpan = value[..nextSegment];
+
+ value = value[(nextSegment + 1)..];
+ nextSegment = value.IndexOf('*');
+ if (nextSegment == -1)
+ {
+ nextSegment = value.Length;
+ }
+
+ ReadOnlySpan<char> heightSpan = value[..nextSegment];
+
+ if (int.TryParse(widthSpan, NumberStyles.Integer, CultureInfo.InvariantCulture, out var width)
+ && int.TryParse(heightSpan, NumberStyles.Integer, CultureInfo.InvariantCulture, out var height))
{
image.Width = width;
image.Height = height;
}
- if (parts.Length >= 6)
+ if (nextSegment < value.Length - 1)
{
- image.BlurHash = parts[5].Replace('/', '*').Replace('\\', '|');
+ value = value[(nextSegment + 1)..];
+ var length = value.Length;
+
+ Span<char> blurHashSpan = stackalloc char[length];
+ for (int i = 0; i < length; i++)
+ {
+ var c = value[i];
+ blurHashSpan[i] = c switch
+ {
+ '/' => '*',
+ '\\' => '|',
+ _ => c
+ };
+ }
+
+ image.BlurHash = new string(blurHashSpan);
}
}
@@ -1311,7 +1352,14 @@ namespace Emby.Server.Implementations.Data
if (!reader.IsDBNull(index))
{
- item.ChannelId = new Guid(reader.GetString(index));
+ if (!Utf8Parser.TryParse(reader[index].ToBlob(), out Guid value, out _, standardFormat: 'N'))
+ {
+ var str = reader.GetString(index);
+ Logger.LogWarning("{ChannelId} isn't in the expected format", str);
+ value = new Guid(str);
+ }
+
+ item.ChannelId = value;
}
index++;
@@ -1790,7 +1838,7 @@ namespace Emby.Server.Implementations.Data
index++;
}
- if (!reader.IsDBNull(index))
+ if (item.ProviderIds.Count == 0 && !reader.IsDBNull(index))
{
DeserializeProviderIds(reader.GetString(index), item);
}
@@ -1799,9 +1847,9 @@ namespace Emby.Server.Implementations.Data
if (query.DtoOptions.EnableImages)
{
- if (!reader.IsDBNull(index))
+ if (item.ImageInfos.Length == 0 && !reader.IsDBNull(index))
{
- DeserializeImages(reader.GetString(index), item);
+ item.ImageInfos = DeserializeImages(reader.GetString(index));
}
index++;
@@ -2116,30 +2164,7 @@ namespace Emby.Server.Implementations.Data
|| query.IsLiked.HasValue;
}
- private readonly ItemFields[] _allFields = Enum.GetNames(typeof(ItemFields))
- .Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true))
- .ToArray();
-
- private string[] GetColumnNamesFromField(ItemFields field)
- {
- switch (field)
- {
- case ItemFields.Settings:
- return new[] { "IsLocked", "PreferredMetadataCountryCode", "PreferredMetadataLanguage", "LockedFields" };
- case ItemFields.ServiceName:
- return new[] { "ExternalServiceId" };
- case ItemFields.SortName:
- return new[] { "ForcedSortName" };
- case ItemFields.Taglines:
- return new[] { "Tagline" };
- case ItemFields.Tags:
- return new[] { "Tags" };
- case ItemFields.IsHD:
- return Array.Empty<string>();
- default:
- return new[] { field.ToString() };
- }
- }
+ private readonly ItemFields[] _allFields = Enum.GetValues<ItemFields>();
private bool HasField(InternalItemsQuery query, ItemFields name)
{
@@ -2329,9 +2354,32 @@ namespace Emby.Server.Implementations.Data
{
if (!HasField(query, field))
{
- foreach (var fieldToRemove in GetColumnNamesFromField(field))
+ switch (field)
{
- list.Remove(fieldToRemove);
+ case ItemFields.Settings:
+ list.Remove("IsLocked");
+ list.Remove("PreferredMetadataCountryCode");
+ list.Remove("PreferredMetadataLanguage");
+ list.Remove("LockedFields");
+ break;
+ case ItemFields.ServiceName:
+ list.Remove("ExternalServiceId");
+ break;
+ case ItemFields.SortName:
+ list.Remove("ForcedSortName");
+ break;
+ case ItemFields.Taglines:
+ list.Remove("Tagline");
+ break;
+ case ItemFields.Tags:
+ list.Remove("Tags");
+ break;
+ case ItemFields.IsHD:
+ // do nothing
+ break;
+ default:
+ list.Remove(field.ToString());
+ break;
}
}
}
@@ -2578,9 +2626,9 @@ namespace Emby.Server.Implementations.Data
}
var commandText = "select "
- + string.Join(',', GetFinalColumnsToSelect(query, new[] { "count(distinct PresentationUniqueKey)" }))
- + GetFromText()
- + GetJoinUserDataText(query);
+ + string.Join(',', GetFinalColumnsToSelect(query, new[] { "count(distinct PresentationUniqueKey)" }))
+ + GetFromText()
+ + GetJoinUserDataText(query);
var whereClauses = GetWhereClauses(query, null);
if (whereClauses.Count != 0)
@@ -2721,87 +2769,22 @@ namespace Emby.Server.Implementations.Data
private string FixUnicodeChars(string buffer)
{
- if (buffer.IndexOf('\u2013', StringComparison.Ordinal) > -1)
- {
- buffer = buffer.Replace('\u2013', '-'); // en dash
- }
-
- if (buffer.IndexOf('\u2014', StringComparison.Ordinal) > -1)
- {
- buffer = buffer.Replace('\u2014', '-'); // em dash
- }
-
- if (buffer.IndexOf('\u2015', StringComparison.Ordinal) > -1)
- {
- buffer = buffer.Replace('\u2015', '-'); // horizontal bar
- }
-
- if (buffer.IndexOf('\u2017', StringComparison.Ordinal) > -1)
- {
- buffer = buffer.Replace('\u2017', '_'); // double low line
- }
-
- if (buffer.IndexOf('\u2018', StringComparison.Ordinal) > -1)
- {
- buffer = buffer.Replace('\u2018', '\''); // left single quotation mark
- }
-
- if (buffer.IndexOf('\u2019', StringComparison.Ordinal) > -1)
- {
- buffer = buffer.Replace('\u2019', '\''); // right single quotation mark
- }
-
- if (buffer.IndexOf('\u201a', StringComparison.Ordinal) > -1)
- {
- buffer = buffer.Replace('\u201a', ','); // single low-9 quotation mark
- }
-
- if (buffer.IndexOf('\u201b', StringComparison.Ordinal) > -1)
- {
- buffer = buffer.Replace('\u201b', '\''); // single high-reversed-9 quotation mark
- }
-
- if (buffer.IndexOf('\u201c', StringComparison.Ordinal) > -1)
- {
- buffer = buffer.Replace('\u201c', '\"'); // left double quotation mark
- }
-
- if (buffer.IndexOf('\u201d', StringComparison.Ordinal) > -1)
- {
- buffer = buffer.Replace('\u201d', '\"'); // right double quotation mark
- }
-
- if (buffer.IndexOf('\u201e', StringComparison.Ordinal) > -1)
- {
- buffer = buffer.Replace('\u201e', '\"'); // double low-9 quotation mark
- }
-
- if (buffer.IndexOf('\u2026', StringComparison.Ordinal) > -1)
- {
- buffer = buffer.Replace("\u2026", "...", StringComparison.Ordinal); // horizontal ellipsis
- }
-
- if (buffer.IndexOf('\u2032', StringComparison.Ordinal) > -1)
- {
- buffer = buffer.Replace('\u2032', '\''); // prime
- }
-
- if (buffer.IndexOf('\u2033', StringComparison.Ordinal) > -1)
- {
- buffer = buffer.Replace('\u2033', '\"'); // double prime
- }
-
- if (buffer.IndexOf('\u0060', StringComparison.Ordinal) > -1)
- {
- buffer = buffer.Replace('\u0060', '\''); // grave accent
- }
-
- if (buffer.IndexOf('\u00B4', StringComparison.Ordinal) > -1)
- {
- buffer = buffer.Replace('\u00B4', '\''); // acute accent
- }
-
- return buffer;
+ buffer = buffer.Replace('\u2013', '-'); // en dash
+ buffer = buffer.Replace('\u2014', '-'); // em dash
+ buffer = buffer.Replace('\u2015', '-'); // horizontal bar
+ buffer = buffer.Replace('\u2017', '_'); // double low line
+ buffer = buffer.Replace('\u2018', '\''); // left single quotation mark
+ buffer = buffer.Replace('\u2019', '\''); // right single quotation mark
+ buffer = buffer.Replace('\u201a', ','); // single low-9 quotation mark
+ buffer = buffer.Replace('\u201b', '\''); // single high-reversed-9 quotation mark
+ buffer = buffer.Replace('\u201c', '\"'); // left double quotation mark
+ buffer = buffer.Replace('\u201d', '\"'); // right double quotation mark
+ buffer = buffer.Replace('\u201e', '\"'); // double low-9 quotation mark
+ buffer = buffer.Replace("\u2026", "...", StringComparison.Ordinal); // horizontal ellipsis
+ buffer = buffer.Replace('\u2032', '\''); // prime
+ buffer = buffer.Replace('\u2033', '\"'); // double prime
+ buffer = buffer.Replace('\u0060', '\''); // grave accent
+ return buffer.Replace('\u00B4', '\''); // acute accent
}
private void AddItem(List<BaseItem> items, BaseItem newItem)
@@ -3584,11 +3567,11 @@ namespace Emby.Server.Implementations.Data
statement?.TryBind("@IsFolder", query.IsFolder);
}
- var includeTypes = query.IncludeItemTypes.SelectMany(MapIncludeItemTypes).ToArray();
+ var includeTypes = query.IncludeItemTypes.Select(MapIncludeItemTypes).Where(x => x != null).ToArray();
// Only specify excluded types if no included types are specified
if (includeTypes.Length == 0)
{
- var excludeTypes = query.ExcludeItemTypes.SelectMany(MapIncludeItemTypes).ToArray();
+ var excludeTypes = query.ExcludeItemTypes.Select(MapIncludeItemTypes).Where(x => x != null).ToArray();
if (excludeTypes.Length == 1)
{
whereClauses.Add("type<>@type");
@@ -4532,7 +4515,7 @@ namespace Emby.Server.Implementations.Data
whereClauses.Add(GetProviderIdClause(query.HasTvdbId.Value, "tvdb"));
}
- var includedItemByNameTypes = GetItemByNameTypesInQuery(query).SelectMany(MapIncludeItemTypes).ToList();
+ var includedItemByNameTypes = GetItemByNameTypesInQuery(query);
var enableItemsByName = (query.IncludeItemsByName ?? false) && includedItemByNameTypes.Count > 0;
var queryTopParentIds = query.TopParentIds;
@@ -4790,27 +4773,27 @@ namespace Emby.Server.Implementations.Data
if (IsTypeInQuery(nameof(Person), query))
{
- list.Add(nameof(Person));
+ list.Add(typeof(Person).FullName);
}
if (IsTypeInQuery(nameof(Genre), query))
{
- list.Add(nameof(Genre));
+ list.Add(typeof(Genre).FullName);
}
if (IsTypeInQuery(nameof(MusicGenre), query))
{
- list.Add(nameof(MusicGenre));
+ list.Add(typeof(MusicGenre).FullName);
}
if (IsTypeInQuery(nameof(MusicArtist), query))
{
- list.Add(nameof(MusicArtist));
+ list.Add(typeof(MusicArtist).FullName);
}
if (IsTypeInQuery(nameof(Studio), query))
{
- list.Add(nameof(Studio));
+ list.Add(typeof(Studio).FullName);
}
return list;
@@ -4915,15 +4898,10 @@ namespace Emby.Server.Implementations.Data
typeof(AggregateFolder)
};
- public void UpdateInheritedValues(CancellationToken cancellationToken)
- {
- UpdateInheritedTags(cancellationToken);
- }
-
- private void UpdateInheritedTags(CancellationToken cancellationToken)
+ public void UpdateInheritedValues()
{
string sql = string.Join(
- ";",
+ ';',
new string[]
{
"delete from itemvalues where type = 6",
@@ -4946,37 +4924,38 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
}
}
- private static Dictionary<string, string[]> GetTypeMapDictionary()
+ private static Dictionary<string, string> GetTypeMapDictionary()
{
- var dict = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase);
+ var dict = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
foreach (var t in _knownTypes)
{
- dict[t.Name] = new[] { t.FullName };
+ dict[t.Name] = t.FullName ;
}
- dict["Program"] = new[] { typeof(LiveTvProgram).FullName };
- dict["TvChannel"] = new[] { typeof(LiveTvChannel).FullName };
+ dict["Program"] = typeof(LiveTvProgram).FullName;
+ dict["TvChannel"] = typeof(LiveTvChannel).FullName;
return dict;
}
// Not crazy about having this all the way down here, but at least it's in one place
- private readonly Dictionary<string, string[]> _types = GetTypeMapDictionary();
+ private readonly Dictionary<string, string> _types = GetTypeMapDictionary();
- private string[] MapIncludeItemTypes(string value)
+ private string MapIncludeItemTypes(string value)
{
- if (_types.TryGetValue(value, out string[] result))
+ if (_types.TryGetValue(value, out string result))
{
return result;
}
if (IsValidType(value))
{
- return new[] { value };
+ return value;
}
- return Array.Empty<string>();
+ Logger.LogWarning("Unknown item type: {ItemType}", value);
+ return null;
}
public void DeleteItem(Guid id)
@@ -5279,31 +5258,46 @@ AND Type = @InternalPersonType)");
public List<string> GetStudioNames()
{
- return GetItemValueNames(new[] { 3 }, new List<string>(), new List<string>());
+ return GetItemValueNames(new[] { 3 }, Array.Empty<string>(), Array.Empty<string>());
}
public List<string> GetAllArtistNames()
{
- return GetItemValueNames(new[] { 0, 1 }, new List<string>(), new List<string>());
+ return GetItemValueNames(new[] { 0, 1 }, Array.Empty<string>(), Array.Empty<string>());
}
public List<string> GetMusicGenreNames()
{
- return GetItemValueNames(new[] { 2 }, new List<string> { "Audio", "MusicVideo", "MusicAlbum", "MusicArtist" }, new List<string>());
+ return GetItemValueNames(
+ new[] { 2 },
+ new string[]
+ {
+ typeof(Audio).FullName,
+ typeof(MusicVideo).FullName,
+ typeof(MusicAlbum).FullName,
+ typeof(MusicArtist).FullName
+ },
+ Array.Empty<string>());
}
public List<string> GetGenreNames()
{
- return GetItemValueNames(new[] { 2 }, new List<string>(), new List<string> { "Audio", "MusicVideo", "MusicAlbum", "MusicArtist" });
+ return GetItemValueNames(
+ new[] { 2 },
+ Array.Empty<string>(),
+ new string[]
+ {
+ typeof(Audio).FullName,
+ typeof(MusicVideo).FullName,
+ typeof(MusicAlbum).FullName,
+ typeof(MusicArtist).FullName
+ });
}
- private List<string> GetItemValueNames(int[] itemValueTypes, List<string> withItemTypes, List<string> excludeItemTypes)
+ private List<string> GetItemValueNames(int[] itemValueTypes, IReadOnlyList<string> withItemTypes, IReadOnlyList<string> excludeItemTypes)
{
CheckDisposed();
- withItemTypes = withItemTypes.SelectMany(MapIncludeItemTypes).ToList();
- excludeItemTypes = excludeItemTypes.SelectMany(MapIncludeItemTypes).ToList();
-
var now = DateTime.UtcNow;
var typeClause = itemValueTypes.Length == 1 ?
@@ -5809,7 +5803,10 @@ AND Type = @InternalPersonType)");
var endIndex = Math.Min(people.Count, startIndex + Limit);
for (var i = startIndex; i < endIndex; i++)
{
- insertText.AppendFormat("(@ItemId, @Name{0}, @Role{0}, @PersonType{0}, @SortOrder{0}, @ListOrder{0}),", i.ToString(CultureInfo.InvariantCulture));
+ insertText.AppendFormat(
+ CultureInfo.InvariantCulture,
+ "(@ItemId, @Name{0}, @Role{0}, @PersonType{0}, @SortOrder{0}, @ListOrder{0}),",
+ i.ToString(CultureInfo.InvariantCulture));
}
// Remove last comma
@@ -6261,7 +6258,7 @@ AND Type = @InternalPersonType)");
CheckDisposed();
if (id == Guid.Empty)
{
- throw new ArgumentException(nameof(id));
+ throw new ArgumentException("Guid can't be empty.", nameof(id));
}
if (attachments == null)
diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs
index 54b18a8c8..4ae35039a 100644
--- a/Emby.Server.Implementations/Dto/DtoService.cs
+++ b/Emby.Server.Implementations/Dto/DtoService.cs
@@ -665,10 +665,7 @@ namespace Emby.Server.Implementations.Dto
var tag = GetImageCacheTag(item, image);
if (!string.IsNullOrEmpty(image.BlurHash))
{
- if (dto.ImageBlurHashes == null)
- {
- dto.ImageBlurHashes = new Dictionary<ImageType, Dictionary<string, string>>();
- }
+ dto.ImageBlurHashes ??= new Dictionary<ImageType, Dictionary<string, string>>();
if (!dto.ImageBlurHashes.ContainsKey(image.Type))
{
@@ -702,10 +699,7 @@ namespace Emby.Server.Implementations.Dto
if (hashes.Count > 0)
{
- if (dto.ImageBlurHashes == null)
- {
- dto.ImageBlurHashes = new Dictionary<ImageType, Dictionary<string, string>>();
- }
+ dto.ImageBlurHashes ??= new Dictionary<ImageType, Dictionary<string, string>>();
dto.ImageBlurHashes[imageType] = hashes;
}
@@ -898,10 +892,7 @@ namespace Emby.Server.Implementations.Dto
dto.Taglines = new string[] { item.Tagline };
}
- if (dto.Taglines == null)
- {
- dto.Taglines = Array.Empty<string>();
- }
+ dto.Taglines ??= Array.Empty<string>();
}
dto.Type = item.GetBaseItemKind();
diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
index 9248053f5..b8a544b8c 100644
--- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj
+++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
@@ -29,9 +29,9 @@
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="5.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="5.0.3" />
<PackageReference Include="Mono.Nat" Version="3.0.1" />
- <PackageReference Include="prometheus-net.DotNetRuntime" Version="3.4.1" />
- <PackageReference Include="sharpcompress" Version="0.28.1" />
- <PackageReference Include="SQLitePCL.pretty.netstandard" Version="2.1.0" />
+ <PackageReference Include="prometheus-net.DotNetRuntime" Version="4.0.0" />
+ <PackageReference Include="sharpcompress" Version="0.28.2" />
+ <PackageReference Include="SQLitePCL.pretty.netstandard" Version="2.2.0" />
<PackageReference Include="DotNet.Glob" Version="3.1.2" />
</ItemGroup>
diff --git a/Emby.Server.Implementations/HttpServer/Security/AuthService.cs b/Emby.Server.Implementations/HttpServer/Security/AuthService.cs
index 4a0fc8239..9afabf527 100644
--- a/Emby.Server.Implementations/HttpServer/Security/AuthService.cs
+++ b/Emby.Server.Implementations/HttpServer/Security/AuthService.cs
@@ -1,6 +1,5 @@
#pragma warning disable CS1591
-using System;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.Net;
diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs
index 3893a1577..27096ed33 100644
--- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs
+++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs
@@ -2,11 +2,10 @@
using System;
using System.Collections.Generic;
-using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
-using System.Text;
+using System.Runtime.InteropServices;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.System;
@@ -24,7 +23,7 @@ namespace Emby.Server.Implementations.IO
private readonly List<IShortcutHandler> _shortcutHandlers = new List<IShortcutHandler>();
private readonly string _tempPath;
- private readonly bool _isEnvironmentCaseInsensitive;
+ private static readonly bool _isEnvironmentCaseInsensitive = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
public ManagedFileSystem(
ILogger<ManagedFileSystem> logger,
@@ -32,8 +31,6 @@ namespace Emby.Server.Implementations.IO
{
Logger = logger;
_tempPath = applicationPaths.TempDirectory;
-
- _isEnvironmentCaseInsensitive = OperatingSystem.Id == OperatingSystemId.Windows;
}
public virtual void AddShortcutHandler(IShortcutHandler handler)
@@ -55,7 +52,7 @@ namespace Emby.Server.Implementations.IO
}
var extension = Path.GetExtension(filename);
- return _shortcutHandlers.Any(i => string.Equals(extension, i.Extension, _isEnvironmentCaseInsensitive ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal));
+ return _shortcutHandlers.Any(i => string.Equals(extension, i.Extension, StringComparison.OrdinalIgnoreCase));
}
/// <summary>
@@ -72,7 +69,7 @@ namespace Emby.Server.Implementations.IO
}
var extension = Path.GetExtension(filename);
- var handler = _shortcutHandlers.FirstOrDefault(i => string.Equals(extension, i.Extension, StringComparison.OrdinalIgnoreCase));
+ var handler = _shortcutHandlers.Find(i => string.Equals(extension, i.Extension, StringComparison.OrdinalIgnoreCase));
return handler?.Resolve(filename);
}
@@ -263,8 +260,6 @@ namespace Emby.Server.Implementations.IO
result.Exists = false;
}
}
-
- result.DirectoryName = fileInfo.DirectoryName;
}
result.CreationTimeUtc = GetCreationTimeUtc(info);
@@ -303,16 +298,37 @@ namespace Emby.Server.Implementations.IO
/// <param name="filename">The filename.</param>
/// <returns>System.String.</returns>
/// <exception cref="ArgumentNullException">The filename is null.</exception>
- public virtual string GetValidFilename(string filename)
+ public string GetValidFilename(string filename)
{
- var builder = new StringBuilder(filename);
-
- foreach (var c in Path.GetInvalidFileNameChars())
+ var invalid = Path.GetInvalidFileNameChars();
+ var first = filename.IndexOfAny(invalid);
+ if (first == -1)
{
- builder = builder.Replace(c, ' ');
+ // Fast path for clean strings
+ return filename;
}
- return builder.ToString();
+ return string.Create(
+ filename.Length,
+ (filename, invalid, first),
+ (chars, state) =>
+ {
+ state.filename.AsSpan().CopyTo(chars);
+
+ chars[state.first++] = ' ';
+
+ var len = chars.Length;
+ foreach (var c in state.invalid)
+ {
+ for (int i = state.first; i < len; i++)
+ {
+ if (chars[i] == c)
+ {
+ chars[i] = ' ';
+ }
+ }
+ }
+ });
}
/// <summary>
@@ -684,20 +700,5 @@ namespace Emby.Server.Implementations.IO
AttributesToSkip = 0
};
}
-
- private static void RunProcess(string path, string args, string workingDirectory)
- {
- using (var process = Process.Start(new ProcessStartInfo
- {
- Arguments = args,
- FileName = path,
- CreateNoWindow = true,
- WorkingDirectory = workingDirectory,
- WindowStyle = ProcessWindowStyle.Normal
- }))
- {
- process.WaitForExit();
- }
- }
}
}
diff --git a/Emby.Server.Implementations/IStartupOptions.cs b/Emby.Server.Implementations/IStartupOptions.cs
index 0b823ff06..f719dc5f8 100644
--- a/Emby.Server.Implementations/IStartupOptions.cs
+++ b/Emby.Server.Implementations/IStartupOptions.cs
@@ -1,6 +1,5 @@
#pragma warning disable CS1591
#nullable enable
-using System;
namespace Emby.Server.Implementations
{
diff --git a/Emby.Server.Implementations/Images/ArtistImageProvider.cs b/Emby.Server.Implementations/Images/ArtistImageProvider.cs
index afa4ec7b1..e96b64595 100644
--- a/Emby.Server.Implementations/Images/ArtistImageProvider.cs
+++ b/Emby.Server.Implementations/Images/ArtistImageProvider.cs
@@ -2,20 +2,12 @@
using System;
using System.Collections.Generic;
-using System.Linq;
-using Emby.Server.Implementations.Images;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Drawing;
-using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Entities.Movies;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Playlists;
using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Querying;
namespace Emby.Server.Implementations.Images
{
diff --git a/Emby.Server.Implementations/Images/DynamicImageProvider.cs b/Emby.Server.Implementations/Images/DynamicImageProvider.cs
index 462eb03a8..50c531482 100644
--- a/Emby.Server.Implementations/Images/DynamicImageProvider.cs
+++ b/Emby.Server.Implementations/Images/DynamicImageProvider.cs
@@ -4,7 +4,6 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using Emby.Server.Implementations.Images;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto;
diff --git a/Emby.Server.Implementations/Images/PlaylistImageProvider.cs b/Emby.Server.Implementations/Images/PlaylistImageProvider.cs
index 0ce1b91e8..a4c106e87 100644
--- a/Emby.Server.Implementations/Images/PlaylistImageProvider.cs
+++ b/Emby.Server.Implementations/Images/PlaylistImageProvider.cs
@@ -29,9 +29,7 @@ namespace Emby.Server.Implementations.Images
{
var subItem = i.Item2;
- var episode = subItem as Episode;
-
- if (episode != null)
+ if (subItem is Episode episode)
{
var series = episode.Series;
if (series != null && series.HasImage(ImageType.Primary))
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index 6a9f4174d..4d207471a 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -48,6 +48,7 @@ using MediaBrowser.Providers.MediaInfo;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
using Episode = MediaBrowser.Controller.Entities.TV.Episode;
+using EpisodeInfo = Emby.Naming.TV.EpisodeInfo;
using Genre = MediaBrowser.Controller.Entities.Genre;
using Person = MediaBrowser.Controller.Entities.Person;
using VideoResolver = Emby.Naming.Video.VideoResolver;
@@ -175,10 +176,7 @@ namespace Emby.Server.Implementations.Library
{
lock (_rootFolderSyncLock)
{
- if (_rootFolder == null)
- {
- _rootFolder = CreateRootFolder();
- }
+ _rootFolder ??= CreateRootFolder();
}
}
@@ -558,7 +556,6 @@ namespace Emby.Server.Implementations.Library
var args = new ItemResolveArgs(_configurationManager.ApplicationPaths, directoryService)
{
Parent = parent,
- Path = fullPath,
FileInfo = fileInfo,
CollectionType = collectionType,
LibraryOptions = libraryOptions
@@ -684,7 +681,7 @@ namespace Emby.Server.Implementations.Library
foreach (var item in items)
{
- ResolverHelper.SetInitialItemValues(item, parent, _fileSystem, this, directoryService);
+ ResolverHelper.SetInitialItemValues(item, parent, this, directoryService);
}
items.AddRange(ResolveFileList(result.ExtraFiles, directoryService, parent, collectionType, resolvers, libraryOptions));
@@ -1163,7 +1160,7 @@ namespace Emby.Server.Implementations.Library
progress.Report(percent * 100);
}
- _itemRepository.UpdateInheritedValues(cancellationToken);
+ _itemRepository.UpdateInheritedValues();
progress.Report(100);
}
@@ -2517,7 +2514,7 @@ namespace Emby.Server.Implementations.Library
public bool FillMissingEpisodeNumbersFromPath(Episode episode, bool forceRefresh)
{
var series = episode.Series;
- bool? isAbsoluteNaming = series == null ? false : string.Equals(series.DisplayOrder, "absolute", StringComparison.OrdinalIgnoreCase);
+ bool? isAbsoluteNaming = series != null && string.Equals(series.DisplayOrder, "absolute", StringComparison.OrdinalIgnoreCase);
if (!isAbsoluteNaming.Value)
{
// In other words, no filter applied
@@ -2529,9 +2526,23 @@ namespace Emby.Server.Implementations.Library
var isFolder = episode.VideoType == VideoType.BluRay || episode.VideoType == VideoType.Dvd;
// TODO nullable - what are we trying to do there with empty episodeInfo?
- var episodeInfo = episode.IsFileProtocol
- ? resolver.Resolve(episode.Path, isFolder, null, null, isAbsoluteNaming) ?? new Naming.TV.EpisodeInfo(episode.Path)
- : new Naming.TV.EpisodeInfo(episode.Path);
+ EpisodeInfo episodeInfo = null;
+ if (episode.IsFileProtocol)
+ {
+ episodeInfo = resolver.Resolve(episode.Path, isFolder, null, null, isAbsoluteNaming);
+ // Resolve from parent folder if it's not the Season folder
+ if (episodeInfo == null && episode.Parent.GetType() == typeof(Folder))
+ {
+ episodeInfo = resolver.Resolve(episode.Parent.Path, true, null, null, isAbsoluteNaming);
+ if (episodeInfo != null)
+ {
+ // add the container
+ episodeInfo.Container = Path.GetExtension(episode.Path)?.TrimStart('.');
+ }
+ }
+ }
+
+ episodeInfo ??= new EpisodeInfo(episode.Path);
try
{
@@ -2881,12 +2892,20 @@ namespace Emby.Server.Implementations.Library
public void UpdatePeople(BaseItem item, List<PersonInfo> people)
{
+ UpdatePeopleAsync(item, people, CancellationToken.None).GetAwaiter().GetResult();
+ }
+
+ /// <inheritdoc />
+ public async Task UpdatePeopleAsync(BaseItem item, List<PersonInfo> people, CancellationToken cancellationToken)
+ {
if (!item.SupportsPeople)
{
return;
}
_itemRepository.UpdatePeople(item.Id, people);
+
+ await SavePeopleMetadataAsync(people, cancellationToken).ConfigureAwait(false);
}
public async Task<ItemImageInfo> ConvertImageToLocal(BaseItem item, ItemImageInfo image, int imageIndex)
@@ -2990,6 +3009,58 @@ namespace Emby.Server.Implementations.Library
}
}
+ private async Task SavePeopleMetadataAsync(IEnumerable<PersonInfo> people, CancellationToken cancellationToken)
+ {
+ var personsToSave = new List<BaseItem>();
+
+ foreach (var person in people)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ var itemUpdateType = ItemUpdateType.MetadataDownload;
+ var saveEntity = false;
+ var personEntity = GetPerson(person.Name);
+
+ // if PresentationUniqueKey is empty it's likely a new item.
+ if (string.IsNullOrEmpty(personEntity.PresentationUniqueKey))
+ {
+ personEntity.PresentationUniqueKey = personEntity.CreatePresentationUniqueKey();
+ saveEntity = true;
+ }
+
+ foreach (var id in person.ProviderIds)
+ {
+ if (!string.Equals(personEntity.GetProviderId(id.Key), id.Value, StringComparison.OrdinalIgnoreCase))
+ {
+ personEntity.SetProviderId(id.Key, id.Value);
+ saveEntity = true;
+ }
+ }
+
+ if (!string.IsNullOrWhiteSpace(person.ImageUrl) && !personEntity.HasImage(ImageType.Primary))
+ {
+ personEntity.SetImage(
+ new ItemImageInfo
+ {
+ Path = person.ImageUrl,
+ Type = ImageType.Primary
+ },
+ 0);
+
+ saveEntity = true;
+ itemUpdateType = ItemUpdateType.ImageUpdate;
+ }
+
+ if (saveEntity)
+ {
+ personsToSave.Add(personEntity);
+ await RunMetadataSavers(personEntity, itemUpdateType).ConfigureAwait(false);
+ }
+ }
+
+ CreateItems(personsToSave, null, CancellationToken.None);
+ }
+
private void StartScanInBackground()
{
Task.Run(() =>
diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs
index b2943020c..85d6d3043 100644
--- a/Emby.Server.Implementations/Library/MediaSourceManager.cs
+++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs
@@ -199,10 +199,15 @@ namespace Emby.Server.Implementations.Library
{
source.SupportsTranscoding = user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding);
}
+ else if (string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
+ {
+ source.SupportsTranscoding = user.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding);
+ source.SupportsDirectStream = user.HasPermission(PermissionKind.EnablePlaybackRemuxing);
+ }
}
}
- return SortMediaSources(list).Where(i => i.Type != MediaSourceType.Placeholder).ToList();
+ return SortMediaSources(list);
}
public MediaProtocol GetPathProtocol(string path)
@@ -436,7 +441,7 @@ namespace Emby.Server.Implementations.Library
}
}
- private static IEnumerable<MediaSourceInfo> SortMediaSources(IEnumerable<MediaSourceInfo> sources)
+ private static List<MediaSourceInfo> SortMediaSources(IEnumerable<MediaSourceInfo> sources)
{
return sources.OrderBy(i =>
{
@@ -451,8 +456,9 @@ namespace Emby.Server.Implementations.Library
{
var stream = i.VideoStream;
- return stream == null || stream.Width == null ? 0 : stream.Width.Value;
+ return stream?.Width ?? 0;
})
+ .Where(i => i.Type != MediaSourceType.Placeholder)
.ToList();
}
@@ -584,18 +590,9 @@ namespace Emby.Server.Implementations.Library
public Task<IDirectStreamProvider> GetDirectStreamProviderByUniqueId(string uniqueId, CancellationToken cancellationToken)
{
- var info = _openStreams.Values.FirstOrDefault(i =>
- {
- var liveStream = i as ILiveStream;
- if (liveStream != null)
- {
- return string.Equals(liveStream.UniqueId, uniqueId, StringComparison.OrdinalIgnoreCase);
- }
-
- return false;
- });
+ var info = _openStreams.FirstOrDefault(i => i.Value != null && string.Equals(i.Value.UniqueId, uniqueId, StringComparison.OrdinalIgnoreCase));
- return Task.FromResult(info as IDirectStreamProvider);
+ return Task.FromResult(info.Value as IDirectStreamProvider);
}
public async Task<LiveStreamResponse> OpenLiveStream(LiveStreamRequest request, CancellationToken cancellationToken)
diff --git a/Emby.Server.Implementations/Library/MusicManager.cs b/Emby.Server.Implementations/Library/MusicManager.cs
index 658c53f28..f8bae4fd1 100644
--- a/Emby.Server.Implementations/Library/MusicManager.cs
+++ b/Emby.Server.Implementations/Library/MusicManager.cs
@@ -100,8 +100,7 @@ namespace Emby.Server.Implementations.Library
public List<BaseItem> GetInstantMixFromItem(BaseItem item, User user, DtoOptions dtoOptions)
{
- var genre = item as MusicGenre;
- if (genre != null)
+ if (item is MusicGenre genre)
{
return GetInstantMixFromGenreIds(new[] { item.Id }, user, dtoOptions);
}
diff --git a/Emby.Server.Implementations/Library/PathExtensions.cs b/Emby.Server.Implementations/Library/PathExtensions.cs
index 6eaecff0f..0de4edb7e 100644
--- a/Emby.Server.Implementations/Library/PathExtensions.cs
+++ b/Emby.Server.Implementations/Library/PathExtensions.cs
@@ -2,8 +2,6 @@
using System;
using System.Diagnostics.CodeAnalysis;
-using System.IO;
-using System.Text.RegularExpressions;
using MediaBrowser.Common.Providers;
namespace Emby.Server.Implementations.Library
@@ -98,8 +96,14 @@ namespace Emby.Server.Implementations.Library
// We have to ensure that the sub path ends with a directory separator otherwise we'll get weird results
// when the sub path matches a similar but in-complete subpath
var oldSubPathEndsWithSeparator = subPath[^1] == newDirectorySeparatorChar;
- if (!path.StartsWith(subPath, StringComparison.OrdinalIgnoreCase)
- || (!oldSubPathEndsWithSeparator && path[subPath.Length] != newDirectorySeparatorChar))
+ if (!path.StartsWith(subPath, StringComparison.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+
+ if (path.Length > subPath.Length
+ && !oldSubPathEndsWithSeparator
+ && path[subPath.Length] != newDirectorySeparatorChar)
{
return false;
}
diff --git a/Emby.Server.Implementations/Library/ResolverHelper.cs b/Emby.Server.Implementations/Library/ResolverHelper.cs
index 4e4cac75b..b1a2e9284 100644
--- a/Emby.Server.Implementations/Library/ResolverHelper.cs
+++ b/Emby.Server.Implementations/Library/ResolverHelper.cs
@@ -1,3 +1,5 @@
+#nullable enable
+
using System;
using System.IO;
using System.Linq;
@@ -18,11 +20,10 @@ namespace Emby.Server.Implementations.Library
/// </summary>
/// <param name="item">The item.</param>
/// <param name="parent">The parent.</param>
- /// <param name="fileSystem">The file system.</param>
/// <param name="libraryManager">The library manager.</param>
/// <param name="directoryService">The directory service.</param>
- /// <exception cref="ArgumentException">Item must have a path</exception>
- public static void SetInitialItemValues(BaseItem item, Folder parent, IFileSystem fileSystem, ILibraryManager libraryManager, IDirectoryService directoryService)
+ /// <exception cref="ArgumentException">Item must have a path.</exception>
+ public static void SetInitialItemValues(BaseItem item, Folder? parent, ILibraryManager libraryManager, IDirectoryService directoryService)
{
// This version of the below method has no ItemResolveArgs, so we have to require the path already being set
if (string.IsNullOrEmpty(item.Path))
@@ -43,9 +44,9 @@ namespace Emby.Server.Implementations.Library
// Make sure DateCreated and DateModified have values
var fileInfo = directoryService.GetFile(item.Path);
- SetDateCreated(item, fileSystem, fileInfo);
+ SetDateCreated(item, fileInfo);
- EnsureName(item, item.Path, fileInfo);
+ EnsureName(item, fileInfo);
}
/// <summary>
@@ -72,9 +73,9 @@ namespace Emby.Server.Implementations.Library
item.Id = libraryManager.GetNewItemId(item.Path, item.GetType());
// Make sure the item has a name
- EnsureName(item, item.Path, args.FileInfo);
+ EnsureName(item, args.FileInfo);
- item.IsLocked = item.Path.IndexOf("[dontfetchmeta]", StringComparison.OrdinalIgnoreCase) != -1 ||
+ item.IsLocked = item.Path.Contains("[dontfetchmeta]", StringComparison.OrdinalIgnoreCase) ||
item.GetParents().Any(i => i.IsLocked);
// Make sure DateCreated and DateModified have values
@@ -84,29 +85,16 @@ namespace Emby.Server.Implementations.Library
/// <summary>
/// Ensures the name.
/// </summary>
- private static void EnsureName(BaseItem item, string fullPath, FileSystemMetadata fileInfo)
+ private static void EnsureName(BaseItem item, FileSystemMetadata fileInfo)
{
// If the subclass didn't supply a name, add it here
- if (string.IsNullOrEmpty(item.Name) && !string.IsNullOrEmpty(fullPath))
+ if (string.IsNullOrEmpty(item.Name) && !string.IsNullOrEmpty(item.Path))
{
- var fileName = fileInfo == null ? Path.GetFileName(fullPath) : fileInfo.Name;
-
- item.Name = GetDisplayName(fileName, fileInfo != null && fileInfo.IsDirectory);
+ item.Name = fileInfo.IsDirectory ? fileInfo.Name : Path.GetFileNameWithoutExtension(fileInfo.Name);
}
}
/// <summary>
- /// Gets the display name.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <param name="isDirectory">if set to <c>true</c> [is directory].</param>
- /// <returns>System.String.</returns>
- private static string GetDisplayName(string path, bool isDirectory)
- {
- return isDirectory ? Path.GetFileName(path) : Path.GetFileNameWithoutExtension(path);
- }
-
- /// <summary>
/// Ensures DateCreated and DateModified have values.
/// </summary>
/// <param name="fileSystem">The file system.</param>
@@ -114,21 +102,6 @@ namespace Emby.Server.Implementations.Library
/// <param name="args">The args.</param>
private static void EnsureDates(IFileSystem fileSystem, BaseItem item, ItemResolveArgs args)
{
- if (fileSystem == null)
- {
- throw new ArgumentNullException(nameof(fileSystem));
- }
-
- if (item == null)
- {
- throw new ArgumentNullException(nameof(item));
- }
-
- if (args == null)
- {
- throw new ArgumentNullException(nameof(args));
- }
-
// See if a different path came out of the resolver than what went in
if (!fileSystem.AreEqual(args.Path, item.Path))
{
@@ -136,7 +109,7 @@ namespace Emby.Server.Implementations.Library
if (childData != null)
{
- SetDateCreated(item, fileSystem, childData);
+ SetDateCreated(item, childData);
}
else
{
@@ -144,17 +117,17 @@ namespace Emby.Server.Implementations.Library
if (fileData.Exists)
{
- SetDateCreated(item, fileSystem, fileData);
+ SetDateCreated(item, fileData);
}
}
}
else
{
- SetDateCreated(item, fileSystem, args.FileInfo);
+ SetDateCreated(item, args.FileInfo);
}
}
- private static void SetDateCreated(BaseItem item, IFileSystem fileSystem, FileSystemMetadata info)
+ private static void SetDateCreated(BaseItem item, FileSystemMetadata? info)
{
var config = BaseItem.ConfigurationManager.GetMetadataConfiguration();
@@ -163,7 +136,7 @@ namespace Emby.Server.Implementations.Library
// directoryService.getFile may return null
if (info != null)
{
- var dateCreated = fileSystem.GetCreationTimeUtc(info);
+ var dateCreated = info.CreationTimeUtc;
if (dateCreated.Equals(DateTime.MinValue))
{
diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
index 90b6a8a7d..4ad84579d 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
@@ -201,6 +201,11 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
continue;
}
+ if (resolvedItem.Files.Count == 0)
+ {
+ continue;
+ }
+
var firstMedia = resolvedItem.Files[0];
var libraryItem = new T
diff --git a/Emby.Server.Implementations/Library/Resolvers/ItemResolver.cs b/Emby.Server.Implementations/Library/Resolvers/ItemResolver.cs
index 9ca76095b..92fb2a753 100644
--- a/Emby.Server.Implementations/Library/Resolvers/ItemResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/ItemResolver.cs
@@ -12,6 +12,12 @@ namespace Emby.Server.Implementations.Library.Resolvers
where T : BaseItem, new()
{
/// <summary>
+ /// Gets the priority.
+ /// </summary>
+ /// <value>The priority.</value>
+ public virtual ResolverPriority Priority => ResolverPriority.First;
+
+ /// <summary>
/// Resolves the specified args.
/// </summary>
/// <param name="args">The args.</param>
@@ -22,12 +28,6 @@ namespace Emby.Server.Implementations.Library.Resolvers
}
/// <summary>
- /// Gets the priority.
- /// </summary>
- /// <value>The priority.</value>
- public virtual ResolverPriority Priority => ResolverPriority.First;
-
- /// <summary>
/// Sets initial values on the newly resolved item.
/// </summary>
/// <param name="item">The item.</param>
diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
index 714bc3a84..16bf4dc4a 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
@@ -376,7 +376,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
{
var multiDiscFolders = new List<FileSystemMetadata>();
- var libraryOptions = args.GetLibraryOptions();
+ var libraryOptions = args.LibraryOptions;
var supportPhotos = string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase) && libraryOptions.EnablePhotos;
var photos = new List<FileSystemMetadata>();
diff --git a/Emby.Server.Implementations/Library/Resolvers/PhotoAlbumResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PhotoAlbumResolver.cs
index 3ac837057..204c8a62e 100644
--- a/Emby.Server.Implementations/Library/Resolvers/PhotoAlbumResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/PhotoAlbumResolver.cs
@@ -13,7 +13,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
public class PhotoAlbumResolver : FolderResolver<PhotoAlbum>
{
private readonly IImageProcessor _imageProcessor;
- private ILibraryManager _libraryManager;
+ private readonly ILibraryManager _libraryManager;
/// <summary>
/// Initializes a new instance of the <see cref="PhotoAlbumResolver"/> class.
@@ -26,6 +26,9 @@ namespace Emby.Server.Implementations.Library.Resolvers
_libraryManager = libraryManager;
}
+ /// <inheritdoc />
+ public override ResolverPriority Priority => ResolverPriority.Second;
+
/// <summary>
/// Resolves the specified args.
/// </summary>
@@ -39,8 +42,8 @@ namespace Emby.Server.Implementations.Library.Resolvers
// Must be an image file within a photo collection
var collectionType = args.GetCollectionType();
- if (string.Equals(collectionType, CollectionType.Photos, StringComparison.OrdinalIgnoreCase) ||
- (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase) && args.GetLibraryOptions().EnablePhotos))
+ if (string.Equals(collectionType, CollectionType.Photos, StringComparison.OrdinalIgnoreCase)
+ || (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase) && args.LibraryOptions.EnablePhotos))
{
if (HasPhotos(args))
{
@@ -84,8 +87,5 @@ namespace Emby.Server.Implementations.Library.Resolvers
return false;
}
-
- /// <inheritdoc />
- public override ResolverPriority Priority => ResolverPriority.Second;
}
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs
index bcfcee9c6..3cb6542cf 100644
--- a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs
@@ -47,7 +47,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
var collectionType = args.CollectionType;
if (string.Equals(collectionType, CollectionType.Photos, StringComparison.OrdinalIgnoreCase)
- || (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase) && args.GetLibraryOptions().EnablePhotos))
+ || (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase) && args.LibraryOptions.EnablePhotos))
{
if (IsImageFile(args.Path, _imageProcessor))
{
diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
index 9b4cd7a3d..6f29bc649 100644
--- a/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
@@ -35,14 +35,10 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
return null;
}
- var season = parent as Season;
-
// Just in case the user decided to nest episodes.
// Not officially supported but in some cases we can handle it.
- if (season == null)
- {
- season = parent.GetParents().OfType<Season>().FirstOrDefault();
- }
+
+ var season = parent as Season ?? parent.GetParents().OfType<Season>().FirstOrDefault();
// If the parent is a Season or Series and the parent is not an extras folder, then this is an Episode if the VideoResolver returns something
// Also handle flat tv folders
@@ -55,11 +51,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
if (episode != null)
{
- var series = parent as Series;
- if (series == null)
- {
- series = parent.GetParents().OfType<Series>().FirstOrDefault();
- }
+ var series = parent as Series ?? parent.GetParents().OfType<Series>().FirstOrDefault();
if (series != null)
{
diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
index 3332e1806..768e2e4f5 100644
--- a/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
@@ -88,7 +88,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("NameSeasonNumber"),
seasonNumber,
- args.GetLibraryOptions().PreferredMetadataLanguage);
+ args.LibraryOptions.PreferredMetadataLanguage);
}
return season;
diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
index 4a9d2cf8c..8fc3e3e75 100644
--- a/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
@@ -19,19 +19,16 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
/// </summary>
public class SeriesResolver : FolderResolver<Series>
{
- private readonly IFileSystem _fileSystem;
private readonly ILogger<SeriesResolver> _logger;
private readonly ILibraryManager _libraryManager;
/// <summary>
/// Initializes a new instance of the <see cref="SeriesResolver"/> class.
/// </summary>
- /// <param name="fileSystem">The file system.</param>
/// <param name="logger">The logger.</param>
/// <param name="libraryManager">The library manager.</param>
- public SeriesResolver(IFileSystem fileSystem, ILogger<SeriesResolver> logger, ILibraryManager libraryManager)
+ public SeriesResolver(ILogger<SeriesResolver> logger, ILibraryManager libraryManager)
{
- _fileSystem = fileSystem;
_logger = logger;
_libraryManager = libraryManager;
}
@@ -59,15 +56,6 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
var collectionType = args.GetCollectionType();
if (string.Equals(collectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase))
{
- // if (args.ContainsFileSystemEntryByName("tvshow.nfo"))
- //{
- // return new Series
- // {
- // Path = args.Path,
- // Name = Path.GetFileName(args.Path)
- // };
- //}
-
var configuredContentType = _libraryManager.GetConfiguredContentType(args.Path);
if (!string.Equals(configuredContentType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase))
{
@@ -100,7 +88,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
return null;
}
- if (IsSeriesFolder(args.Path, args.FileSystemChildren, args.DirectoryService, _fileSystem, _logger, _libraryManager, false))
+ if (IsSeriesFolder(args.Path, args.FileSystemChildren, _logger, _libraryManager, false))
{
return new Series
{
@@ -117,8 +105,6 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
public static bool IsSeriesFolder(
string path,
IEnumerable<FileSystemMetadata> fileSystemChildren,
- IDirectoryService directoryService,
- IFileSystem fileSystem,
ILogger<SeriesResolver> logger,
ILibraryManager libraryManager,
bool isTvContentType)
diff --git a/Emby.Server.Implementations/Library/SearchEngine.cs b/Emby.Server.Implementations/Library/SearchEngine.cs
index 94602582b..bcdf854ca 100644
--- a/Emby.Server.Implementations/Library/SearchEngine.cs
+++ b/Emby.Server.Implementations/Library/SearchEngine.cs
@@ -12,7 +12,6 @@ using MediaBrowser.Controller.Extensions;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Search;
-using Microsoft.Extensions.Logging;
using Genre = MediaBrowser.Controller.Entities.Genre;
using Person = MediaBrowser.Controller.Entities.Person;
diff --git a/Emby.Server.Implementations/Library/UserDataManager.cs b/Emby.Server.Implementations/Library/UserDataManager.cs
index d16275b19..827e3c64b 100644
--- a/Emby.Server.Implementations/Library/UserDataManager.cs
+++ b/Emby.Server.Implementations/Library/UserDataManager.cs
@@ -13,8 +13,8 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
-using Book = MediaBrowser.Controller.Entities.Book;
using AudioBook = MediaBrowser.Controller.Entities.AudioBook;
+using Book = MediaBrowser.Controller.Entities.Book;
namespace Emby.Server.Implementations.Library
{
@@ -248,15 +248,15 @@ namespace Emby.Server.Implementations.Library
}
else if (positionTicks > 0 && hasRuntime && item is AudioBook)
{
- var minIn = TimeSpan.FromTicks(positionTicks).TotalMinutes;
- var minOut = TimeSpan.FromTicks(runtimeTicks - positionTicks).TotalMinutes;
+ var playbackPositionInMinutes = TimeSpan.FromTicks(positionTicks).TotalMinutes;
+ var remainingTimeInMinutes = TimeSpan.FromTicks(runtimeTicks - positionTicks).TotalMinutes;
- if (minIn > _config.Configuration.MinAudiobookResume)
+ if (playbackPositionInMinutes < _config.Configuration.MinAudiobookResume)
{
// ignore progress during the beginning
positionTicks = 0;
}
- else if (minOut < _config.Configuration.MaxAudiobookResume || positionTicks >= runtimeTicks)
+ else if (remainingTimeInMinutes < _config.Configuration.MaxAudiobookResume || positionTicks >= runtimeTicks)
{
// mark as completed close to the end
positionTicks = 0;
diff --git a/Emby.Server.Implementations/Library/UserViewManager.cs b/Emby.Server.Implementations/Library/UserViewManager.cs
index b6b7ea949..ac041bcf6 100644
--- a/Emby.Server.Implementations/Library/UserViewManager.cs
+++ b/Emby.Server.Implementations/Library/UserViewManager.cs
@@ -2,7 +2,6 @@
using System;
using System.Collections.Generic;
-using System.Globalization;
using System.Linq;
using System.Threading;
using Jellyfin.Data.Entities;
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
index 91a21db60..28a2095e1 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
@@ -17,7 +17,6 @@ using Jellyfin.Data.Enums;
using Jellyfin.Data.Events;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.Net;
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
@@ -802,22 +801,22 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
public ActiveRecordingInfo GetActiveRecordingInfo(string path)
{
- if (string.IsNullOrWhiteSpace(path))
+ if (string.IsNullOrWhiteSpace(path) || _activeRecordings.IsEmpty)
{
return null;
}
- foreach (var recording in _activeRecordings.Values)
+ foreach (var (_, recordingInfo) in _activeRecordings)
{
- if (string.Equals(recording.Path, path, StringComparison.Ordinal) && !recording.CancellationTokenSource.IsCancellationRequested)
+ if (string.Equals(recordingInfo.Path, path, StringComparison.Ordinal) && !recordingInfo.CancellationTokenSource.IsCancellationRequested)
{
- var timer = recording.Timer;
+ var timer = recordingInfo.Timer;
if (timer.Status != RecordingStatus.InProgress)
{
return null;
}
- return recording;
+ return recordingInfo;
}
}
@@ -1622,9 +1621,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
return _activeRecordings
- .Values
- .ToList()
- .Any(i => string.Equals(i.Path, path, StringComparison.OrdinalIgnoreCase) && !string.Equals(i.Timer.Id, timerId, StringComparison.OrdinalIgnoreCase));
+ .Any(i => string.Equals(i.Value.Path, path, StringComparison.OrdinalIgnoreCase) && !string.Equals(i.Value.Timer.Id, timerId, StringComparison.OrdinalIgnoreCase));
}
private IRecorder GetRecorder(MediaSourceInfo mediaSource)
@@ -2240,14 +2237,10 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
var enabledTimersForSeries = new List<TimerInfo>();
foreach (var timer in allTimers)
{
- var existingTimer = _timerProvider.GetTimer(timer.Id);
-
- if (existingTimer == null)
- {
- existingTimer = string.IsNullOrWhiteSpace(timer.ProgramId)
+ var existingTimer = _timerProvider.GetTimer(timer.Id)
+ ?? (string.IsNullOrWhiteSpace(timer.ProgramId)
? null
- : _timerProvider.GetTimerByProgramId(timer.ProgramId);
- }
+ : _timerProvider.GetTimerByProgramId(timer.ProgramId));
if (existingTimer == null)
{
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs
index c20b08088..1cac9cb96 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs
@@ -4,9 +4,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using System.Text;
using System.Text.Json;
-using System.Threading.Tasks;
using MediaBrowser.Common.Json;
using Microsoft.Extensions.Logging;
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs
index da707fec6..b1259de23 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs
@@ -2,7 +2,6 @@
using System;
using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Model.Serialization;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.LiveTv.EmbyTV
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
index 1926e738f..9af65cabb 100644
--- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
@@ -787,14 +787,11 @@ namespace Emby.Server.Implementations.LiveTv.Listings
{
var channelNumber = GetChannelNumber(channel);
- var station = allStations.Find(item => string.Equals(item.stationID, channel.stationID, StringComparison.OrdinalIgnoreCase));
- if (station == null)
- {
- station = new ScheduleDirect.Station
+ var station = allStations.Find(item => string.Equals(item.stationID, channel.stationID, StringComparison.OrdinalIgnoreCase))
+ ?? new ScheduleDirect.Station
{
stationID = channel.stationID
};
- }
var channelInfo = new ChannelInfo
{
diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
index 76c875737..6824aa442 100644
--- a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
+++ b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
@@ -4,7 +4,6 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
-using System.IO.Compression;
using System.Linq;
using System.Net.Http;
using System.Threading;
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
index 63a3146aa..1145d8aa1 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
@@ -987,10 +987,7 @@ namespace Emby.Server.Implementations.LiveTv
var externalProgramId = programTuple.Item2;
string externalSeriesId = programTuple.Item3;
- if (timerList == null)
- {
- timerList = (await GetTimersInternal(new TimerQuery(), cancellationToken).ConfigureAwait(false)).Items;
- }
+ timerList ??= (await GetTimersInternal(new TimerQuery(), cancellationToken).ConfigureAwait(false)).Items;
var timer = timerList.FirstOrDefault(i => string.Equals(i.ProgramId, externalProgramId, StringComparison.OrdinalIgnoreCase));
var foundSeriesTimer = false;
@@ -1018,10 +1015,7 @@ namespace Emby.Server.Implementations.LiveTv
continue;
}
- if (seriesTimerList == null)
- {
- seriesTimerList = (await GetSeriesTimersInternal(new SeriesTimerQuery(), cancellationToken).ConfigureAwait(false)).Items;
- }
+ seriesTimerList ??= (await GetSeriesTimersInternal(new SeriesTimerQuery(), cancellationToken).ConfigureAwait(false)).Items;
var seriesTimer = seriesTimerList.FirstOrDefault(i => string.Equals(i.SeriesId, externalSeriesId, StringComparison.OrdinalIgnoreCase));
@@ -1974,10 +1968,7 @@ namespace Emby.Server.Implementations.LiveTv
};
}
- if (service == null)
- {
- service = _services[0];
- }
+ service ??= _services[0];
var info = await service.GetNewTimerDefaultsAsync(cancellationToken, programInfo).ConfigureAwait(false);
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
index 68173a0ef..4aa5832b1 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
@@ -8,10 +8,8 @@ using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text.Json;
-using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
-using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Json;
using MediaBrowser.Common.Net;
@@ -19,7 +17,6 @@ using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
@@ -424,10 +421,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
string audioCodec = channelInfo.AudioCodec;
- if (!videoBitrate.HasValue)
- {
- videoBitrate = isHd ? 15000000 : 2000000;
- }
+ videoBitrate ??= isHd ? 15000000 : 2000000;
int? audioBitrate = isHd ? 448000 : 192000;
@@ -664,7 +658,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
_modelCache.Clear();
}
- cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(new CancellationTokenSource(discoveryDurationMs).Token, cancellationToken).Token;
+ using var timedCancellationToken = new CancellationTokenSource(discoveryDurationMs);
+ using var linkedCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(timedCancellationToken.Token, cancellationToken);
+ cancellationToken = linkedCancellationTokenSource.Token;
var list = new List<TunerHostInfo>();
// Create udp broadcast discovery message
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs
index 78e62ff0a..f8baf55da 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs
@@ -150,7 +150,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
public async Task CopyToAsync(Stream stream, CancellationToken cancellationToken)
{
- cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, LiveStreamCancellationTokenSource.Token).Token;
+ using var linkedCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, LiveStreamCancellationTokenSource.Token);
+ cancellationToken = linkedCancellationTokenSource.Token;
// use non-async filestream on windows along with read due to https://github.com/dotnet/corefx/issues/6039
var allowAsync = Environment.OSVersion.Platform != PlatformID.Win32NT;
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
index 2af635492..cc30a516d 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
@@ -4,7 +4,6 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
-using System.Linq;
using System.Net.Http;
using System.Text.RegularExpressions;
using System.Threading;
diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs
index 98de848bc..46858b4fb 100644
--- a/Emby.Server.Implementations/Localization/LocalizationManager.cs
+++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs
@@ -11,7 +11,6 @@ using MediaBrowser.Common.Json;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Globalization;
-using MediaBrowser.Model.Serialization;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Localization
@@ -316,10 +315,9 @@ namespace Emby.Server.Implementations.Localization
}
const string Prefix = "Core";
- var key = Prefix + culture;
return _dictionaries.GetOrAdd(
- key,
+ culture,
f => GetDictionary(Prefix, culture, DefaultCulture + ".json").GetAwaiter().GetResult());
}
diff --git a/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs b/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs
index a9dab9138..031b5d2e7 100644
--- a/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs
+++ b/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs
@@ -15,7 +15,6 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
-using MediaBrowser.Model.MediaInfo;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.MediaEncoder
diff --git a/Emby.Server.Implementations/Playlists/PlaylistManager.cs b/Emby.Server.Implementations/Playlists/PlaylistManager.cs
index 932f721ab..2d1a559f1 100644
--- a/Emby.Server.Implementations/Playlists/PlaylistManager.cs
+++ b/Emby.Server.Implementations/Playlists/PlaylistManager.cs
@@ -215,7 +215,7 @@ namespace Emby.Server.Implementations.Playlists
// Create a list of the new linked children to add to the playlist
var childrenToAdd = newItems
- .Select(i => LinkedChild.Create(i))
+ .Select(LinkedChild.Create)
.ToList();
// Log duplicates that have been ignored, if any
diff --git a/Emby.Server.Implementations/Plugins/PluginManager.cs b/Emby.Server.Implementations/Plugins/PluginManager.cs
index 3a8296455..14df20936 100644
--- a/Emby.Server.Implementations/Plugins/PluginManager.cs
+++ b/Emby.Server.Implementations/Plugins/PluginManager.cs
@@ -44,12 +44,7 @@ namespace Emby.Server.Implementations.Plugins
{
get
{
- if (_httpClientFactory == null)
- {
- _httpClientFactory = _appHost.Resolve<IHttpClientFactory>();
- }
-
- return _httpClientFactory;
+ return _httpClientFactory ?? (_httpClientFactory = _appHost.Resolve<IHttpClientFactory>());
}
}
@@ -166,9 +161,7 @@ namespace Emby.Server.Implementations.Plugins
/// </summary>
public void CreatePlugins()
{
- _ = _appHost.GetExports<IPlugin>(CreatePluginInstance)
- .Where(i => i != null)
- .ToArray();
+ _ = _appHost.GetExports<IPlugin>(CreatePluginInstance);
}
/// <summary>
@@ -278,11 +271,7 @@ namespace Emby.Server.Implementations.Plugins
// If no version is given, return the current instance.
var plugins = _plugins.Where(p => p.Id.Equals(id)).ToList();
- plugin = plugins.FirstOrDefault(p => p.Instance != null);
- if (plugin == null)
- {
- plugin = plugins.OrderByDescending(p => p.Version).FirstOrDefault();
- }
+ plugin = plugins.FirstOrDefault(p => p.Instance != null) ?? plugins.OrderByDescending(p => p.Version).FirstOrDefault();
}
else
{
diff --git a/Emby.Server.Implementations/QuickConnect/QuickConnectManager.cs b/Emby.Server.Implementations/QuickConnect/QuickConnectManager.cs
index 0e5f65c72..0259dc436 100644
--- a/Emby.Server.Implementations/QuickConnect/QuickConnectManager.cs
+++ b/Emby.Server.Implementations/QuickConnect/QuickConnectManager.cs
@@ -3,7 +3,6 @@ using System.Collections.Concurrent;
using System.Globalization;
using System.Linq;
using System.Security.Cryptography;
-using MediaBrowser.Common;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Authentication;
@@ -258,19 +257,17 @@ namespace Emby.Server.Implementations.QuickConnect
}
// Expire stale connection requests
- var values = _currentRequests.Values.ToList();
-
- for (int i = 0; i < values.Count; i++)
+ foreach (var (_, currentRequest) in _currentRequests)
{
- var added = values[i].DateAdded ?? DateTime.UnixEpoch;
- if (DateTime.UtcNow > added.AddMinutes(Timeout) || expireAll)
+ var added = currentRequest.DateAdded ?? DateTime.UnixEpoch;
+ if (expireAll || DateTime.UtcNow > added.AddMinutes(Timeout))
{
- var code = values[i].Code;
- _logger.LogDebug("Removing expired request {code}", code);
+ var code = currentRequest.Code;
+ _logger.LogDebug("Removing expired request {Code}", code);
if (!_currentRequests.TryRemove(code, out _))
{
- _logger.LogWarning("Request {code} already expired", code);
+ _logger.LogWarning("Request {Code} already expired", code);
}
}
}
diff --git a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
index 3cc2cefb9..101d9b537 100644
--- a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
@@ -4,7 +4,6 @@ using System;
using System.Globalization;
using System.IO;
using System.Linq;
-using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
@@ -302,12 +301,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
{
get
{
- if (_id == null)
- {
- _id = ScheduledTask.GetType().FullName.GetMD5().ToString("N", CultureInfo.InvariantCulture);
- }
-
- return _id;
+ return _id ??= ScheduledTask.GetType().FullName.GetMD5().ToString("N", CultureInfo.InvariantCulture);
}
}
@@ -349,9 +343,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
{
var trigger = (ITaskTrigger)sender;
- var configurableTask = ScheduledTask as IConfigurableScheduledTask;
-
- if (configurableTask != null && !configurableTask.IsEnabled)
+ if (ScheduledTask is IConfigurableScheduledTask configurableTask && !configurableTask.IsEnabled)
{
return;
}
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs
index 649305fd5..2312c85d9 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs
@@ -12,9 +12,9 @@ using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Tasks;
-using MediaBrowser.Model.Globalization;
namespace Emby.Server.Implementations.ScheduledTasks
{
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs
index c384cf4bb..57d294a40 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs
@@ -5,8 +5,8 @@ using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Tasks;
using MediaBrowser.Model.Globalization;
+using MediaBrowser.Model.Tasks;
namespace Emby.Server.Implementations.ScheduledTasks
{
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs
index a69380cbb..11a5fb79f 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs
@@ -9,7 +9,6 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Updates;
using MediaBrowser.Model.Globalization;
-using MediaBrowser.Model.Net;
using MediaBrowser.Model.Tasks;
using Microsoft.Extensions.Logging;
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs
index e470adcf4..51b620404 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs
@@ -6,8 +6,8 @@ using System.Threading;
using System.Threading.Tasks;
using Emby.Server.Implementations.Library;
using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Tasks;
using MediaBrowser.Model.Globalization;
+using MediaBrowser.Model.Tasks;
namespace Emby.Server.Implementations.ScheduledTasks
{
diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs
index 10e28c33a..6844152ea 100644
--- a/Emby.Server.Implementations/Session/SessionManager.cs
+++ b/Emby.Server.Implementations/Session/SessionManager.cs
@@ -1475,17 +1475,14 @@ namespace Emby.Server.Implementations.Session
user = _userManager.GetUserById(request.UserId);
}
- if (user == null)
- {
- user = _userManager.GetUserByName(request.Username);
- }
+ user ??= _userManager.GetUserByName(request.Username);
if (enforcePassword)
{
user = await _userManager.AuthenticateUser(
request.Username,
request.Password,
- request.PasswordSha1,
+ null,
request.RemoteEndPoint,
true).ConfigureAwait(false);
}
diff --git a/Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs b/Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs
index aee959c53..72c0a838e 100644
--- a/Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs
+++ b/Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs
@@ -87,7 +87,7 @@ namespace Emby.Server.Implementations.SyncPlay
_sessionManager = sessionManager;
_libraryManager = libraryManager;
_logger = loggerFactory.CreateLogger<SyncPlayManager>();
- _sessionManager.SessionControllerConnected += OnSessionControllerConnected;
+ _sessionManager.SessionEnded += OnSessionEnded;
}
/// <inheritdoc />
@@ -269,14 +269,17 @@ namespace Emby.Server.Implementations.SyncPlay
var user = _userManager.GetUserById(session.UserId);
List<GroupInfoDto> list = new List<GroupInfoDto>();
- foreach (var group in _groups.Values)
+ lock (_groupsLock)
{
- // Locking required as group is not thread-safe.
- lock (group)
+ foreach (var (_, group) in _groups)
{
- if (group.HasAccessToPlayQueue(user))
+ // Locking required as group is not thread-safe.
+ lock (group)
{
- list.Add(group.GetInfo());
+ if (group.HasAccessToPlayQueue(user))
+ {
+ list.Add(group.GetInfo());
+ }
}
}
}
@@ -352,18 +355,18 @@ namespace Emby.Server.Implementations.SyncPlay
return;
}
- _sessionManager.SessionControllerConnected -= OnSessionControllerConnected;
+ _sessionManager.SessionEnded -= OnSessionEnded;
_disposed = true;
}
- private void OnSessionControllerConnected(object sender, SessionEventArgs e)
+ private void OnSessionEnded(object sender, SessionEventArgs e)
{
var session = e.SessionInfo;
if (_sessionToGroupMap.TryGetValue(session.Id, out var group))
{
- var request = new JoinGroupRequest(group.GroupId);
- JoinGroup(session, request, CancellationToken.None);
+ var leaveGroupRequest = new LeaveGroupRequest();
+ LeaveGroup(session, leaveGroupRequest, CancellationToken.None);
}
}
diff --git a/Emby.Server.Implementations/TV/TVSeriesManager.cs b/Emby.Server.Implementations/TV/TVSeriesManager.cs
index d3f6fa34d..829df64bf 100644
--- a/Emby.Server.Implementations/TV/TVSeriesManager.cs
+++ b/Emby.Server.Implementations/TV/TVSeriesManager.cs
@@ -43,9 +43,7 @@ namespace Emby.Server.Implementations.TV
string presentationUniqueKey = null;
if (!string.IsNullOrEmpty(request.SeriesId))
{
- var series = _libraryManager.GetItemById(request.SeriesId) as Series;
-
- if (series != null)
+ if (_libraryManager.GetItemById(request.SeriesId) is Series series)
{
presentationUniqueKey = GetUniqueSeriesKey(series);
}
@@ -95,9 +93,7 @@ namespace Emby.Server.Implementations.TV
int? limit = null;
if (!string.IsNullOrEmpty(request.SeriesId))
{
- var series = _libraryManager.GetItemById(request.SeriesId) as Series;
-
- if (series != null)
+ if (_libraryManager.GetItemById(request.SeriesId) is Series series)
{
presentationUniqueKey = GetUniqueSeriesKey(series);
limit = 1;
diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs
index c4e75fe85..b4154b361 100644
--- a/Jellyfin.Api/Controllers/DynamicHlsController.cs
+++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs
@@ -52,8 +52,6 @@ namespace Jellyfin.Api.Controllers
private readonly IServerConfigurationManager _serverConfigurationManager;
private readonly IMediaEncoder _mediaEncoder;
private readonly IFileSystem _fileSystem;
- private readonly ISubtitleEncoder _subtitleEncoder;
- private readonly IConfiguration _configuration;
private readonly IDeviceManager _deviceManager;
private readonly TranscodingJobHelper _transcodingJobHelper;
private readonly ILogger<DynamicHlsController> _logger;
@@ -72,12 +70,11 @@ namespace Jellyfin.Api.Controllers
/// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
/// <param name="mediaEncoder">Instance of the <see cref="IMediaEncoder"/> interface.</param>
/// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
- /// <param name="subtitleEncoder">Instance of the <see cref="ISubtitleEncoder"/> interface.</param>
- /// <param name="configuration">Instance of the <see cref="IConfiguration"/> interface.</param>
/// <param name="deviceManager">Instance of the <see cref="IDeviceManager"/> interface.</param>
/// <param name="transcodingJobHelper">Instance of the <see cref="TranscodingJobHelper"/> class.</param>
/// <param name="logger">Instance of the <see cref="ILogger{DynamicHlsController}"/> interface.</param>
/// <param name="dynamicHlsHelper">Instance of <see cref="DynamicHlsHelper"/>.</param>
+ /// <param name="encodingHelper">Instance of <see cref="EncodingHelper"/>.</param>
public DynamicHlsController(
ILibraryManager libraryManager,
IUserManager userManager,
@@ -87,15 +84,12 @@ namespace Jellyfin.Api.Controllers
IServerConfigurationManager serverConfigurationManager,
IMediaEncoder mediaEncoder,
IFileSystem fileSystem,
- ISubtitleEncoder subtitleEncoder,
- IConfiguration configuration,
IDeviceManager deviceManager,
TranscodingJobHelper transcodingJobHelper,
ILogger<DynamicHlsController> logger,
- DynamicHlsHelper dynamicHlsHelper)
+ DynamicHlsHelper dynamicHlsHelper,
+ EncodingHelper encodingHelper)
{
- _encodingHelper = new EncodingHelper(mediaEncoder, fileSystem, subtitleEncoder, configuration);
-
_libraryManager = libraryManager;
_userManager = userManager;
_dlnaManager = dlnaManager;
@@ -104,12 +98,12 @@ namespace Jellyfin.Api.Controllers
_serverConfigurationManager = serverConfigurationManager;
_mediaEncoder = mediaEncoder;
_fileSystem = fileSystem;
- _subtitleEncoder = subtitleEncoder;
- _configuration = configuration;
_deviceManager = deviceManager;
_transcodingJobHelper = transcodingJobHelper;
_logger = logger;
_dynamicHlsHelper = dynamicHlsHelper;
+ _encodingHelper = encodingHelper;
+
_encodingOptions = serverConfigurationManager.GetEncodingOptions();
}
@@ -1126,9 +1120,7 @@ namespace Jellyfin.Api.Controllers
_libraryManager,
_serverConfigurationManager,
_mediaEncoder,
- _fileSystem,
- _subtitleEncoder,
- _configuration,
+ _encodingHelper,
_dlnaManager,
_deviceManager,
_transcodingJobHelper,
@@ -1211,9 +1203,7 @@ namespace Jellyfin.Api.Controllers
_libraryManager,
_serverConfigurationManager,
_mediaEncoder,
- _fileSystem,
- _subtitleEncoder,
- _configuration,
+ _encodingHelper,
_dlnaManager,
_deviceManager,
_transcodingJobHelper,
diff --git a/Jellyfin.Api/Controllers/ItemLookupController.cs b/Jellyfin.Api/Controllers/ItemLookupController.cs
index dabd4deb7..9fa307858 100644
--- a/Jellyfin.Api/Controllers/ItemLookupController.cs
+++ b/Jellyfin.Api/Controllers/ItemLookupController.cs
@@ -238,48 +238,6 @@ namespace Jellyfin.Api.Controllers
}
/// <summary>
- /// Gets a remote image.
- /// </summary>
- /// <param name="imageUrl">The image url.</param>
- /// <param name="providerName">The provider name.</param>
- /// <response code="200">Remote image retrieved.</response>
- /// <returns>
- /// A <see cref="Task" /> that represents the asynchronous operation to get the remote search results.
- /// The task result contains an <see cref="FileStreamResult"/> containing the images file stream.
- /// </returns>
- [HttpGet("Items/RemoteSearch/Image")]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesImageFile]
- public async Task<ActionResult> GetRemoteSearchImage(
- [FromQuery, Required] string imageUrl,
- [FromQuery, Required] string providerName)
- {
- var urlHash = imageUrl.GetMD5();
- var pointerCachePath = GetFullCachePath(urlHash.ToString());
-
- try
- {
- var contentPath = await System.IO.File.ReadAllTextAsync(pointerCachePath).ConfigureAwait(false);
- if (System.IO.File.Exists(contentPath))
- {
- return PhysicalFile(contentPath, MimeTypes.GetMimeType(contentPath));
- }
- }
- catch (FileNotFoundException)
- {
- // Means the file isn't cached yet
- }
- catch (IOException)
- {
- // Means the file isn't cached yet
- }
-
- await DownloadImage(providerName, imageUrl, urlHash, pointerCachePath).ConfigureAwait(false);
- var updatedContentPath = await System.IO.File.ReadAllTextAsync(pointerCachePath).ConfigureAwait(false);
- return PhysicalFile(updatedContentPath, MimeTypes.GetMimeType(updatedContentPath));
- }
-
- /// <summary>
/// Applies search criteria to an item and refreshes metadata.
/// </summary>
/// <param name="itemId">Item id.</param>
@@ -320,54 +278,5 @@ namespace Jellyfin.Api.Controllers
return NoContent();
}
-
- /// <summary>
- /// Downloads the image.
- /// </summary>
- /// <param name="providerName">Name of the provider.</param>
- /// <param name="url">The URL.</param>
- /// <param name="urlHash">The URL hash.</param>
- /// <param name="pointerCachePath">The pointer cache path.</param>
- /// <returns>Task.</returns>
- private async Task DownloadImage(string providerName, string url, Guid urlHash, string pointerCachePath)
- {
- using var result = await _providerManager.GetSearchImage(providerName, url, CancellationToken.None).ConfigureAwait(false);
- if (result.Content.Headers.ContentType?.MediaType == null)
- {
- throw new ResourceNotFoundException(nameof(result.Content.Headers.ContentType));
- }
-
- var ext = result.Content.Headers.ContentType.MediaType.Split('/')[^1];
- var fullCachePath = GetFullCachePath(urlHash + "." + ext);
-
- var directory = Path.GetDirectoryName(fullCachePath) ?? throw new ResourceNotFoundException($"Provided path ({fullCachePath}) is not valid.");
- Directory.CreateDirectory(directory);
- using (var stream = result.Content)
- {
- // use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
- await using var fileStream = new FileStream(
- fullCachePath,
- FileMode.Create,
- FileAccess.Write,
- FileShare.None,
- IODefaults.FileStreamBufferSize,
- true);
-
- await stream.CopyToAsync(fileStream).ConfigureAwait(false);
- }
-
- var pointerCacheDirectory = Path.GetDirectoryName(pointerCachePath) ?? throw new ArgumentException($"Provided path ({pointerCachePath}) is not valid.", nameof(pointerCachePath));
-
- Directory.CreateDirectory(pointerCacheDirectory);
- await System.IO.File.WriteAllTextAsync(pointerCachePath, fullCachePath).ConfigureAwait(false);
- }
-
- /// <summary>
- /// Gets the full cache path.
- /// </summary>
- /// <param name="filename">The filename.</param>
- /// <returns>System.String.</returns>
- private string GetFullCachePath(string filename)
- => Path.Combine(_appPaths.CachePath, "remote-images", filename.Substring(0, 1), filename);
}
}
diff --git a/Jellyfin.Api/Controllers/MoviesController.cs b/Jellyfin.Api/Controllers/MoviesController.cs
index 4d788ad7d..d0a2358ae 100644
--- a/Jellyfin.Api/Controllers/MoviesController.cs
+++ b/Jellyfin.Api/Controllers/MoviesController.cs
@@ -89,7 +89,7 @@ namespace Jellyfin.Api.Controllers
// nameof(LiveTvProgram)
},
// IsMovie = true
- OrderBy = new[] { ItemSortBy.DatePlayed, ItemSortBy.Random }.Select(i => new ValueTuple<string, SortOrder>(i, SortOrder.Descending)).ToArray(),
+ OrderBy = new[] { (ItemSortBy.DatePlayed, SortOrder.Descending), (ItemSortBy.Random, SortOrder.Descending) },
Limit = 7,
ParentId = parentIdGuid,
Recursive = true,
@@ -110,7 +110,7 @@ namespace Jellyfin.Api.Controllers
{
IncludeItemTypes = itemTypes.ToArray(),
IsMovie = true,
- OrderBy = new[] { ItemSortBy.Random }.Select(i => new ValueTuple<string, SortOrder>(i, SortOrder.Descending)).ToArray(),
+ OrderBy = new[] { (ItemSortBy.Random, SortOrder.Descending) },
Limit = 10,
IsFavoriteOrLiked = true,
ExcludeItemIds = recentlyPlayedMovies.Select(i => i.Id).ToArray(),
@@ -120,7 +120,7 @@ namespace Jellyfin.Api.Controllers
DtoOptions = dtoOptions
});
- var mostRecentMovies = recentlyPlayedMovies.Take(6).ToList();
+ var mostRecentMovies = recentlyPlayedMovies.GetRange(0, Math.Min(recentlyPlayedMovies.Count, 6));
// Get recently played directors
var recentDirectors = GetDirectors(mostRecentMovies)
.ToList();
@@ -191,7 +191,8 @@ namespace Jellyfin.Api.Controllers
foreach (var name in names)
{
- var items = _libraryManager.GetItemList(new InternalItemsQuery(user)
+ var items = _libraryManager.GetItemList(
+ new InternalItemsQuery(user)
{
Person = name,
// Account for duplicates by imdb id, since the database doesn't support this yet
diff --git a/Jellyfin.Api/Controllers/PluginsController.cs b/Jellyfin.Api/Controllers/PluginsController.cs
index 24285bfb9..7a6130719 100644
--- a/Jellyfin.Api/Controllers/PluginsController.cs
+++ b/Jellyfin.Api/Controllers/PluginsController.cs
@@ -12,7 +12,6 @@ using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Json;
using MediaBrowser.Common.Plugins;
using MediaBrowser.Common.Updates;
-using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Plugins;
using Microsoft.AspNetCore.Authorization;
@@ -208,12 +207,7 @@ namespace Jellyfin.Api.Controllers
var plugins = _pluginManager.Plugins.Where(p => p.Id.Equals(pluginId));
// Select the un-instanced one first.
- var plugin = plugins.FirstOrDefault(p => p.Instance == null);
- if (plugin == null)
- {
- // Then by the status.
- plugin = plugins.OrderBy(p => p.Manifest.Status).FirstOrDefault();
- }
+ var plugin = plugins.FirstOrDefault(p => p.Instance == null) ?? plugins.OrderBy(p => p.Manifest.Status).FirstOrDefault();
if (plugin != null)
{
diff --git a/Jellyfin.Api/Controllers/RemoteImageController.cs b/Jellyfin.Api/Controllers/RemoteImageController.cs
index e226adc64..ec836f43e 100644
--- a/Jellyfin.Api/Controllers/RemoteImageController.cs
+++ b/Jellyfin.Api/Controllers/RemoteImageController.cs
@@ -146,58 +146,6 @@ namespace Jellyfin.Api.Controllers
}
/// <summary>
- /// Gets a remote image.
- /// </summary>
- /// <param name="imageUrl">The image url.</param>
- /// <response code="200">Remote image returned.</response>
- /// <response code="404">Remote image not found.</response>
- /// <returns>Image Stream.</returns>
- [HttpGet("Images/Remote")]
- [Produces(MediaTypeNames.Application.Octet)]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status404NotFound)]
- [ProducesImageFile]
- public async Task<ActionResult> GetRemoteImage([FromQuery, Required] Uri imageUrl)
- {
- var urlHash = imageUrl.ToString().GetMD5();
- var pointerCachePath = GetFullCachePath(urlHash.ToString());
-
- string? contentPath = null;
- var hasFile = false;
-
- try
- {
- contentPath = await System.IO.File.ReadAllTextAsync(pointerCachePath).ConfigureAwait(false);
- if (System.IO.File.Exists(contentPath))
- {
- hasFile = true;
- }
- }
- catch (FileNotFoundException)
- {
- // The file isn't cached yet
- }
- catch (IOException)
- {
- // The file isn't cached yet
- }
-
- if (!hasFile)
- {
- await DownloadImage(imageUrl, urlHash, pointerCachePath).ConfigureAwait(false);
- contentPath = await System.IO.File.ReadAllTextAsync(pointerCachePath).ConfigureAwait(false);
- }
-
- if (string.IsNullOrEmpty(contentPath))
- {
- return NotFound();
- }
-
- var contentType = MimeTypes.GetMimeType(contentPath);
- return PhysicalFile(contentPath, contentType);
- }
-
- /// <summary>
/// Downloads a remote image for an item.
/// </summary>
/// <param name="itemId">Item Id.</param>
diff --git a/Jellyfin.Api/Controllers/SearchController.cs b/Jellyfin.Api/Controllers/SearchController.cs
index 6c22050a7..73bdf9018 100644
--- a/Jellyfin.Api/Controllers/SearchController.cs
+++ b/Jellyfin.Api/Controllers/SearchController.cs
@@ -228,10 +228,7 @@ namespace Jellyfin.Api.Controllers
itemWithImage = GetParentWithImage<Series>(item, ImageType.Thumb);
}
- if (itemWithImage == null)
- {
- itemWithImage = GetParentWithImage<BaseItem>(item, ImageType.Thumb);
- }
+ itemWithImage ??= GetParentWithImage<BaseItem>(item, ImageType.Thumb);
if (itemWithImage != null)
{
diff --git a/Jellyfin.Api/Controllers/SuggestionsController.cs b/Jellyfin.Api/Controllers/SuggestionsController.cs
index a55f13e66..a811a29c3 100644
--- a/Jellyfin.Api/Controllers/SuggestionsController.cs
+++ b/Jellyfin.Api/Controllers/SuggestionsController.cs
@@ -69,7 +69,7 @@ namespace Jellyfin.Api.Controllers
var dtoOptions = new DtoOptions().AddClientFields(Request);
var result = _libraryManager.GetItemsResult(new InternalItemsQuery(user)
{
- OrderBy = new[] { ItemSortBy.Random }.Select(i => new ValueTuple<string, SortOrder>(i, SortOrder.Descending)).ToArray(),
+ OrderBy = new[] { (ItemSortBy.Random, SortOrder.Descending) },
MediaTypes = mediaType,
IncludeItemTypes = type,
IsVirtualItem = false,
diff --git a/Jellyfin.Api/Controllers/TvShowsController.cs b/Jellyfin.Api/Controllers/TvShowsController.cs
index e1c67f830..59400db2a 100644
--- a/Jellyfin.Api/Controllers/TvShowsController.cs
+++ b/Jellyfin.Api/Controllers/TvShowsController.cs
@@ -155,7 +155,7 @@ namespace Jellyfin.Api.Controllers
var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user)
{
IncludeItemTypes = new[] { nameof(Episode) },
- OrderBy = new[] { ItemSortBy.PremiereDate, ItemSortBy.SortName }.Select(i => new ValueTuple<string, SortOrder>(i, SortOrder.Ascending)).ToArray(),
+ OrderBy = new[] { (ItemSortBy.PremiereDate, SortOrder.Ascending), (ItemSortBy.SortName, SortOrder.Ascending) },
MinPremiereDate = minPremiereDate,
StartIndex = startIndex,
Limit = limit,
diff --git a/Jellyfin.Api/Controllers/UniversalAudioController.cs b/Jellyfin.Api/Controllers/UniversalAudioController.cs
index dcdd8b367..679f055bc 100644
--- a/Jellyfin.Api/Controllers/UniversalAudioController.cs
+++ b/Jellyfin.Api/Controllers/UniversalAudioController.cs
@@ -298,9 +298,9 @@ namespace Jellyfin.Api.Controllers
{
Type = DlnaProfileType.Audio,
Context = EncodingContext.Streaming,
- Container = transcodingContainer,
- AudioCodec = audioCodec,
- Protocol = transcodingProtocol,
+ Container = transcodingContainer ?? "mp3",
+ AudioCodec = audioCodec ?? "mp3",
+ Protocol = transcodingProtocol ?? "http",
BreakOnNonKeyFrames = breakOnNonKeyFrames ?? false,
MaxAudioChannels = transcodingAudioChannels?.ToString(CultureInfo.InvariantCulture)
}
diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs
index 3c0d2aca1..b13db4baa 100644
--- a/Jellyfin.Api/Controllers/UserController.cs
+++ b/Jellyfin.Api/Controllers/UserController.cs
@@ -177,11 +177,9 @@ namespace Jellyfin.Api.Controllers
return StatusCode(StatusCodes.Status403Forbidden, "Only sha1 password is not allowed.");
}
- // Password should always be null
AuthenticateUserByName request = new AuthenticateUserByName
{
Username = user.Username,
- Password = null,
Pw = pw
};
return await AuthenticateUserByName(request).ConfigureAwait(false);
@@ -208,7 +206,6 @@ namespace Jellyfin.Api.Controllers
DeviceId = auth.DeviceId,
DeviceName = auth.Device,
Password = request.Pw,
- PasswordSha1 = request.Password,
RemoteEndPoint = HttpContext.GetNormalizedRemoteIp().ToString(),
Username = request.Username
}).ConfigureAwait(false);
diff --git a/Jellyfin.Api/Controllers/VideoHlsController.cs b/Jellyfin.Api/Controllers/VideoHlsController.cs
index 445bad69d..800cfa81f 100644
--- a/Jellyfin.Api/Controllers/VideoHlsController.cs
+++ b/Jellyfin.Api/Controllers/VideoHlsController.cs
@@ -48,9 +48,6 @@ namespace Jellyfin.Api.Controllers
private readonly IMediaSourceManager _mediaSourceManager;
private readonly IServerConfigurationManager _serverConfigurationManager;
private readonly IMediaEncoder _mediaEncoder;
- private readonly IFileSystem _fileSystem;
- private readonly ISubtitleEncoder _subtitleEncoder;
- private readonly IConfiguration _configuration;
private readonly IDeviceManager _deviceManager;
private readonly TranscodingJobHelper _transcodingJobHelper;
private readonly ILogger<VideoHlsController> _logger;
@@ -60,9 +57,6 @@ namespace Jellyfin.Api.Controllers
/// Initializes a new instance of the <see cref="VideoHlsController"/> class.
/// </summary>
/// <param name="mediaEncoder">Instance of the <see cref="IMediaEncoder"/> interface.</param>
- /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
- /// <param name="subtitleEncoder">Instance of the <see cref="ISubtitleEncoder"/> interface.</param>
- /// <param name="configuration">Instance of the <see cref="IConfiguration"/> interface.</param>
/// <param name="dlnaManager">Instance of the <see cref="IDlnaManager"/> interface.</param>
/// <param name="userManger">Instance of the <see cref="IUserManager"/> interface.</param>
/// <param name="authorizationContext">Instance of the <see cref="IAuthorizationContext"/> interface.</param>
@@ -72,11 +66,9 @@ namespace Jellyfin.Api.Controllers
/// <param name="deviceManager">Instance of the <see cref="IDeviceManager"/> interface.</param>
/// <param name="transcodingJobHelper">The <see cref="TranscodingJobHelper"/> singleton.</param>
/// <param name="logger">Instance of the <see cref="ILogger{VideoHlsController}"/>.</param>
+ /// <param name="encodingHelper">Instance of <see cref="EncodingHelper"/>.</param>
public VideoHlsController(
IMediaEncoder mediaEncoder,
- IFileSystem fileSystem,
- ISubtitleEncoder subtitleEncoder,
- IConfiguration configuration,
IDlnaManager dlnaManager,
IUserManager userManger,
IAuthorizationContext authorizationContext,
@@ -85,10 +77,9 @@ namespace Jellyfin.Api.Controllers
IServerConfigurationManager serverConfigurationManager,
IDeviceManager deviceManager,
TranscodingJobHelper transcodingJobHelper,
- ILogger<VideoHlsController> logger)
+ ILogger<VideoHlsController> logger,
+ EncodingHelper encodingHelper)
{
- _encodingHelper = new EncodingHelper(mediaEncoder, fileSystem, subtitleEncoder, configuration);
-
_dlnaManager = dlnaManager;
_authContext = authorizationContext;
_userManager = userManger;
@@ -96,12 +87,11 @@ namespace Jellyfin.Api.Controllers
_mediaSourceManager = mediaSourceManager;
_serverConfigurationManager = serverConfigurationManager;
_mediaEncoder = mediaEncoder;
- _fileSystem = fileSystem;
- _subtitleEncoder = subtitleEncoder;
- _configuration = configuration;
_deviceManager = deviceManager;
_transcodingJobHelper = transcodingJobHelper;
_logger = logger;
+ _encodingHelper = encodingHelper;
+
_encodingOptions = serverConfigurationManager.GetEncodingOptions();
}
@@ -285,9 +275,7 @@ namespace Jellyfin.Api.Controllers
_libraryManager,
_serverConfigurationManager,
_mediaEncoder,
- _fileSystem,
- _subtitleEncoder,
- _configuration,
+ _encodingHelper,
_dlnaManager,
_deviceManager,
_transcodingJobHelper,
diff --git a/Jellyfin.Api/Controllers/VideosController.cs b/Jellyfin.Api/Controllers/VideosController.cs
index 699ca5327..e544d001e 100644
--- a/Jellyfin.Api/Controllers/VideosController.cs
+++ b/Jellyfin.Api/Controllers/VideosController.cs
@@ -49,12 +49,10 @@ namespace Jellyfin.Api.Controllers
private readonly IMediaSourceManager _mediaSourceManager;
private readonly IServerConfigurationManager _serverConfigurationManager;
private readonly IMediaEncoder _mediaEncoder;
- private readonly IFileSystem _fileSystem;
- private readonly ISubtitleEncoder _subtitleEncoder;
- private readonly IConfiguration _configuration;
private readonly IDeviceManager _deviceManager;
private readonly TranscodingJobHelper _transcodingJobHelper;
private readonly IHttpClientFactory _httpClientFactory;
+ private readonly EncodingHelper _encodingHelper;
private readonly TranscodingJobType _transcodingJobType = TranscodingJobType.Progressive;
@@ -69,12 +67,10 @@ namespace Jellyfin.Api.Controllers
/// <param name="mediaSourceManager">Instance of the <see cref="IMediaSourceManager"/> interface.</param>
/// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
/// <param name="mediaEncoder">Instance of the <see cref="IMediaEncoder"/> interface.</param>
- /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
- /// <param name="subtitleEncoder">Instance of the <see cref="ISubtitleEncoder"/> interface.</param>
- /// <param name="configuration">Instance of the <see cref="IConfiguration"/> interface.</param>
/// <param name="deviceManager">Instance of the <see cref="IDeviceManager"/> interface.</param>
/// <param name="transcodingJobHelper">Instance of the <see cref="TranscodingJobHelper"/> class.</param>
/// <param name="httpClientFactory">Instance of the <see cref="IHttpClientFactory"/> interface.</param>
+ /// <param name="encodingHelper">Instance of <see cref="EncodingHelper"/>.</param>
public VideosController(
ILibraryManager libraryManager,
IUserManager userManager,
@@ -84,12 +80,10 @@ namespace Jellyfin.Api.Controllers
IMediaSourceManager mediaSourceManager,
IServerConfigurationManager serverConfigurationManager,
IMediaEncoder mediaEncoder,
- IFileSystem fileSystem,
- ISubtitleEncoder subtitleEncoder,
- IConfiguration configuration,
IDeviceManager deviceManager,
TranscodingJobHelper transcodingJobHelper,
- IHttpClientFactory httpClientFactory)
+ IHttpClientFactory httpClientFactory,
+ EncodingHelper encodingHelper)
{
_libraryManager = libraryManager;
_userManager = userManager;
@@ -99,12 +93,10 @@ namespace Jellyfin.Api.Controllers
_mediaSourceManager = mediaSourceManager;
_serverConfigurationManager = serverConfigurationManager;
_mediaEncoder = mediaEncoder;
- _fileSystem = fileSystem;
- _subtitleEncoder = subtitleEncoder;
- _configuration = configuration;
_deviceManager = deviceManager;
_transcodingJobHelper = transcodingJobHelper;
_httpClientFactory = httpClientFactory;
+ _encodingHelper = encodingHelper;
}
/// <summary>
@@ -444,9 +436,7 @@ namespace Jellyfin.Api.Controllers
_libraryManager,
_serverConfigurationManager,
_mediaEncoder,
- _fileSystem,
- _subtitleEncoder,
- _configuration,
+ _encodingHelper,
_dlnaManager,
_deviceManager,
_transcodingJobHelper,
@@ -515,8 +505,7 @@ namespace Jellyfin.Api.Controllers
// Need to start ffmpeg (because media can't be returned directly)
var encodingOptions = _serverConfigurationManager.GetEncodingOptions();
- var encodingHelper = new EncodingHelper(_mediaEncoder, _fileSystem, _subtitleEncoder, _configuration);
- var ffmpegCommandLineArguments = encodingHelper.GetProgressiveVideoFullCommandLine(state, encodingOptions, outputPath, "superfast");
+ var ffmpegCommandLineArguments = _encodingHelper.GetProgressiveVideoFullCommandLine(state, encodingOptions, outputPath, "superfast");
return await FileStreamResponseHelpers.GetTranscodedFile(
state,
isHeadRequest,
@@ -538,7 +527,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="deviceProfileId">Optional. The dlna device profile id to utilize.</param>
/// <param name="playSessionId">The play session id.</param>
/// <param name="segmentContainer">The segment container.</param>
- /// <param name="segmentLength">The segment lenght.</param>
+ /// <param name="segmentLength">The segment length.</param>
/// <param name="minSegments">The minimum number of segments.</param>
/// <param name="mediaSourceId">The media version id, if playing an alternate version.</param>
/// <param name="deviceId">The device id of the client requesting. Used to stop encoding processes when needed.</param>
@@ -567,7 +556,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="maxVideoBitDepth">Optional. The maximum video bit depth.</param>
/// <param name="requireAvc">Optional. Whether to require avc.</param>
/// <param name="deInterlace">Optional. Whether to deinterlace the video.</param>
- /// <param name="requireNonAnamorphic">Optional. Whether to require a non anamporphic stream.</param>
+ /// <param name="requireNonAnamorphic">Optional. Whether to require a non anamorphic stream.</param>
/// <param name="transcodingMaxAudioChannels">Optional. The maximum number of audio channels to transcode.</param>
/// <param name="cpuCoreLimit">Optional. The limit of how many cpu cores to use.</param>
/// <param name="liveStreamId">The live stream id.</param>
@@ -581,8 +570,8 @@ namespace Jellyfin.Api.Controllers
/// <param name="streamOptions">Optional. The streaming options.</param>
/// <response code="200">Video stream returned.</response>
/// <returns>A <see cref="FileResult"/> containing the audio file.</returns>
- [HttpGet("{itemId}/{stream=stream}.{container}")]
- [HttpHead("{itemId}/{stream=stream}.{container}", Name = "HeadVideoStreamByContainer")]
+ [HttpGet("{itemId}/stream.{container}")]
+ [HttpHead("{itemId}/stream.{container}", Name = "HeadVideoStreamByContainer")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesVideoFile]
public Task<ActionResult> GetVideoStreamByContainer(
diff --git a/Jellyfin.Api/Extensions/DtoExtensions.cs b/Jellyfin.Api/Extensions/DtoExtensions.cs
index e0c744325..06173315a 100644
--- a/Jellyfin.Api/Extensions/DtoExtensions.cs
+++ b/Jellyfin.Api/Extensions/DtoExtensions.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
-using System.Linq;
using Jellyfin.Api.Helpers;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Dto;
diff --git a/Jellyfin.Api/Helpers/AudioHelper.cs b/Jellyfin.Api/Helpers/AudioHelper.cs
index 21ec2d32f..cf35ee23a 100644
--- a/Jellyfin.Api/Helpers/AudioHelper.cs
+++ b/Jellyfin.Api/Helpers/AudioHelper.cs
@@ -1,5 +1,4 @@
-using System;
-using System.Net.Http;
+using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Api.Models.StreamingDtos;
@@ -33,13 +32,11 @@ namespace Jellyfin.Api.Helpers
private readonly IMediaSourceManager _mediaSourceManager;
private readonly IServerConfigurationManager _serverConfigurationManager;
private readonly IMediaEncoder _mediaEncoder;
- private readonly IFileSystem _fileSystem;
- private readonly ISubtitleEncoder _subtitleEncoder;
- private readonly IConfiguration _configuration;
private readonly IDeviceManager _deviceManager;
private readonly TranscodingJobHelper _transcodingJobHelper;
private readonly IHttpClientFactory _httpClientFactory;
private readonly IHttpContextAccessor _httpContextAccessor;
+ private readonly EncodingHelper _encodingHelper;
/// <summary>
/// Initializes a new instance of the <see cref="AudioHelper"/> class.
@@ -51,13 +48,11 @@ namespace Jellyfin.Api.Helpers
/// <param name="mediaSourceManager">Instance of the <see cref="IMediaSourceManager"/> interface.</param>
/// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
/// <param name="mediaEncoder">Instance of the <see cref="IMediaEncoder"/> interface.</param>
- /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
- /// <param name="subtitleEncoder">Instance of the <see cref="ISubtitleEncoder"/> interface.</param>
- /// <param name="configuration">Instance of the <see cref="IConfiguration"/> interface.</param>
/// <param name="deviceManager">Instance of the <see cref="IDeviceManager"/> interface.</param>
/// <param name="transcodingJobHelper">Instance of <see cref="TranscodingJobHelper"/>.</param>
/// <param name="httpClientFactory">Instance of the <see cref="IHttpClientFactory"/> interface.</param>
/// <param name="httpContextAccessor">Instance of the <see cref="IHttpContextAccessor"/> interface.</param>
+ /// <param name="encodingHelper">Instance of <see cref="EncodingHelper"/>.</param>
public AudioHelper(
IDlnaManager dlnaManager,
IAuthorizationContext authContext,
@@ -66,13 +61,11 @@ namespace Jellyfin.Api.Helpers
IMediaSourceManager mediaSourceManager,
IServerConfigurationManager serverConfigurationManager,
IMediaEncoder mediaEncoder,
- IFileSystem fileSystem,
- ISubtitleEncoder subtitleEncoder,
- IConfiguration configuration,
IDeviceManager deviceManager,
TranscodingJobHelper transcodingJobHelper,
IHttpClientFactory httpClientFactory,
- IHttpContextAccessor httpContextAccessor)
+ IHttpContextAccessor httpContextAccessor,
+ EncodingHelper encodingHelper)
{
_dlnaManager = dlnaManager;
_authContext = authContext;
@@ -81,13 +74,11 @@ namespace Jellyfin.Api.Helpers
_mediaSourceManager = mediaSourceManager;
_serverConfigurationManager = serverConfigurationManager;
_mediaEncoder = mediaEncoder;
- _fileSystem = fileSystem;
- _subtitleEncoder = subtitleEncoder;
- _configuration = configuration;
_deviceManager = deviceManager;
_transcodingJobHelper = transcodingJobHelper;
_httpClientFactory = httpClientFactory;
_httpContextAccessor = httpContextAccessor;
+ _encodingHelper = encodingHelper;
}
/// <summary>
@@ -117,9 +108,7 @@ namespace Jellyfin.Api.Helpers
_libraryManager,
_serverConfigurationManager,
_mediaEncoder,
- _fileSystem,
- _subtitleEncoder,
- _configuration,
+ _encodingHelper,
_dlnaManager,
_deviceManager,
_transcodingJobHelper,
@@ -188,8 +177,7 @@ namespace Jellyfin.Api.Helpers
// Need to start ffmpeg (because media can't be returned directly)
var encodingOptions = _serverConfigurationManager.GetEncodingOptions();
- var encodingHelper = new EncodingHelper(_mediaEncoder, _fileSystem, _subtitleEncoder, _configuration);
- var ffmpegCommandLineArguments = encodingHelper.GetProgressiveAudioFullCommandLine(state, encodingOptions, outputPath);
+ var ffmpegCommandLineArguments = _encodingHelper.GetProgressiveAudioFullCommandLine(state, encodingOptions, outputPath);
return await FileStreamResponseHelpers.GetTranscodedFile(
state,
isHeadRequest,
diff --git a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs
index 751b48682..fcada0e77 100644
--- a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs
+++ b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs
@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net;
-using System.Net.Mime;
using System.Security.Claims;
using System.Text;
using System.Threading;
@@ -41,14 +40,12 @@ namespace Jellyfin.Api.Helpers
private readonly IMediaSourceManager _mediaSourceManager;
private readonly IServerConfigurationManager _serverConfigurationManager;
private readonly IMediaEncoder _mediaEncoder;
- private readonly IFileSystem _fileSystem;
- private readonly ISubtitleEncoder _subtitleEncoder;
- private readonly IConfiguration _configuration;
private readonly IDeviceManager _deviceManager;
private readonly TranscodingJobHelper _transcodingJobHelper;
private readonly INetworkManager _networkManager;
private readonly ILogger<DynamicHlsHelper> _logger;
private readonly IHttpContextAccessor _httpContextAccessor;
+ private readonly EncodingHelper _encodingHelper;
/// <summary>
/// Initializes a new instance of the <see cref="DynamicHlsHelper"/> class.
@@ -60,14 +57,12 @@ namespace Jellyfin.Api.Helpers
/// <param name="mediaSourceManager">Instance of the <see cref="IMediaSourceManager"/> interface.</param>
/// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
/// <param name="mediaEncoder">Instance of the <see cref="IMediaEncoder"/> interface.</param>
- /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
- /// <param name="subtitleEncoder">Instance of the <see cref="ISubtitleEncoder"/> interface.</param>
- /// <param name="configuration">Instance of the <see cref="IConfiguration"/> interface.</param>
/// <param name="deviceManager">Instance of the <see cref="IDeviceManager"/> interface.</param>
/// <param name="transcodingJobHelper">Instance of <see cref="TranscodingJobHelper"/>.</param>
/// <param name="networkManager">Instance of the <see cref="INetworkManager"/> interface.</param>
/// <param name="logger">Instance of the <see cref="ILogger{DynamicHlsHelper}"/> interface.</param>
/// <param name="httpContextAccessor">Instance of the <see cref="IHttpContextAccessor"/> interface.</param>
+ /// <param name="encodingHelper">Instance of <see cref="EncodingHelper"/>.</param>
public DynamicHlsHelper(
ILibraryManager libraryManager,
IUserManager userManager,
@@ -76,14 +71,12 @@ namespace Jellyfin.Api.Helpers
IMediaSourceManager mediaSourceManager,
IServerConfigurationManager serverConfigurationManager,
IMediaEncoder mediaEncoder,
- IFileSystem fileSystem,
- ISubtitleEncoder subtitleEncoder,
- IConfiguration configuration,
IDeviceManager deviceManager,
TranscodingJobHelper transcodingJobHelper,
INetworkManager networkManager,
ILogger<DynamicHlsHelper> logger,
- IHttpContextAccessor httpContextAccessor)
+ IHttpContextAccessor httpContextAccessor,
+ EncodingHelper encodingHelper)
{
_libraryManager = libraryManager;
_userManager = userManager;
@@ -92,14 +85,12 @@ namespace Jellyfin.Api.Helpers
_mediaSourceManager = mediaSourceManager;
_serverConfigurationManager = serverConfigurationManager;
_mediaEncoder = mediaEncoder;
- _fileSystem = fileSystem;
- _subtitleEncoder = subtitleEncoder;
- _configuration = configuration;
_deviceManager = deviceManager;
_transcodingJobHelper = transcodingJobHelper;
_networkManager = networkManager;
_logger = logger;
_httpContextAccessor = httpContextAccessor;
+ _encodingHelper = encodingHelper;
}
/// <summary>
@@ -145,9 +136,7 @@ namespace Jellyfin.Api.Helpers
_libraryManager,
_serverConfigurationManager,
_mediaEncoder,
- _fileSystem,
- _subtitleEncoder,
- _configuration,
+ _encodingHelper,
_dlnaManager,
_deviceManager,
_transcodingJobHelper,
@@ -228,9 +217,8 @@ namespace Jellyfin.Api.Helpers
var sdrVideoUrl = ReplaceProfile(playlistUrl, "hevc", string.Join(',', requestedVideoProfiles), "main");
sdrVideoUrl += "&AllowVideoStreamCopy=false";
- EncodingHelper encodingHelper = new EncodingHelper(_mediaEncoder, _fileSystem, _subtitleEncoder, _configuration);
- var sdrOutputVideoBitrate = encodingHelper.GetVideoBitrateParamValue(state.VideoRequest, state.VideoStream, state.OutputVideoCodec) ?? 0;
- var sdrOutputAudioBitrate = encodingHelper.GetAudioBitrateParam(state.VideoRequest, state.AudioStream) ?? 0;
+ var sdrOutputVideoBitrate = _encodingHelper.GetVideoBitrateParamValue(state.VideoRequest, state.VideoStream, state.OutputVideoCodec) ?? 0;
+ var sdrOutputAudioBitrate = _encodingHelper.GetAudioBitrateParam(state.VideoRequest, state.AudioStream) ?? 0;
var sdrTotalBitrate = sdrOutputAudioBitrate + sdrOutputVideoBitrate;
AppendPlaylist(builder, state, sdrVideoUrl, sdrTotalBitrate, subtitleGroup);
diff --git a/Jellyfin.Api/Helpers/MediaInfoHelper.cs b/Jellyfin.Api/Helpers/MediaInfoHelper.cs
index f07271821..295cfaf08 100644
--- a/Jellyfin.Api/Helpers/MediaInfoHelper.cs
+++ b/Jellyfin.Api/Helpers/MediaInfoHelper.cs
@@ -309,7 +309,7 @@ namespace Jellyfin.Api.Helpers
{
if (!user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding)
&& !user.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding)
- && !user.HasPermission(PermissionKind.EnablePlaybackRemuxing))
+ && user.HasPermission(PermissionKind.EnablePlaybackRemuxing))
{
options.ForceDirectStream = true;
}
diff --git a/Jellyfin.Api/Helpers/ProgressiveFileCopier.cs b/Jellyfin.Api/Helpers/ProgressiveFileCopier.cs
index 8bddf00d5..963e17724 100644
--- a/Jellyfin.Api/Helpers/ProgressiveFileCopier.cs
+++ b/Jellyfin.Api/Helpers/ProgressiveFileCopier.cs
@@ -71,7 +71,8 @@ namespace Jellyfin.Api.Helpers
/// <returns>A <see cref="Task"/>.</returns>
public async Task WriteToAsync(Stream outputStream, CancellationToken cancellationToken)
{
- cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _cancellationToken).Token;
+ using var linkedCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _cancellationToken);
+ cancellationToken = linkedCancellationTokenSource.Token;
try
{
diff --git a/Jellyfin.Api/Helpers/StreamingHelpers.cs b/Jellyfin.Api/Helpers/StreamingHelpers.cs
index e2306aa27..8cffe9c4c 100644
--- a/Jellyfin.Api/Helpers/StreamingHelpers.cs
+++ b/Jellyfin.Api/Helpers/StreamingHelpers.cs
@@ -41,9 +41,7 @@ namespace Jellyfin.Api.Helpers
/// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
/// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
/// <param name="mediaEncoder">Instance of the <see cref="IMediaEncoder"/> interface.</param>
- /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
- /// <param name="subtitleEncoder">Instance of the <see cref="ISubtitleEncoder"/> interface.</param>
- /// <param name="configuration">Instance of the <see cref="IConfiguration"/> interface.</param>
+ /// <param name="encodingHelper">Instance of <see cref="EncodingHelper"/>.</param>
/// <param name="dlnaManager">Instance of the <see cref="IDlnaManager"/> interface.</param>
/// <param name="deviceManager">Instance of the <see cref="IDeviceManager"/> interface.</param>
/// <param name="transcodingJobHelper">Initialized <see cref="TranscodingJobHelper"/>.</param>
@@ -59,16 +57,13 @@ namespace Jellyfin.Api.Helpers
ILibraryManager libraryManager,
IServerConfigurationManager serverConfigurationManager,
IMediaEncoder mediaEncoder,
- IFileSystem fileSystem,
- ISubtitleEncoder subtitleEncoder,
- IConfiguration configuration,
+ EncodingHelper encodingHelper,
IDlnaManager dlnaManager,
IDeviceManager deviceManager,
TranscodingJobHelper transcodingJobHelper,
TranscodingJobType transcodingJobType,
CancellationToken cancellationToken)
{
- EncodingHelper encodingHelper = new EncodingHelper(mediaEncoder, fileSystem, subtitleEncoder, configuration);
// Parse the DLNA time seek header
if (!streamingRequest.StartTimeTicks.HasValue)
{
@@ -121,14 +116,14 @@ namespace Jellyfin.Api.Helpers
if (!string.IsNullOrWhiteSpace(streamingRequest.AudioCodec))
{
state.SupportedAudioCodecs = streamingRequest.AudioCodec.Split(',', StringSplitOptions.RemoveEmptyEntries);
- state.Request.AudioCodec = state.SupportedAudioCodecs.FirstOrDefault(i => mediaEncoder.CanEncodeToAudioCodec(i))
+ state.Request.AudioCodec = state.SupportedAudioCodecs.FirstOrDefault(mediaEncoder.CanEncodeToAudioCodec)
?? state.SupportedAudioCodecs.FirstOrDefault();
}
if (!string.IsNullOrWhiteSpace(streamingRequest.SubtitleCodec))
{
state.SupportedSubtitleCodecs = streamingRequest.SubtitleCodec.Split(',', StringSplitOptions.RemoveEmptyEntries);
- state.Request.SubtitleCodec = state.SupportedSubtitleCodecs.FirstOrDefault(i => mediaEncoder.CanEncodeToSubtitleCodec(i))
+ state.Request.SubtitleCodec = state.SupportedSubtitleCodecs.FirstOrDefault(mediaEncoder.CanEncodeToSubtitleCodec)
?? state.SupportedSubtitleCodecs.FirstOrDefault();
}
@@ -297,16 +292,14 @@ namespace Jellyfin.Api.Helpers
}
}
- if (profile == null)
- {
- profile = dlnaManager.GetDefaultProfile();
- }
+ profile ??= dlnaManager.GetDefaultProfile();
var audioCodec = state.ActualOutputAudioCodec;
if (!state.IsVideoRequest)
{
- responseHeaders.Add("contentFeatures.dlna.org", new ContentFeatureBuilder(profile).BuildAudioHeader(
+ responseHeaders.Add("contentFeatures.dlna.org", ContentFeatureBuilder.BuildAudioHeader(
+ profile,
state.OutputContainer,
audioCodec,
state.OutputAudioBitrate,
@@ -323,7 +316,7 @@ namespace Jellyfin.Api.Helpers
responseHeaders.Add(
"contentFeatures.dlna.org",
- new ContentFeatureBuilder(profile).BuildVideoHeader(state.OutputContainer, videoCodec, audioCodec, state.OutputWidth, state.OutputHeight, state.TargetVideoBitDepth, state.OutputVideoBitrate, state.TargetTimestamp, isStaticallyStreamed, state.RunTimeTicks, state.TargetVideoProfile, state.TargetVideoLevel, state.TargetFramerate, state.TargetPacketLength, state.TranscodeSeekInfo, state.IsTargetAnamorphic, state.IsTargetInterlaced, state.TargetRefFrames, state.TargetVideoStreamCount, state.TargetAudioStreamCount, state.TargetVideoCodecTag, state.IsTargetAVC).FirstOrDefault() ?? string.Empty);
+ ContentFeatureBuilder.BuildVideoHeader(profile, state.OutputContainer, videoCodec, audioCodec, state.OutputWidth, state.OutputHeight, state.TargetVideoBitDepth, state.OutputVideoBitrate, state.TargetTimestamp, isStaticallyStreamed, state.RunTimeTicks, state.TargetVideoProfile, state.TargetVideoLevel, state.TargetFramerate, state.TargetPacketLength, state.TranscodeSeekInfo, state.IsTargetAnamorphic, state.IsTargetInterlaced, state.TargetRefFrames, state.TargetVideoStreamCount, state.TargetAudioStreamCount, state.TargetVideoCodecTag, state.IsTargetAVC).FirstOrDefault() ?? string.Empty);
}
}
diff --git a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs b/Jellyfin.Api/Helpers/TranscodingJobHelper.cs
index 240d132b1..0879cbd18 100644
--- a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs
+++ b/Jellyfin.Api/Helpers/TranscodingJobHelper.cs
@@ -62,8 +62,7 @@ namespace Jellyfin.Api.Helpers
/// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
/// <param name="sessionManager">Instance of the <see cref="ISessionManager"/> interface.</param>
/// <param name="authorizationContext">Instance of the <see cref="IAuthorizationContext"/> interface.</param>
- /// <param name="subtitleEncoder">Instance of the <see cref="ISubtitleEncoder"/> interface.</param>
- /// <param name="configuration">Instance of the <see cref="IConfiguration"/> interface.</param>
+ /// <param name="encodingHelper">Instance of <see cref="EncodingHelper"/>.</param>
/// <param name="loggerFactory">Instance of the <see cref="ILoggerFactory"/> interface.</param>
public TranscodingJobHelper(
ILogger<TranscodingJobHelper> logger,
@@ -73,8 +72,7 @@ namespace Jellyfin.Api.Helpers
IServerConfigurationManager serverConfigurationManager,
ISessionManager sessionManager,
IAuthorizationContext authorizationContext,
- ISubtitleEncoder subtitleEncoder,
- IConfiguration configuration,
+ EncodingHelper encodingHelper,
ILoggerFactory loggerFactory)
{
_logger = logger;
@@ -84,8 +82,8 @@ namespace Jellyfin.Api.Helpers
_serverConfigurationManager = serverConfigurationManager;
_sessionManager = sessionManager;
_authorizationContext = authorizationContext;
+ _encodingHelper = encodingHelper;
_loggerFactory = loggerFactory;
- _encodingHelper = new EncodingHelper(mediaEncoder, fileSystem, subtitleEncoder, configuration);
DeleteEncodedMediaCache();
diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj
index d6dc5a2dc..c10c34b59 100644
--- a/Jellyfin.Api/Jellyfin.Api.csproj
+++ b/Jellyfin.Api/Jellyfin.Api.csproj
@@ -17,8 +17,8 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="5.0.5" />
<PackageReference Include="Microsoft.Extensions.Http" Version="5.0.0" />
- <PackageReference Include="Swashbuckle.AspNetCore" Version="6.0.7" />
- <PackageReference Include="Swashbuckle.AspNetCore.ReDoc" Version="6.0.7" />
+ <PackageReference Include="Swashbuckle.AspNetCore" Version="6.1.4" />
+ <PackageReference Include="Swashbuckle.AspNetCore.ReDoc" Version="6.1.4" />
</ItemGroup>
<ItemGroup>
diff --git a/Jellyfin.Api/Models/UserDtos/AuthenticateUserByName.cs b/Jellyfin.Api/Models/UserDtos/AuthenticateUserByName.cs
index 393627435..41f7b169e 100644
--- a/Jellyfin.Api/Models/UserDtos/AuthenticateUserByName.cs
+++ b/Jellyfin.Api/Models/UserDtos/AuthenticateUserByName.cs
@@ -1,4 +1,6 @@
-namespace Jellyfin.Api.Models.UserDtos
+using System;
+
+namespace Jellyfin.Api.Models.UserDtos
{
/// <summary>
/// The authenticate user by name request body.
@@ -18,6 +20,7 @@
/// <summary>
/// Gets or sets the sha1-hashed password.
/// </summary>
+ [Obsolete("Send password using pw field")]
public string? Password { get; set; }
}
}
diff --git a/Jellyfin.Data/Entities/AccessSchedule.cs b/Jellyfin.Data/Entities/AccessSchedule.cs
index 7974d3add..befc4ca02 100644
--- a/Jellyfin.Data/Entities/AccessSchedule.cs
+++ b/Jellyfin.Data/Entities/AccessSchedule.cs
@@ -26,20 +26,20 @@ namespace Jellyfin.Data.Entities
}
/// <summary>
- /// Gets or sets the id of this instance.
+ /// Gets the id of this instance.
/// </summary>
/// <remarks>
/// Identity, Indexed, Required.
/// </remarks>
[XmlIgnore]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id { get; protected set; }
+ public int Id { get; private set; }
/// <summary>
- /// Gets or sets the id of the associated user.
+ /// Gets the id of the associated user.
/// </summary>
[XmlIgnore]
- public Guid UserId { get; protected set; }
+ public Guid UserId { get; private set; }
/// <summary>
/// Gets or sets the day of week.
diff --git a/Jellyfin.Data/Entities/ActivityLog.cs b/Jellyfin.Data/Entities/ActivityLog.cs
index e4534e8b5..1d1b86552 100644
--- a/Jellyfin.Data/Entities/ActivityLog.cs
+++ b/Jellyfin.Data/Entities/ActivityLog.cs
@@ -38,11 +38,10 @@ namespace Jellyfin.Data.Entities
}
/// <summary>
- /// Gets or sets the identity of this instance.
- /// This is the key in the backing database.
+ /// Gets the identity of this instance.
/// </summary>
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id { get; protected set; }
+ public int Id { get; private set; }
/// <summary>
/// Gets or sets the name.
@@ -120,7 +119,7 @@ namespace Jellyfin.Data.Entities
/// <inheritdoc />
[ConcurrencyCheck]
- public uint RowVersion { get; set; }
+ public uint RowVersion { get; private set; }
/// <inheritdoc />
public void OnSavingChanges()
diff --git a/Jellyfin.Data/Entities/CustomItemDisplayPreferences.cs b/Jellyfin.Data/Entities/CustomItemDisplayPreferences.cs
index de37fb544..b07b7c731 100644
--- a/Jellyfin.Data/Entities/CustomItemDisplayPreferences.cs
+++ b/Jellyfin.Data/Entities/CustomItemDisplayPreferences.cs
@@ -27,13 +27,13 @@ namespace Jellyfin.Data.Entities
}
/// <summary>
- /// Gets or sets the Id.
+ /// Gets the Id.
/// </summary>
/// <remarks>
/// Required.
/// </remarks>
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id { get; protected set; }
+ public int Id { get; private set; }
/// <summary>
/// Gets or sets the user Id.
diff --git a/Jellyfin.Data/Entities/DisplayPreferences.cs b/Jellyfin.Data/Entities/DisplayPreferences.cs
index 64cd6812a..646961238 100644
--- a/Jellyfin.Data/Entities/DisplayPreferences.cs
+++ b/Jellyfin.Data/Entities/DisplayPreferences.cs
@@ -1,6 +1,4 @@
-#pragma warning disable CA2227
-
-using System;
+using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
@@ -35,13 +33,13 @@ namespace Jellyfin.Data.Entities
}
/// <summary>
- /// Gets or sets the Id.
+ /// Gets the Id.
/// </summary>
/// <remarks>
/// Required.
/// </remarks>
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id { get; protected set; }
+ public int Id { get; private set; }
/// <summary>
/// Gets or sets the user Id.
@@ -145,8 +143,8 @@ namespace Jellyfin.Data.Entities
public string? TvHome { get; set; }
/// <summary>
- /// Gets or sets the home sections.
+ /// Gets the home sections.
/// </summary>
- public virtual ICollection<HomeSection> HomeSections { get; protected set; }
+ public virtual ICollection<HomeSection> HomeSections { get; private set; }
}
}
diff --git a/Jellyfin.Data/Entities/Group.cs b/Jellyfin.Data/Entities/Group.cs
index b14e22b7b..14da0bb15 100644
--- a/Jellyfin.Data/Entities/Group.cs
+++ b/Jellyfin.Data/Entities/Group.cs
@@ -1,5 +1,3 @@
-#pragma warning disable CA2227
-
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
@@ -33,12 +31,12 @@ namespace Jellyfin.Data.Entities
}
/// <summary>
- /// Gets or sets the id of this group.
+ /// Gets the id of this group.
/// </summary>
/// <remarks>
/// Identity, Indexed, Required.
/// </remarks>
- public Guid Id { get; protected set; }
+ public Guid Id { get; private set; }
/// <summary>
/// Gets or sets the group's name.
@@ -52,17 +50,17 @@ namespace Jellyfin.Data.Entities
/// <inheritdoc />
[ConcurrencyCheck]
- public uint RowVersion { get; set; }
+ public uint RowVersion { get; private set; }
/// <summary>
- /// Gets or sets a collection containing the group's permissions.
+ /// Gets a collection containing the group's permissions.
/// </summary>
- public virtual ICollection<Permission> Permissions { get; protected set; }
+ public virtual ICollection<Permission> Permissions { get; private set; }
/// <summary>
- /// Gets or sets a collection containing the group's preferences.
+ /// Gets a collection containing the group's preferences.
/// </summary>
- public virtual ICollection<Preference> Preferences { get; protected set; }
+ public virtual ICollection<Preference> Preferences { get; private set; }
/// <inheritdoc/>
public bool HasPermission(PermissionKind kind)
diff --git a/Jellyfin.Data/Entities/HomeSection.cs b/Jellyfin.Data/Entities/HomeSection.cs
index 5adc52491..e194aa537 100644
--- a/Jellyfin.Data/Entities/HomeSection.cs
+++ b/Jellyfin.Data/Entities/HomeSection.cs
@@ -1,5 +1,4 @@
-using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
+using System.ComponentModel.DataAnnotations.Schema;
using Jellyfin.Data.Enums;
namespace Jellyfin.Data.Entities
@@ -10,13 +9,13 @@ namespace Jellyfin.Data.Entities
public class HomeSection
{
/// <summary>
- /// Gets or sets the id.
+ /// Gets the id.
/// </summary>
/// <remarks>
/// Identity. Required.
/// </remarks>
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id { get; protected set; }
+ public int Id { get; private set; }
/// <summary>
/// Gets or sets the Id of the associated display preferences.
diff --git a/Jellyfin.Data/Entities/ImageInfo.cs b/Jellyfin.Data/Entities/ImageInfo.cs
index e0c37047d..b5c7a1c12 100644
--- a/Jellyfin.Data/Entities/ImageInfo.cs
+++ b/Jellyfin.Data/Entities/ImageInfo.cs
@@ -20,18 +20,18 @@ namespace Jellyfin.Data.Entities
}
/// <summary>
- /// Gets or sets the id.
+ /// Gets the id.
/// </summary>
/// <remarks>
/// Identity, Indexed, Required.
/// </remarks>
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id { get; protected set; }
+ public int Id { get; private set; }
/// <summary>
- /// Gets or sets the user id.
+ /// Gets the user id.
/// </summary>
- public Guid? UserId { get; protected set; }
+ public Guid? UserId { get; private set; }
/// <summary>
/// Gets or sets the path of the image.
diff --git a/Jellyfin.Data/Entities/ItemDisplayPreferences.cs b/Jellyfin.Data/Entities/ItemDisplayPreferences.cs
index 4bfeb2fa3..948126d0a 100644
--- a/Jellyfin.Data/Entities/ItemDisplayPreferences.cs
+++ b/Jellyfin.Data/Entities/ItemDisplayPreferences.cs
@@ -29,13 +29,13 @@ namespace Jellyfin.Data.Entities
}
/// <summary>
- /// Gets or sets the Id.
+ /// Gets the id.
/// </summary>
/// <remarks>
/// Required.
/// </remarks>
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id { get; protected set; }
+ public int Id { get; private set; }
/// <summary>
/// Gets or sets the user Id.
diff --git a/Jellyfin.Data/Entities/Libraries/Artwork.cs b/Jellyfin.Data/Entities/Libraries/Artwork.cs
index 84a524de2..923525fc5 100644
--- a/Jellyfin.Data/Entities/Libraries/Artwork.cs
+++ b/Jellyfin.Data/Entities/Libraries/Artwork.cs
@@ -1,5 +1,3 @@
-#pragma warning disable CA2227
-
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
@@ -30,13 +28,13 @@ namespace Jellyfin.Data.Entities.Libraries
}
/// <summary>
- /// Gets or sets the id.
+ /// Gets the id.
/// </summary>
/// <remarks>
/// Identity, Indexed, Required.
/// </remarks>
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id { get; protected set; }
+ public int Id { get; private set; }
/// <summary>
/// Gets or sets the path.
@@ -58,7 +56,7 @@ namespace Jellyfin.Data.Entities.Libraries
/// <inheritdoc />
[ConcurrencyCheck]
- public uint RowVersion { get; set; }
+ public uint RowVersion { get; private set; }
/// <inheritdoc />
public void OnSavingChanges()
diff --git a/Jellyfin.Data/Entities/Libraries/Book.cs b/Jellyfin.Data/Entities/Libraries/Book.cs
index aea3d58d5..a838686d0 100644
--- a/Jellyfin.Data/Entities/Libraries/Book.cs
+++ b/Jellyfin.Data/Entities/Libraries/Book.cs
@@ -1,5 +1,3 @@
-#pragma warning disable CA2227
-
using System.Collections.Generic;
using Jellyfin.Data.Interfaces;
@@ -21,11 +19,11 @@ namespace Jellyfin.Data.Entities.Libraries
}
/// <summary>
- /// Gets or sets a collection containing the metadata for this book.
+ /// Gets a collection containing the metadata for this book.
/// </summary>
- public virtual ICollection<BookMetadata> BookMetadata { get; protected set; }
+ public virtual ICollection<BookMetadata> BookMetadata { get; private set; }
/// <inheritdoc />
- public virtual ICollection<Release> Releases { get; protected set; }
+ public virtual ICollection<Release> Releases { get; private set; }
}
}
diff --git a/Jellyfin.Data/Entities/Libraries/BookMetadata.cs b/Jellyfin.Data/Entities/Libraries/BookMetadata.cs
index 1ff4327b0..4a350d200 100644
--- a/Jellyfin.Data/Entities/Libraries/BookMetadata.cs
+++ b/Jellyfin.Data/Entities/Libraries/BookMetadata.cs
@@ -1,5 +1,3 @@
-#pragma warning disable CA2227
-
using System.Collections.Generic;
using Jellyfin.Data.Interfaces;
@@ -26,9 +24,9 @@ namespace Jellyfin.Data.Entities.Libraries
public long? Isbn { get; set; }
/// <summary>
- /// Gets or sets a collection of the publishers for this book.
+ /// Gets a collection of the publishers for this book.
/// </summary>
- public virtual ICollection<Company> Publishers { get; protected set; }
+ public virtual ICollection<Company> Publishers { get; private set; }
/// <inheritdoc />
public ICollection<Company> Companies => Publishers;
diff --git a/Jellyfin.Data/Entities/Libraries/Chapter.cs b/Jellyfin.Data/Entities/Libraries/Chapter.cs
index 11f53ae20..3d81f713d 100644
--- a/Jellyfin.Data/Entities/Libraries/Chapter.cs
+++ b/Jellyfin.Data/Entities/Libraries/Chapter.cs
@@ -1,5 +1,3 @@
-#pragma warning disable CA2227
-
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
@@ -29,13 +27,13 @@ namespace Jellyfin.Data.Entities.Libraries
}
/// <summary>
- /// Gets or sets the id.
+ /// Gets the id.
/// </summary>
/// <remarks>
/// Identity, Indexed, Required.
/// </remarks>
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id { get; protected set; }
+ public int Id { get; private set; }
/// <summary>
/// Gets or sets the name.
@@ -74,7 +72,7 @@ namespace Jellyfin.Data.Entities.Libraries
/// <inheritdoc />
[ConcurrencyCheck]
- public uint RowVersion { get; protected set; }
+ public uint RowVersion { get; private set; }
/// <inheritdoc />
public void OnSavingChanges()
diff --git a/Jellyfin.Data/Entities/Libraries/Collection.cs b/Jellyfin.Data/Entities/Libraries/Collection.cs
index 4253b7ecc..7de601969 100644
--- a/Jellyfin.Data/Entities/Libraries/Collection.cs
+++ b/Jellyfin.Data/Entities/Libraries/Collection.cs
@@ -1,5 +1,4 @@
#pragma warning disable CA1711 // Identifiers should not have incorrect suffix
-#pragma warning disable CA2227
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
@@ -22,13 +21,13 @@ namespace Jellyfin.Data.Entities.Libraries
}
/// <summary>
- /// Gets or sets the id.
+ /// Gets the id.
/// </summary>
/// <remarks>
/// Identity, Indexed, Required.
/// </remarks>
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id { get; protected set; }
+ public int Id { get; private set; }
/// <summary>
/// Gets or sets the name.
@@ -42,12 +41,12 @@ namespace Jellyfin.Data.Entities.Libraries
/// <inheritdoc />
[ConcurrencyCheck]
- public uint RowVersion { get; set; }
+ public uint RowVersion { get; private set; }
/// <summary>
- /// Gets or sets a collection containing this collection's items.
+ /// Gets a collection containing this collection's items.
/// </summary>
- public virtual ICollection<CollectionItem> Items { get; protected set; }
+ public virtual ICollection<CollectionItem> Items { get; private set; }
/// <inheritdoc />
public void OnSavingChanges()
diff --git a/Jellyfin.Data/Entities/Libraries/CollectionItem.cs b/Jellyfin.Data/Entities/Libraries/CollectionItem.cs
index e19362bdf..0cb4716db 100644
--- a/Jellyfin.Data/Entities/Libraries/CollectionItem.cs
+++ b/Jellyfin.Data/Entities/Libraries/CollectionItem.cs
@@ -29,7 +29,7 @@ namespace Jellyfin.Data.Entities.Libraries
/// <inheritdoc />
[ConcurrencyCheck]
- public uint RowVersion { get; set; }
+ public uint RowVersion { get; private set; }
/// <summary>
/// Gets or sets the library item.
diff --git a/Jellyfin.Data/Entities/Libraries/Company.cs b/Jellyfin.Data/Entities/Libraries/Company.cs
index 09050bb52..1abbee445 100644
--- a/Jellyfin.Data/Entities/Libraries/Company.cs
+++ b/Jellyfin.Data/Entities/Libraries/Company.cs
@@ -1,5 +1,3 @@
-#pragma warning disable CA2227
-
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
@@ -22,27 +20,27 @@ namespace Jellyfin.Data.Entities.Libraries
}
/// <summary>
- /// Gets or sets the id.
+ /// Gets the id.
/// </summary>
/// <remarks>
/// Identity, Indexed, Required.
/// </remarks>
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id { get; protected set; }
+ public int Id { get; private set; }
/// <inheritdoc />
[ConcurrencyCheck]
- public uint RowVersion { get; set; }
+ public uint RowVersion { get; private set; }
/// <summary>
- /// Gets or sets a collection containing the metadata.
+ /// Gets a collection containing the metadata.
/// </summary>
- public virtual ICollection<CompanyMetadata> CompanyMetadata { get; protected set; }
+ public virtual ICollection<CompanyMetadata> CompanyMetadata { get; private set; }
/// <summary>
- /// Gets or sets a collection containing this company's child companies.
+ /// Gets a collection containing this company's child companies.
/// </summary>
- public virtual ICollection<Company> ChildCompanies { get; protected set; }
+ public virtual ICollection<Company> ChildCompanies { get; private set; }
/// <inheritdoc />
public ICollection<Company> Companies => ChildCompanies;
diff --git a/Jellyfin.Data/Entities/Libraries/CustomItem.cs b/Jellyfin.Data/Entities/Libraries/CustomItem.cs
index 88d1a0c25..e27d01d86 100644
--- a/Jellyfin.Data/Entities/Libraries/CustomItem.cs
+++ b/Jellyfin.Data/Entities/Libraries/CustomItem.cs
@@ -1,5 +1,3 @@
-#pragma warning disable CA2227
-
using System.Collections.Generic;
using Jellyfin.Data.Interfaces;
@@ -21,11 +19,11 @@ namespace Jellyfin.Data.Entities.Libraries
}
/// <summary>
- /// Gets or sets a collection containing the metadata for this item.
+ /// Gets a collection containing the metadata for this item.
/// </summary>
- public virtual ICollection<CustomItemMetadata> CustomItemMetadata { get; protected set; }
+ public virtual ICollection<CustomItemMetadata> CustomItemMetadata { get; private set; }
/// <inheritdoc />
- public virtual ICollection<Release> Releases { get; protected set; }
+ public virtual ICollection<Release> Releases { get; private set; }
}
}
diff --git a/Jellyfin.Data/Entities/Libraries/Episode.cs b/Jellyfin.Data/Entities/Libraries/Episode.cs
index 458c7d9f5..ce2f0c617 100644
--- a/Jellyfin.Data/Entities/Libraries/Episode.cs
+++ b/Jellyfin.Data/Entities/Libraries/Episode.cs
@@ -1,5 +1,3 @@
-#pragma warning disable CA2227
-
using System.Collections.Generic;
using Jellyfin.Data.Interfaces;
@@ -26,11 +24,11 @@ namespace Jellyfin.Data.Entities.Libraries
public int? EpisodeNumber { get; set; }
/// <inheritdoc />
- public virtual ICollection<Release> Releases { get; protected set; }
+ public virtual ICollection<Release> Releases { get; private set; }
/// <summary>
- /// Gets or sets a collection containing the metadata for this episode.
+ /// Gets a collection containing the metadata for this episode.
/// </summary>
- public virtual ICollection<EpisodeMetadata> EpisodeMetadata { get; protected set; }
+ public virtual ICollection<EpisodeMetadata> EpisodeMetadata { get; private set; }
}
}
diff --git a/Jellyfin.Data/Entities/Libraries/Genre.cs b/Jellyfin.Data/Entities/Libraries/Genre.cs
index 9f3d65028..3b822ee82 100644
--- a/Jellyfin.Data/Entities/Libraries/Genre.cs
+++ b/Jellyfin.Data/Entities/Libraries/Genre.cs
@@ -19,13 +19,13 @@ namespace Jellyfin.Data.Entities.Libraries
}
/// <summary>
- /// Gets or sets the id.
+ /// Gets the id.
/// </summary>
/// <remarks>
/// Identity, Indexed, Required.
/// </remarks>
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id { get; protected set; }
+ public int Id { get; private set; }
/// <summary>
/// Gets or sets the name.
@@ -39,7 +39,7 @@ namespace Jellyfin.Data.Entities.Libraries
/// <inheritdoc />
[ConcurrencyCheck]
- public uint RowVersion { get; protected set; }
+ public uint RowVersion { get; private set; }
/// <inheritdoc />
public void OnSavingChanges()
diff --git a/Jellyfin.Data/Entities/Libraries/ItemMetadata.cs b/Jellyfin.Data/Entities/Libraries/ItemMetadata.cs
index d12e011a8..d429a90c6 100644
--- a/Jellyfin.Data/Entities/Libraries/ItemMetadata.cs
+++ b/Jellyfin.Data/Entities/Libraries/ItemMetadata.cs
@@ -1,5 +1,3 @@
-#pragma warning disable CA2227
-
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
@@ -43,13 +41,13 @@ namespace Jellyfin.Data.Entities.Libraries
}
/// <summary>
- /// Gets or sets the id.
+ /// Gets the id.
/// </summary>
/// <remarks>
/// Identity, Indexed, Required.
/// </remarks>
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id { get; protected set; }
+ public int Id { get; private set; }
/// <summary>
/// Gets or sets the title.
@@ -99,12 +97,12 @@ namespace Jellyfin.Data.Entities.Libraries
public DateTimeOffset? ReleaseDate { get; set; }
/// <summary>
- /// Gets or sets the date added.
+ /// Gets the date added.
/// </summary>
/// <remarks>
/// Required.
/// </remarks>
- public DateTime DateAdded { get; protected set; }
+ public DateTime DateAdded { get; private set; }
/// <summary>
/// Gets or sets the date modified.
@@ -114,37 +112,32 @@ namespace Jellyfin.Data.Entities.Libraries
/// </remarks>
public DateTime DateModified { get; set; }
- /// <summary>
- /// Gets or sets the row version.
- /// </summary>
- /// <remarks>
- /// Required, ConcurrencyToken.
- /// </remarks>
+ /// <inheritdoc />
[ConcurrencyCheck]
- public uint RowVersion { get; set; }
+ public uint RowVersion { get; private set; }
/// <summary>
- /// Gets or sets a collection containing the person roles for this item.
+ /// Gets a collection containing the person roles for this item.
/// </summary>
- public virtual ICollection<PersonRole> PersonRoles { get; protected set; }
+ public virtual ICollection<PersonRole> PersonRoles { get; private set; }
/// <summary>
- /// Gets or sets a collection containing the genres for this item.
+ /// Gets a collection containing the genres for this item.
/// </summary>
- public virtual ICollection<Genre> Genres { get; protected set; }
+ public virtual ICollection<Genre> Genres { get; private set; }
/// <inheritdoc />
- public virtual ICollection<Artwork> Artwork { get; protected set; }
+ public virtual ICollection<Artwork> Artwork { get; private set; }
/// <summary>
- /// Gets or sets a collection containing the ratings for this item.
+ /// Gets a collection containing the ratings for this item.
/// </summary>
- public virtual ICollection<Rating> Ratings { get; protected set; }
+ public virtual ICollection<Rating> Ratings { get; private set; }
/// <summary>
- /// Gets or sets a collection containing the metadata sources for this item.
+ /// Gets a collection containing the metadata sources for this item.
/// </summary>
- public virtual ICollection<MetadataProviderId> Sources { get; protected set; }
+ public virtual ICollection<MetadataProviderId> Sources { get; private set; }
/// <inheritdoc />
public void OnSavingChanges()
diff --git a/Jellyfin.Data/Entities/Libraries/Library.cs b/Jellyfin.Data/Entities/Libraries/Library.cs
index e45384902..0db42a1c7 100644
--- a/Jellyfin.Data/Entities/Libraries/Library.cs
+++ b/Jellyfin.Data/Entities/Libraries/Library.cs
@@ -21,13 +21,13 @@ namespace Jellyfin.Data.Entities.Libraries
}
/// <summary>
- /// Gets or sets the id.
+ /// Gets the id.
/// </summary>
/// <remarks>
/// Identity, Indexed, Required.
/// </remarks>
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id { get; protected set; }
+ public int Id { get; private set; }
/// <summary>
/// Gets or sets the name.
@@ -49,7 +49,7 @@ namespace Jellyfin.Data.Entities.Libraries
/// <inheritdoc />
[ConcurrencyCheck]
- public uint RowVersion { get; set; }
+ public uint RowVersion { get; private set; }
/// <inheritdoc />
public void OnSavingChanges()
diff --git a/Jellyfin.Data/Entities/Libraries/LibraryItem.cs b/Jellyfin.Data/Entities/Libraries/LibraryItem.cs
index 67ffad944..d889b871e 100644
--- a/Jellyfin.Data/Entities/Libraries/LibraryItem.cs
+++ b/Jellyfin.Data/Entities/Libraries/LibraryItem.cs
@@ -21,22 +21,22 @@ namespace Jellyfin.Data.Entities.Libraries
}
/// <summary>
- /// Gets or sets the id.
+ /// Gets the id.
/// </summary>
/// <remarks>
/// Identity, Indexed, Required.
/// </remarks>
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id { get; protected set; }
+ public int Id { get; private set; }
/// <summary>
- /// Gets or sets the date this library item was added.
+ /// Gets the date this library item was added.
/// </summary>
- public DateTime DateAdded { get; protected set; }
+ public DateTime DateAdded { get; private set; }
/// <inheritdoc />
[ConcurrencyCheck]
- public uint RowVersion { get; protected set; }
+ public uint RowVersion { get; private set; }
/// <summary>
/// Gets or sets the library of this item.
diff --git a/Jellyfin.Data/Entities/Libraries/MediaFile.cs b/Jellyfin.Data/Entities/Libraries/MediaFile.cs
index f3e2fe653..36e1e4777 100644
--- a/Jellyfin.Data/Entities/Libraries/MediaFile.cs
+++ b/Jellyfin.Data/Entities/Libraries/MediaFile.cs
@@ -1,5 +1,3 @@
-#pragma warning disable CA2227
-
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
@@ -33,13 +31,13 @@ namespace Jellyfin.Data.Entities.Libraries
}
/// <summary>
- /// Gets or sets the id.
+ /// Gets the id.
/// </summary>
/// <remarks>
/// Identity, Indexed, Required.
/// </remarks>
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id { get; protected set; }
+ public int Id { get; private set; }
/// <summary>
/// Gets or sets the path relative to the library root.
@@ -61,12 +59,12 @@ namespace Jellyfin.Data.Entities.Libraries
/// <inheritdoc />
[ConcurrencyCheck]
- public uint RowVersion { get; set; }
+ public uint RowVersion { get; private set; }
/// <summary>
- /// Gets or sets a collection containing the streams in this file.
+ /// Gets a collection containing the streams in this file.
/// </summary>
- public virtual ICollection<MediaFileStream> MediaFileStreams { get; protected set; }
+ public virtual ICollection<MediaFileStream> MediaFileStreams { get; private set; }
/// <inheritdoc />
public void OnSavingChanges()
diff --git a/Jellyfin.Data/Entities/Libraries/MediaFileStream.cs b/Jellyfin.Data/Entities/Libraries/MediaFileStream.cs
index ba21294fc..e24e73ecb 100644
--- a/Jellyfin.Data/Entities/Libraries/MediaFileStream.cs
+++ b/Jellyfin.Data/Entities/Libraries/MediaFileStream.cs
@@ -21,13 +21,13 @@ namespace Jellyfin.Data.Entities.Libraries
}
/// <summary>
- /// Gets or sets the id.
+ /// Gets the id.
/// </summary>
/// <remarks>
/// Identity, Indexed, Required.
/// </remarks>
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id { get; protected set; }
+ public int Id { get; private set; }
/// <summary>
/// Gets or sets the stream number.
@@ -39,7 +39,7 @@ namespace Jellyfin.Data.Entities.Libraries
/// <inheritdoc />
[ConcurrencyCheck]
- public uint RowVersion { get; set; }
+ public uint RowVersion { get; private set; }
/// <inheritdoc />
public void OnSavingChanges()
diff --git a/Jellyfin.Data/Entities/Libraries/MetadataProvider.cs b/Jellyfin.Data/Entities/Libraries/MetadataProvider.cs
index fb2587882..b27196078 100644
--- a/Jellyfin.Data/Entities/Libraries/MetadataProvider.cs
+++ b/Jellyfin.Data/Entities/Libraries/MetadataProvider.cs
@@ -25,13 +25,13 @@ namespace Jellyfin.Data.Entities.Libraries
}
/// <summary>
- /// Gets or sets the id.
+ /// Gets the id.
/// </summary>
/// <remarks>
/// Identity, Indexed, Required.
/// </remarks>
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id { get; protected set; }
+ public int Id { get; private set; }
/// <summary>
/// Gets or sets the name.
@@ -45,7 +45,7 @@ namespace Jellyfin.Data.Entities.Libraries
/// <inheritdoc />
[ConcurrencyCheck]
- public uint RowVersion { get; set; }
+ public uint RowVersion { get; private set; }
/// <inheritdoc />
public void OnSavingChanges()
diff --git a/Jellyfin.Data/Entities/Libraries/MetadataProviderId.cs b/Jellyfin.Data/Entities/Libraries/MetadataProviderId.cs
index 2a9c904c8..44c198518 100644
--- a/Jellyfin.Data/Entities/Libraries/MetadataProviderId.cs
+++ b/Jellyfin.Data/Entities/Libraries/MetadataProviderId.cs
@@ -27,13 +27,13 @@ namespace Jellyfin.Data.Entities.Libraries
}
/// <summary>
- /// Gets or sets the id.
+ /// Gets the id.
/// </summary>
/// <remarks>
/// Identity, Indexed, Required.
/// </remarks>
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id { get; protected set; }
+ public int Id { get; private set; }
/// <summary>
/// Gets or sets the provider id.
@@ -47,7 +47,7 @@ namespace Jellyfin.Data.Entities.Libraries
/// <inheritdoc />
[ConcurrencyCheck]
- public uint RowVersion { get; set; }
+ public uint RowVersion { get; private set; }
/// <summary>
/// Gets or sets the metadata provider.
diff --git a/Jellyfin.Data/Entities/Libraries/Movie.cs b/Jellyfin.Data/Entities/Libraries/Movie.cs
index f89cacff4..499fafd0e 100644
--- a/Jellyfin.Data/Entities/Libraries/Movie.cs
+++ b/Jellyfin.Data/Entities/Libraries/Movie.cs
@@ -1,5 +1,3 @@
-#pragma warning disable CA2227
-
using System.Collections.Generic;
using Jellyfin.Data.Interfaces;
@@ -21,11 +19,11 @@ namespace Jellyfin.Data.Entities.Libraries
}
/// <inheritdoc />
- public virtual ICollection<Release> Releases { get; protected set; }
+ public virtual ICollection<Release> Releases { get; private set; }
/// <summary>
- /// Gets or sets a collection containing the metadata for this movie.
+ /// Gets a collection containing the metadata for this movie.
/// </summary>
- public virtual ICollection<MovieMetadata> MovieMetadata { get; protected set; }
+ public virtual ICollection<MovieMetadata> MovieMetadata { get; private set; }
}
}
diff --git a/Jellyfin.Data/Entities/Libraries/MovieMetadata.cs b/Jellyfin.Data/Entities/Libraries/MovieMetadata.cs
index fb181dea6..44b5f34d7 100644
--- a/Jellyfin.Data/Entities/Libraries/MovieMetadata.cs
+++ b/Jellyfin.Data/Entities/Libraries/MovieMetadata.cs
@@ -1,5 +1,3 @@
-#pragma warning disable CA2227
-
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Jellyfin.Data.Interfaces;
@@ -62,9 +60,9 @@ namespace Jellyfin.Data.Entities.Libraries
public string? Country { get; set; }
/// <summary>
- /// Gets or sets the studios that produced this movie.
+ /// Gets the studios that produced this movie.
/// </summary>
- public virtual ICollection<Company> Studios { get; protected set; }
+ public virtual ICollection<Company> Studios { get; private set; }
/// <inheritdoc />
public ICollection<Company> Companies => Studios;
diff --git a/Jellyfin.Data/Entities/Libraries/MusicAlbum.cs b/Jellyfin.Data/Entities/Libraries/MusicAlbum.cs
index 4049cdac8..d6231bbf0 100644
--- a/Jellyfin.Data/Entities/Libraries/MusicAlbum.cs
+++ b/Jellyfin.Data/Entities/Libraries/MusicAlbum.cs
@@ -1,5 +1,3 @@
-#pragma warning disable CA2227
-
using System.Collections.Generic;
namespace Jellyfin.Data.Entities.Libraries
@@ -20,13 +18,13 @@ namespace Jellyfin.Data.Entities.Libraries
}
/// <summary>
- /// Gets or sets a collection containing the album metadata.
+ /// Gets a collection containing the album metadata.
/// </summary>
- public virtual ICollection<MusicAlbumMetadata> MusicAlbumMetadata { get; protected set; }
+ public virtual ICollection<MusicAlbumMetadata> MusicAlbumMetadata { get; private set; }
/// <summary>
- /// Gets or sets a collection containing the tracks.
+ /// Gets a collection containing the tracks.
/// </summary>
- public virtual ICollection<Track> Tracks { get; protected set; }
+ public virtual ICollection<Track> Tracks { get; private set; }
}
}
diff --git a/Jellyfin.Data/Entities/Libraries/MusicAlbumMetadata.cs b/Jellyfin.Data/Entities/Libraries/MusicAlbumMetadata.cs
index 3080bd692..691f3504f 100644
--- a/Jellyfin.Data/Entities/Libraries/MusicAlbumMetadata.cs
+++ b/Jellyfin.Data/Entities/Libraries/MusicAlbumMetadata.cs
@@ -1,5 +1,3 @@
-#pragma warning disable CA2227
-
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
@@ -51,8 +49,8 @@ namespace Jellyfin.Data.Entities.Libraries
public string? Country { get; set; }
/// <summary>
- /// Gets or sets a collection containing the labels.
+ /// Gets a collection containing the labels.
/// </summary>
- public virtual ICollection<Company> Labels { get; protected set; }
+ public virtual ICollection<Company> Labels { get; private set; }
}
}
diff --git a/Jellyfin.Data/Entities/Libraries/Person.cs b/Jellyfin.Data/Entities/Libraries/Person.cs
index 159bd47be..8b67d920d 100644
--- a/Jellyfin.Data/Entities/Libraries/Person.cs
+++ b/Jellyfin.Data/Entities/Libraries/Person.cs
@@ -1,5 +1,3 @@
-#pragma warning disable CA2227
-
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
@@ -32,13 +30,13 @@ namespace Jellyfin.Data.Entities.Libraries
}
/// <summary>
- /// Gets or sets the id.
+ /// Gets the id.
/// </summary>
/// <remarks>
/// Identity, Indexed, Required.
/// </remarks>
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id { get; protected set; }
+ public int Id { get; private set; }
/// <summary>
/// Gets or sets the name.
@@ -61,12 +59,12 @@ namespace Jellyfin.Data.Entities.Libraries
public string? SourceId { get; set; }
/// <summary>
- /// Gets or sets the date added.
+ /// Gets the date added.
/// </summary>
/// <remarks>
/// Required.
/// </remarks>
- public DateTime DateAdded { get; protected set; }
+ public DateTime DateAdded { get; private set; }
/// <summary>
/// Gets or sets the date modified.
@@ -78,12 +76,12 @@ namespace Jellyfin.Data.Entities.Libraries
/// <inheritdoc />
[ConcurrencyCheck]
- public uint RowVersion { get; set; }
+ public uint RowVersion { get; private set; }
/// <summary>
- /// Gets or sets a list of metadata sources for this person.
+ /// Gets a list of metadata sources for this person.
/// </summary>
- public virtual ICollection<MetadataProviderId> Sources { get; protected set; }
+ public virtual ICollection<MetadataProviderId> Sources { get; private set; }
/// <inheritdoc />
public void OnSavingChanges()
diff --git a/Jellyfin.Data/Entities/Libraries/PersonRole.cs b/Jellyfin.Data/Entities/Libraries/PersonRole.cs
index 988aa84ba..7d40bdf44 100644
--- a/Jellyfin.Data/Entities/Libraries/PersonRole.cs
+++ b/Jellyfin.Data/Entities/Libraries/PersonRole.cs
@@ -1,5 +1,3 @@
-#pragma warning disable CA2227
-
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
@@ -27,13 +25,13 @@ namespace Jellyfin.Data.Entities.Libraries
}
/// <summary>
- /// Gets or sets the id.
+ /// Gets the id.
/// </summary>
/// <remarks>
/// Identity, Indexed, Required.
/// </remarks>
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id { get; protected set; }
+ public int Id { get; private set; }
/// <summary>
/// Gets or sets the name of the person's role.
@@ -55,7 +53,7 @@ namespace Jellyfin.Data.Entities.Libraries
/// <inheritdoc />
[ConcurrencyCheck]
- public uint RowVersion { get; protected set; }
+ public uint RowVersion { get; private set; }
/// <summary>
/// Gets or sets the person.
@@ -66,12 +64,12 @@ namespace Jellyfin.Data.Entities.Libraries
public virtual Person Person { get; set; }
/// <inheritdoc />
- public virtual ICollection<Artwork> Artwork { get; protected set; }
+ public virtual ICollection<Artwork> Artwork { get; private set; }
/// <summary>
- /// Gets or sets a collection containing the metadata sources for this person role.
+ /// Gets a collection containing the metadata sources for this person role.
/// </summary>
- public virtual ICollection<MetadataProviderId> Sources { get; protected set; }
+ public virtual ICollection<MetadataProviderId> Sources { get; private set; }
/// <inheritdoc />
public void OnSavingChanges()
diff --git a/Jellyfin.Data/Entities/Libraries/Photo.cs b/Jellyfin.Data/Entities/Libraries/Photo.cs
index eb5c96267..4b459432b 100644
--- a/Jellyfin.Data/Entities/Libraries/Photo.cs
+++ b/Jellyfin.Data/Entities/Libraries/Photo.cs
@@ -1,5 +1,3 @@
-#pragma warning disable CA2227
-
using System.Collections.Generic;
using Jellyfin.Data.Interfaces;
@@ -21,11 +19,11 @@ namespace Jellyfin.Data.Entities.Libraries
}
/// <summary>
- /// Gets or sets a collection containing the photo metadata.
+ /// Gets a collection containing the photo metadata.
/// </summary>
- public virtual ICollection<PhotoMetadata> PhotoMetadata { get; protected set; }
+ public virtual ICollection<PhotoMetadata> PhotoMetadata { get; private set; }
/// <inheritdoc />
- public virtual ICollection<Release> Releases { get; protected set; }
+ public virtual ICollection<Release> Releases { get; private set; }
}
}
diff --git a/Jellyfin.Data/Entities/Libraries/Rating.cs b/Jellyfin.Data/Entities/Libraries/Rating.cs
index 6862012a8..58c8fa49e 100644
--- a/Jellyfin.Data/Entities/Libraries/Rating.cs
+++ b/Jellyfin.Data/Entities/Libraries/Rating.cs
@@ -19,13 +19,13 @@ namespace Jellyfin.Data.Entities.Libraries
}
/// <summary>
- /// Gets or sets the id.
+ /// Gets the id.
/// </summary>
/// <remarks>
/// Identity, Indexed, Required.
/// </remarks>
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id { get; protected set; }
+ public int Id { get; private set; }
/// <summary>
/// Gets or sets the value.
@@ -42,7 +42,7 @@ namespace Jellyfin.Data.Entities.Libraries
/// <inheritdoc />
[ConcurrencyCheck]
- public uint RowVersion { get; set; }
+ public uint RowVersion { get; private set; }
/// <summary>
/// Gets or sets the rating type.
diff --git a/Jellyfin.Data/Entities/Libraries/RatingSource.cs b/Jellyfin.Data/Entities/Libraries/RatingSource.cs
index ae0d806ff..0f3a07324 100644
--- a/Jellyfin.Data/Entities/Libraries/RatingSource.cs
+++ b/Jellyfin.Data/Entities/Libraries/RatingSource.cs
@@ -21,13 +21,13 @@ namespace Jellyfin.Data.Entities.Libraries
}
/// <summary>
- /// Gets or sets the id.
+ /// Gets the id.
/// </summary>
/// <remarks>
/// Identity, Indexed, Required.
/// </remarks>
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id { get; protected set; }
+ public int Id { get; private set; }
/// <summary>
/// Gets or sets the name.
@@ -57,7 +57,7 @@ namespace Jellyfin.Data.Entities.Libraries
/// <inheritdoc />
[ConcurrencyCheck]
- public uint RowVersion { get; set; }
+ public uint RowVersion { get; private set; }
/// <summary>
/// Gets or sets the metadata source.
diff --git a/Jellyfin.Data/Entities/Libraries/Release.cs b/Jellyfin.Data/Entities/Libraries/Release.cs
index 21d403979..d3d52bf5c 100644
--- a/Jellyfin.Data/Entities/Libraries/Release.cs
+++ b/Jellyfin.Data/Entities/Libraries/Release.cs
@@ -1,5 +1,3 @@
-#pragma warning disable CA2227
-
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
@@ -31,13 +29,13 @@ namespace Jellyfin.Data.Entities.Libraries
}
/// <summary>
- /// Gets or sets the id.
+ /// Gets the id.
/// </summary>
/// <remarks>
/// Identity, Indexed, Required.
/// </remarks>
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id { get; protected set; }
+ public int Id { get; private set; }
/// <summary>
/// Gets or sets the name.
@@ -51,17 +49,17 @@ namespace Jellyfin.Data.Entities.Libraries
/// <inheritdoc />
[ConcurrencyCheck]
- public uint RowVersion { get; set; }
+ public uint RowVersion { get; private set; }
/// <summary>
- /// Gets or sets a collection containing the media files for this release.
+ /// Gets a collection containing the media files for this release.
/// </summary>
- public virtual ICollection<MediaFile> MediaFiles { get; protected set; }
+ public virtual ICollection<MediaFile> MediaFiles { get; private set; }
/// <summary>
- /// Gets or sets a collection containing the chapters for this release.
+ /// Gets a collection containing the chapters for this release.
/// </summary>
- public virtual ICollection<Chapter> Chapters { get; protected set; }
+ public virtual ICollection<Chapter> Chapters { get; private set; }
/// <inheritdoc />
public void OnSavingChanges()
diff --git a/Jellyfin.Data/Entities/Libraries/Season.cs b/Jellyfin.Data/Entities/Libraries/Season.cs
index 04f723a1d..fc110b49d 100644
--- a/Jellyfin.Data/Entities/Libraries/Season.cs
+++ b/Jellyfin.Data/Entities/Libraries/Season.cs
@@ -1,5 +1,3 @@
-#pragma warning disable CA2227
-
using System.Collections.Generic;
namespace Jellyfin.Data.Entities.Libraries
@@ -25,13 +23,13 @@ namespace Jellyfin.Data.Entities.Libraries
public int? SeasonNumber { get; set; }
/// <summary>
- /// Gets or sets the season metadata.
+ /// Gets the season metadata.
/// </summary>
- public virtual ICollection<SeasonMetadata> SeasonMetadata { get; protected set; }
+ public virtual ICollection<SeasonMetadata> SeasonMetadata { get; private set; }
/// <summary>
- /// Gets or sets a collection containing the number of episodes.
+ /// Gets a collection containing the number of episodes.
/// </summary>
- public virtual ICollection<Episode> Episodes { get; protected set; }
+ public virtual ICollection<Episode> Episodes { get; private set; }
}
}
diff --git a/Jellyfin.Data/Entities/Libraries/Series.cs b/Jellyfin.Data/Entities/Libraries/Series.cs
index 59508831e..0354433e0 100644
--- a/Jellyfin.Data/Entities/Libraries/Series.cs
+++ b/Jellyfin.Data/Entities/Libraries/Series.cs
@@ -1,5 +1,3 @@
-#pragma warning disable CA2227
-
using System;
using System.Collections.Generic;
@@ -16,7 +14,6 @@ namespace Jellyfin.Data.Entities.Libraries
/// <param name="library">The library.</param>
public Series(Library library) : base(library)
{
- DateAdded = DateTime.UtcNow;
Seasons = new HashSet<Season>();
SeriesMetadata = new HashSet<SeriesMetadata>();
}
@@ -37,13 +34,13 @@ namespace Jellyfin.Data.Entities.Libraries
public DateTime? FirstAired { get; set; }
/// <summary>
- /// Gets or sets a collection containing the series metadata.
+ /// Gets a collection containing the series metadata.
/// </summary>
- public virtual ICollection<SeriesMetadata> SeriesMetadata { get; protected set; }
+ public virtual ICollection<SeriesMetadata> SeriesMetadata { get; private set; }
/// <summary>
- /// Gets or sets a collection containing the seasons.
+ /// Gets a collection containing the seasons.
/// </summary>
- public virtual ICollection<Season> Seasons { get; protected set; }
+ public virtual ICollection<Season> Seasons { get; private set; }
}
}
diff --git a/Jellyfin.Data/Entities/Libraries/SeriesMetadata.cs b/Jellyfin.Data/Entities/Libraries/SeriesMetadata.cs
index 730deccae..42115802c 100644
--- a/Jellyfin.Data/Entities/Libraries/SeriesMetadata.cs
+++ b/Jellyfin.Data/Entities/Libraries/SeriesMetadata.cs
@@ -1,8 +1,5 @@
-#pragma warning disable CA2227
-
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
using Jellyfin.Data.Interfaces;
namespace Jellyfin.Data.Entities.Libraries
@@ -63,9 +60,9 @@ namespace Jellyfin.Data.Entities.Libraries
public string? Country { get; set; }
/// <summary>
- /// Gets or sets a collection containing the networks.
+ /// Gets a collection containing the networks.
/// </summary>
- public virtual ICollection<Company> Networks { get; protected set; }
+ public virtual ICollection<Company> Networks { get; private set; }
/// <inheritdoc />
public ICollection<Company> Companies => Networks;
diff --git a/Jellyfin.Data/Entities/Libraries/Track.cs b/Jellyfin.Data/Entities/Libraries/Track.cs
index 86a3edff8..d35400033 100644
--- a/Jellyfin.Data/Entities/Libraries/Track.cs
+++ b/Jellyfin.Data/Entities/Libraries/Track.cs
@@ -1,5 +1,3 @@
-#pragma warning disable CA2227
-
using System.Collections.Generic;
using Jellyfin.Data.Interfaces;
@@ -26,11 +24,11 @@ namespace Jellyfin.Data.Entities.Libraries
public int? TrackNumber { get; set; }
/// <inheritdoc />
- public virtual ICollection<Release> Releases { get; protected set; }
+ public virtual ICollection<Release> Releases { get; private set; }
/// <summary>
- /// Gets or sets a collection containing the track metadata.
+ /// Gets a collection containing the track metadata.
/// </summary>
- public virtual ICollection<TrackMetadata> TrackMetadata { get; protected set; }
+ public virtual ICollection<TrackMetadata> TrackMetadata { get; private set; }
}
}
diff --git a/Jellyfin.Data/Entities/Permission.cs b/Jellyfin.Data/Entities/Permission.cs
index f059dedfa..6d2e68077 100644
--- a/Jellyfin.Data/Entities/Permission.cs
+++ b/Jellyfin.Data/Entities/Permission.cs
@@ -1,5 +1,6 @@
#pragma warning disable CA1711 // Identifiers should not have incorrect suffix
+using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Jellyfin.Data.Enums;
@@ -25,21 +26,26 @@ namespace Jellyfin.Data.Entities
}
/// <summary>
- /// Gets or sets the id of this permission.
+ /// Gets the id of this permission.
/// </summary>
/// <remarks>
/// Identity, Indexed, Required.
/// </remarks>
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id { get; protected set; }
+ public int Id { get; private set; }
/// <summary>
- /// Gets or sets the type of this permission.
+ /// Gets or sets the id of the associated user.
+ /// </summary>
+ public Guid? UserId { get; set; }
+
+ /// <summary>
+ /// Gets the type of this permission.
/// </summary>
/// <remarks>
/// Required.
/// </remarks>
- public PermissionKind Kind { get; protected set; }
+ public PermissionKind Kind { get; private set; }
/// <summary>
/// Gets or sets a value indicating whether the associated user has this permission.
@@ -51,7 +57,7 @@ namespace Jellyfin.Data.Entities
/// <inheritdoc />
[ConcurrencyCheck]
- public uint RowVersion { get; set; }
+ public uint RowVersion { get; private set; }
/// <inheritdoc/>
public void OnSavingChanges()
diff --git a/Jellyfin.Data/Entities/Preference.cs b/Jellyfin.Data/Entities/Preference.cs
index a8813ab88..a6ab275d3 100644
--- a/Jellyfin.Data/Entities/Preference.cs
+++ b/Jellyfin.Data/Entities/Preference.cs
@@ -24,21 +24,26 @@ namespace Jellyfin.Data.Entities
}
/// <summary>
- /// Gets or sets the id of this preference.
+ /// Gets the id of this preference.
/// </summary>
/// <remarks>
/// Identity, Indexed, Required.
/// </remarks>
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id { get; protected set; }
+ public int Id { get; private set; }
/// <summary>
- /// Gets or sets the type of this preference.
+ /// Gets or sets the id of the associated user.
+ /// </summary>
+ public Guid? UserId { get; set; }
+
+ /// <summary>
+ /// Gets the type of this preference.
/// </summary>
/// <remarks>
/// Required.
/// </remarks>
- public PreferenceKind Kind { get; protected set; }
+ public PreferenceKind Kind { get; private set; }
/// <summary>
/// Gets or sets the value of this preference.
@@ -52,7 +57,7 @@ namespace Jellyfin.Data.Entities
/// <inheritdoc/>
[ConcurrencyCheck]
- public uint RowVersion { get; set; }
+ public uint RowVersion { get; private set; }
/// <inheritdoc/>
public void OnSavingChanges()
diff --git a/Jellyfin.Data/Entities/User.cs b/Jellyfin.Data/Entities/User.cs
index 74331726c..e309e54de 100644
--- a/Jellyfin.Data/Entities/User.cs
+++ b/Jellyfin.Data/Entities/User.cs
@@ -1,5 +1,3 @@
-#pragma warning disable CA2227
-
using System;
using System.Collections.Generic;
using System.ComponentModel;
@@ -302,64 +300,54 @@ namespace Jellyfin.Data.Entities
public virtual ImageInfo? ProfileImage { get; set; }
/// <summary>
- /// Gets or sets the user's display preferences.
+ /// Gets the user's display preferences.
/// </summary>
- /// <remarks>
- /// Required.
- /// </remarks>
- public virtual ICollection<DisplayPreferences> DisplayPreferences { get; set; }
+ public virtual ICollection<DisplayPreferences> DisplayPreferences { get; private set; }
/// <summary>
/// Gets or sets the level of sync play permissions this user has.
/// </summary>
public SyncPlayUserAccessType SyncPlayAccess { get; set; }
- /// <summary>
- /// Gets or sets the row version.
- /// </summary>
- /// <remarks>
- /// Required, Concurrency Token.
- /// </remarks>
+ /// <inheritdoc />
[ConcurrencyCheck]
- public uint RowVersion { get; set; }
+ public uint RowVersion { get; private set; }
/// <summary>
- /// Gets or sets the list of access schedules this user has.
+ /// Gets the list of access schedules this user has.
/// </summary>
- public virtual ICollection<AccessSchedule> AccessSchedules { get; protected set; }
+ public virtual ICollection<AccessSchedule> AccessSchedules { get; private set; }
/// <summary>
- /// Gets or sets the list of item display preferences.
+ /// Gets the list of item display preferences.
/// </summary>
- public virtual ICollection<ItemDisplayPreferences> ItemDisplayPreferences { get; protected set; }
+ public virtual ICollection<ItemDisplayPreferences> ItemDisplayPreferences { get; private set; }
/*
/// <summary>
- /// Gets or sets the list of groups this user is a member of.
+ /// Gets the list of groups this user is a member of.
/// </summary>
- [ForeignKey("Group_Groups_Guid")]
- public virtual ICollection<Group> Groups { get; protected set; }
+ public virtual ICollection<Group> Groups { get; private set; }
*/
/// <summary>
- /// Gets or sets the list of permissions this user has.
+ /// Gets the list of permissions this user has.
/// </summary>
[ForeignKey("Permission_Permissions_Guid")]
- public virtual ICollection<Permission> Permissions { get; protected set; }
+ public virtual ICollection<Permission> Permissions { get; private set; }
/*
/// <summary>
- /// Gets or sets the list of provider mappings this user has.
+ /// Gets the list of provider mappings this user has.
/// </summary>
- [ForeignKey("ProviderMapping_ProviderMappings_Id")]
- public virtual ICollection<ProviderMapping> ProviderMappings { get; protected set; }
+ public virtual ICollection<ProviderMapping> ProviderMappings { get; private set; }
*/
/// <summary>
- /// Gets or sets the list of preferences this user has.
+ /// Gets the list of preferences this user has.
/// </summary>
[ForeignKey("Preference_Preferences_Guid")]
- public virtual ICollection<Preference> Preferences { get; protected set; }
+ public virtual ICollection<Preference> Preferences { get; private set; }
/// <inheritdoc/>
public void OnSavingChanges()
diff --git a/Jellyfin.Data/Enums/HomeSectionType.cs b/Jellyfin.Data/Enums/HomeSectionType.cs
index e597c9431..9bcd097dc 100644
--- a/Jellyfin.Data/Enums/HomeSectionType.cs
+++ b/Jellyfin.Data/Enums/HomeSectionType.cs
@@ -48,6 +48,11 @@
/// <summary>
/// Live TV.
/// </summary>
- LiveTv = 8
+ LiveTv = 8,
+
+ /// <summary>
+ /// Continue Reading.
+ /// </summary>
+ ResumeBook = 9
}
}
diff --git a/Jellyfin.Networking/Configuration/NetworkConfiguration.cs b/Jellyfin.Networking/Configuration/NetworkConfiguration.cs
index 91bf0015f..faf814c06 100644
--- a/Jellyfin.Networking/Configuration/NetworkConfiguration.cs
+++ b/Jellyfin.Networking/Configuration/NetworkConfiguration.cs
@@ -1,7 +1,6 @@
#pragma warning disable CA1819 // Properties should not return arrays
using System;
-using MediaBrowser.Model.Configuration;
namespace Jellyfin.Networking.Configuration
{
diff --git a/Jellyfin.Networking/Configuration/NetworkConfigurationExtensions.cs b/Jellyfin.Networking/Configuration/NetworkConfigurationExtensions.cs
index e77b17ba9..8cbe398b0 100644
--- a/Jellyfin.Networking/Configuration/NetworkConfigurationExtensions.cs
+++ b/Jellyfin.Networking/Configuration/NetworkConfigurationExtensions.cs
@@ -1,4 +1,3 @@
-using Jellyfin.Networking.Configuration;
using MediaBrowser.Common.Configuration;
namespace Jellyfin.Networking.Configuration
diff --git a/Jellyfin.Networking/Manager/NetworkManager.cs b/Jellyfin.Networking/Manager/NetworkManager.cs
index 73e8b2cd7..4078fd126 100644
--- a/Jellyfin.Networking/Manager/NetworkManager.cs
+++ b/Jellyfin.Networking/Manager/NetworkManager.cs
@@ -1067,7 +1067,7 @@ namespace Jellyfin.Networking.Manager
}
// Internal interfaces must be private, not excluded and part of the LocalNetworkSubnet.
- _internalInterfaces = CreateCollection(_interfaceAddresses.Where(i => IsInLocalNetwork(i)));
+ _internalInterfaces = CreateCollection(_interfaceAddresses.Where(IsInLocalNetwork));
}
_logger.LogInformation("Defined LAN addresses : {0}", _lanSubnets.AsString());
diff --git a/Jellyfin.Server.Implementations/JellyfinDb.cs b/Jellyfin.Server.Implementations/JellyfinDb.cs
index 39f842354..db648472d 100644
--- a/Jellyfin.Server.Implementations/JellyfinDb.cs
+++ b/Jellyfin.Server.Implementations/JellyfinDb.cs
@@ -149,21 +149,77 @@ namespace Jellyfin.Server.Implementations
modelBuilder.HasDefaultSchema("jellyfin");
+ // Collations
+
+ modelBuilder.Entity<User>()
+ .Property(user => user.Username)
+ .UseCollation("NOCASE");
+
+ // Delete behavior
+
+ modelBuilder.Entity<User>()
+ .HasOne(u => u.ProfileImage)
+ .WithOne()
+ .OnDelete(DeleteBehavior.Cascade);
+
+ modelBuilder.Entity<User>()
+ .HasMany(u => u.Permissions)
+ .WithOne()
+ .HasForeignKey(p => p.UserId)
+ .OnDelete(DeleteBehavior.Cascade);
+
+ modelBuilder.Entity<User>()
+ .HasMany(u => u.Preferences)
+ .WithOne()
+ .HasForeignKey(p => p.UserId)
+ .OnDelete(DeleteBehavior.Cascade);
+
+ modelBuilder.Entity<User>()
+ .HasMany(u => u.AccessSchedules)
+ .WithOne()
+ .OnDelete(DeleteBehavior.Cascade);
+
+ modelBuilder.Entity<User>()
+ .HasMany(u => u.DisplayPreferences)
+ .WithOne()
+ .OnDelete(DeleteBehavior.Cascade);
+
+ modelBuilder.Entity<User>()
+ .HasMany(u => u.ItemDisplayPreferences)
+ .WithOne()
+ .OnDelete(DeleteBehavior.Cascade);
+
modelBuilder.Entity<DisplayPreferences>()
- .HasIndex(entity => entity.UserId)
- .IsUnique(false);
+ .HasMany(d => d.HomeSections)
+ .WithOne()
+ .OnDelete(DeleteBehavior.Cascade);
+
+ // Indexes
+
+ modelBuilder.Entity<User>()
+ .HasIndex(entity => entity.Username)
+ .IsUnique();
modelBuilder.Entity<DisplayPreferences>()
.HasIndex(entity => new { entity.UserId, entity.ItemId, entity.Client })
.IsUnique();
modelBuilder.Entity<CustomItemDisplayPreferences>()
- .HasIndex(entity => entity.UserId)
- .IsUnique(false);
-
- modelBuilder.Entity<CustomItemDisplayPreferences>()
.HasIndex(entity => new { entity.UserId, entity.ItemId, entity.Client, entity.Key })
.IsUnique();
+
+ // Used to get a user's permissions or a specific permission for a user.
+ // Also prevents multiple values being created for a user.
+ // Filtered over non-null user ids for when other entities (groups, API keys) get permissions
+ modelBuilder.Entity<Permission>()
+ .HasIndex(p => new { p.UserId, p.Kind })
+ .HasFilter("[UserId] IS NOT NULL")
+ .IsUnique();
+
+ modelBuilder.Entity<Preference>()
+ .HasIndex(p => new { p.UserId, p.Kind })
+ .HasFilter("[UserId] IS NOT NULL")
+ .IsUnique();
}
}
}
diff --git a/Jellyfin.Server.Implementations/Migrations/20210320181425_AddIndexesAndCollations.Designer.cs b/Jellyfin.Server.Implementations/Migrations/20210320181425_AddIndexesAndCollations.Designer.cs
new file mode 100644
index 000000000..869676824
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Migrations/20210320181425_AddIndexesAndCollations.Designer.cs
@@ -0,0 +1,535 @@
+#pragma warning disable CS1591
+
+// <auto-generated />
+using System;
+using Jellyfin.Server.Implementations;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+namespace Jellyfin.Server.Implementations.Migrations
+{
+ [DbContext(typeof(JellyfinDb))]
+ [Migration("20210320181425_AddIndexesAndCollations")]
+ partial class AddIndexesAndCollations
+ {
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasDefaultSchema("jellyfin")
+ .HasAnnotation("ProductVersion", "5.0.3");
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.AccessSchedule", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property<int>("DayOfWeek")
+ .HasColumnType("INTEGER");
+
+ b.Property<double>("EndHour")
+ .HasColumnType("REAL");
+
+ b.Property<double>("StartHour")
+ .HasColumnType("REAL");
+
+ b.Property<Guid>("UserId")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("AccessSchedules");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.ActivityLog", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property<DateTime>("DateCreated")
+ .HasColumnType("TEXT");
+
+ b.Property<string>("ItemId")
+ .HasMaxLength(256)
+ .HasColumnType("TEXT");
+
+ b.Property<int>("LogSeverity")
+ .HasColumnType("INTEGER");
+
+ b.Property<string>("Name")
+ .IsRequired()
+ .HasMaxLength(512)
+ .HasColumnType("TEXT");
+
+ b.Property<string>("Overview")
+ .HasMaxLength(512)
+ .HasColumnType("TEXT");
+
+ b.Property<uint>("RowVersion")
+ .IsConcurrencyToken()
+ .HasColumnType("INTEGER");
+
+ b.Property<string>("ShortOverview")
+ .HasMaxLength(512)
+ .HasColumnType("TEXT");
+
+ b.Property<string>("Type")
+ .IsRequired()
+ .HasMaxLength(256)
+ .HasColumnType("TEXT");
+
+ b.Property<Guid>("UserId")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.ToTable("ActivityLogs");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.CustomItemDisplayPreferences", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property<string>("Client")
+ .IsRequired()
+ .HasMaxLength(32)
+ .HasColumnType("TEXT");
+
+ b.Property<Guid>("ItemId")
+ .HasColumnType("TEXT");
+
+ b.Property<string>("Key")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property<Guid>("UserId")
+ .HasColumnType("TEXT");
+
+ b.Property<string>("Value")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId", "ItemId", "Client", "Key")
+ .IsUnique();
+
+ b.ToTable("CustomItemDisplayPreferences");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.DisplayPreferences", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property<int>("ChromecastVersion")
+ .HasColumnType("INTEGER");
+
+ b.Property<string>("Client")
+ .IsRequired()
+ .HasMaxLength(32)
+ .HasColumnType("TEXT");
+
+ b.Property<string>("DashboardTheme")
+ .HasMaxLength(32)
+ .HasColumnType("TEXT");
+
+ b.Property<bool>("EnableNextVideoInfoOverlay")
+ .HasColumnType("INTEGER");
+
+ b.Property<int?>("IndexBy")
+ .HasColumnType("INTEGER");
+
+ b.Property<Guid>("ItemId")
+ .HasColumnType("TEXT");
+
+ b.Property<int>("ScrollDirection")
+ .HasColumnType("INTEGER");
+
+ b.Property<bool>("ShowBackdrop")
+ .HasColumnType("INTEGER");
+
+ b.Property<bool>("ShowSidebar")
+ .HasColumnType("INTEGER");
+
+ b.Property<int>("SkipBackwardLength")
+ .HasColumnType("INTEGER");
+
+ b.Property<int>("SkipForwardLength")
+ .HasColumnType("INTEGER");
+
+ b.Property<string>("TvHome")
+ .HasMaxLength(32)
+ .HasColumnType("TEXT");
+
+ b.Property<Guid>("UserId")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId", "ItemId", "Client")
+ .IsUnique();
+
+ b.ToTable("DisplayPreferences");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.HomeSection", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property<int>("DisplayPreferencesId")
+ .HasColumnType("INTEGER");
+
+ b.Property<int>("Order")
+ .HasColumnType("INTEGER");
+
+ b.Property<int>("Type")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("Id");
+
+ b.HasIndex("DisplayPreferencesId");
+
+ b.ToTable("HomeSection");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.ImageInfo", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property<DateTime>("LastModified")
+ .HasColumnType("TEXT");
+
+ b.Property<string>("Path")
+ .IsRequired()
+ .HasMaxLength(512)
+ .HasColumnType("TEXT");
+
+ b.Property<Guid?>("UserId")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId")
+ .IsUnique();
+
+ b.ToTable("ImageInfos");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.ItemDisplayPreferences", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property<string>("Client")
+ .IsRequired()
+ .HasMaxLength(32)
+ .HasColumnType("TEXT");
+
+ b.Property<int?>("IndexBy")
+ .HasColumnType("INTEGER");
+
+ b.Property<Guid>("ItemId")
+ .HasColumnType("TEXT");
+
+ b.Property<bool>("RememberIndexing")
+ .HasColumnType("INTEGER");
+
+ b.Property<bool>("RememberSorting")
+ .HasColumnType("INTEGER");
+
+ b.Property<string>("SortBy")
+ .IsRequired()
+ .HasMaxLength(64)
+ .HasColumnType("TEXT");
+
+ b.Property<int>("SortOrder")
+ .HasColumnType("INTEGER");
+
+ b.Property<Guid>("UserId")
+ .HasColumnType("TEXT");
+
+ b.Property<int>("ViewType")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("ItemDisplayPreferences");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property<int>("Kind")
+ .HasColumnType("INTEGER");
+
+ b.Property<Guid?>("Permission_Permissions_Guid")
+ .HasColumnType("TEXT");
+
+ b.Property<uint>("RowVersion")
+ .IsConcurrencyToken()
+ .HasColumnType("INTEGER");
+
+ b.Property<Guid?>("UserId")
+ .HasColumnType("TEXT");
+
+ b.Property<bool>("Value")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId", "Kind")
+ .IsUnique()
+ .HasFilter("[UserId] IS NOT NULL");
+
+ b.ToTable("Permissions");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property<int>("Kind")
+ .HasColumnType("INTEGER");
+
+ b.Property<Guid?>("Preference_Preferences_Guid")
+ .HasColumnType("TEXT");
+
+ b.Property<uint>("RowVersion")
+ .IsConcurrencyToken()
+ .HasColumnType("INTEGER");
+
+ b.Property<Guid?>("UserId")
+ .HasColumnType("TEXT");
+
+ b.Property<string>("Value")
+ .IsRequired()
+ .HasMaxLength(65535)
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId", "Kind")
+ .IsUnique()
+ .HasFilter("[UserId] IS NOT NULL");
+
+ b.ToTable("Preferences");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.User", b =>
+ {
+ b.Property<Guid>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property<string>("AudioLanguagePreference")
+ .HasMaxLength(255)
+ .HasColumnType("TEXT");
+
+ b.Property<string>("AuthenticationProviderId")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("TEXT");
+
+ b.Property<bool>("DisplayCollectionsView")
+ .HasColumnType("INTEGER");
+
+ b.Property<bool>("DisplayMissingEpisodes")
+ .HasColumnType("INTEGER");
+
+ b.Property<string>("EasyPassword")
+ .HasMaxLength(65535)
+ .HasColumnType("TEXT");
+
+ b.Property<bool>("EnableAutoLogin")
+ .HasColumnType("INTEGER");
+
+ b.Property<bool>("EnableLocalPassword")
+ .HasColumnType("INTEGER");
+
+ b.Property<bool>("EnableNextEpisodeAutoPlay")
+ .HasColumnType("INTEGER");
+
+ b.Property<bool>("EnableUserPreferenceAccess")
+ .HasColumnType("INTEGER");
+
+ b.Property<bool>("HidePlayedInLatest")
+ .HasColumnType("INTEGER");
+
+ b.Property<long>("InternalId")
+ .HasColumnType("INTEGER");
+
+ b.Property<int>("InvalidLoginAttemptCount")
+ .HasColumnType("INTEGER");
+
+ b.Property<DateTime?>("LastActivityDate")
+ .HasColumnType("TEXT");
+
+ b.Property<DateTime?>("LastLoginDate")
+ .HasColumnType("TEXT");
+
+ b.Property<int?>("LoginAttemptsBeforeLockout")
+ .HasColumnType("INTEGER");
+
+ b.Property<int>("MaxActiveSessions")
+ .HasColumnType("INTEGER");
+
+ b.Property<int?>("MaxParentalAgeRating")
+ .HasColumnType("INTEGER");
+
+ b.Property<bool>("MustUpdatePassword")
+ .HasColumnType("INTEGER");
+
+ b.Property<string>("Password")
+ .HasMaxLength(65535)
+ .HasColumnType("TEXT");
+
+ b.Property<string>("PasswordResetProviderId")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("TEXT");
+
+ b.Property<bool>("PlayDefaultAudioTrack")
+ .HasColumnType("INTEGER");
+
+ b.Property<bool>("RememberAudioSelections")
+ .HasColumnType("INTEGER");
+
+ b.Property<bool>("RememberSubtitleSelections")
+ .HasColumnType("INTEGER");
+
+ b.Property<int?>("RemoteClientBitrateLimit")
+ .HasColumnType("INTEGER");
+
+ b.Property<uint>("RowVersion")
+ .IsConcurrencyToken()
+ .HasColumnType("INTEGER");
+
+ b.Property<string>("SubtitleLanguagePreference")
+ .HasMaxLength(255)
+ .HasColumnType("TEXT");
+
+ b.Property<int>("SubtitleMode")
+ .HasColumnType("INTEGER");
+
+ b.Property<int>("SyncPlayAccess")
+ .HasColumnType("INTEGER");
+
+ b.Property<string>("Username")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("TEXT")
+ .UseCollation("NOCASE");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Username")
+ .IsUnique();
+
+ b.ToTable("Users");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.AccessSchedule", b =>
+ {
+ b.HasOne("Jellyfin.Data.Entities.User", null)
+ .WithMany("AccessSchedules")
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.DisplayPreferences", b =>
+ {
+ b.HasOne("Jellyfin.Data.Entities.User", null)
+ .WithMany("DisplayPreferences")
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.HomeSection", b =>
+ {
+ b.HasOne("Jellyfin.Data.Entities.DisplayPreferences", null)
+ .WithMany("HomeSections")
+ .HasForeignKey("DisplayPreferencesId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.ImageInfo", b =>
+ {
+ b.HasOne("Jellyfin.Data.Entities.User", null)
+ .WithOne("ProfileImage")
+ .HasForeignKey("Jellyfin.Data.Entities.ImageInfo", "UserId")
+ .OnDelete(DeleteBehavior.Cascade);
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.ItemDisplayPreferences", b =>
+ {
+ b.HasOne("Jellyfin.Data.Entities.User", null)
+ .WithMany("ItemDisplayPreferences")
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b =>
+ {
+ b.HasOne("Jellyfin.Data.Entities.User", null)
+ .WithMany("Permissions")
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade);
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b =>
+ {
+ b.HasOne("Jellyfin.Data.Entities.User", null)
+ .WithMany("Preferences")
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade);
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.DisplayPreferences", b =>
+ {
+ b.Navigation("HomeSections");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.User", b =>
+ {
+ b.Navigation("AccessSchedules");
+
+ b.Navigation("DisplayPreferences");
+
+ b.Navigation("ItemDisplayPreferences");
+
+ b.Navigation("Permissions");
+
+ b.Navigation("Preferences");
+
+ b.Navigation("ProfileImage");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Migrations/20210320181425_AddIndexesAndCollations.cs b/Jellyfin.Server.Implementations/Migrations/20210320181425_AddIndexesAndCollations.cs
new file mode 100644
index 000000000..506e4ae66
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Migrations/20210320181425_AddIndexesAndCollations.cs
@@ -0,0 +1,240 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1601
+
+using System;
+using Microsoft.EntityFrameworkCore.Migrations;
+
+namespace Jellyfin.Server.Implementations.Migrations
+{
+ public partial class AddIndexesAndCollations : Migration
+ {
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropForeignKey(
+ name: "FK_ImageInfos_Users_UserId",
+ schema: "jellyfin",
+ table: "ImageInfos");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_Permissions_Users_Permission_Permissions_Guid",
+ schema: "jellyfin",
+ table: "Permissions");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_Preferences_Users_Preference_Preferences_Guid",
+ schema: "jellyfin",
+ table: "Preferences");
+
+ migrationBuilder.DropIndex(
+ name: "IX_Preferences_Preference_Preferences_Guid",
+ schema: "jellyfin",
+ table: "Preferences");
+
+ migrationBuilder.DropIndex(
+ name: "IX_Permissions_Permission_Permissions_Guid",
+ schema: "jellyfin",
+ table: "Permissions");
+
+ migrationBuilder.DropIndex(
+ name: "IX_DisplayPreferences_UserId",
+ schema: "jellyfin",
+ table: "DisplayPreferences");
+
+ migrationBuilder.DropIndex(
+ name: "IX_CustomItemDisplayPreferences_UserId",
+ schema: "jellyfin",
+ table: "CustomItemDisplayPreferences");
+
+ migrationBuilder.AlterColumn<string>(
+ name: "Username",
+ schema: "jellyfin",
+ table: "Users",
+ type: "TEXT",
+ maxLength: 255,
+ nullable: false,
+ collation: "NOCASE",
+ oldClrType: typeof(string),
+ oldType: "TEXT",
+ oldMaxLength: 255);
+
+ migrationBuilder.AddColumn<Guid>(
+ name: "UserId",
+ schema: "jellyfin",
+ table: "Preferences",
+ type: "TEXT",
+ nullable: true);
+
+ migrationBuilder.AddColumn<Guid>(
+ name: "UserId",
+ schema: "jellyfin",
+ table: "Permissions",
+ type: "TEXT",
+ nullable: true);
+
+ migrationBuilder.Sql("UPDATE Preferences SET UserId = Preference_Preferences_Guid");
+ migrationBuilder.Sql("UPDATE Permissions SET UserId = Permission_Permissions_Guid");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_Users_Username",
+ schema: "jellyfin",
+ table: "Users",
+ column: "Username",
+ unique: true);
+
+ migrationBuilder.CreateIndex(
+ name: "IX_Preferences_UserId_Kind",
+ schema: "jellyfin",
+ table: "Preferences",
+ columns: new[] { "UserId", "Kind" },
+ unique: true,
+ filter: "[UserId] IS NOT NULL");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_Permissions_UserId_Kind",
+ schema: "jellyfin",
+ table: "Permissions",
+ columns: new[] { "UserId", "Kind" },
+ unique: true,
+ filter: "[UserId] IS NOT NULL");
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_ImageInfos_Users_UserId",
+ schema: "jellyfin",
+ table: "ImageInfos",
+ column: "UserId",
+ principalSchema: "jellyfin",
+ principalTable: "Users",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_Permissions_Users_UserId",
+ schema: "jellyfin",
+ table: "Permissions",
+ column: "UserId",
+ principalSchema: "jellyfin",
+ principalTable: "Users",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_Preferences_Users_UserId",
+ schema: "jellyfin",
+ table: "Preferences",
+ column: "UserId",
+ principalSchema: "jellyfin",
+ principalTable: "Users",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ }
+
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropForeignKey(
+ name: "FK_ImageInfos_Users_UserId",
+ schema: "jellyfin",
+ table: "ImageInfos");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_Permissions_Users_UserId",
+ schema: "jellyfin",
+ table: "Permissions");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_Preferences_Users_UserId",
+ schema: "jellyfin",
+ table: "Preferences");
+
+ migrationBuilder.DropIndex(
+ name: "IX_Users_Username",
+ schema: "jellyfin",
+ table: "Users");
+
+ migrationBuilder.DropIndex(
+ name: "IX_Preferences_UserId_Kind",
+ schema: "jellyfin",
+ table: "Preferences");
+
+ migrationBuilder.DropIndex(
+ name: "IX_Permissions_UserId_Kind",
+ schema: "jellyfin",
+ table: "Permissions");
+
+ migrationBuilder.DropColumn(
+ name: "UserId",
+ schema: "jellyfin",
+ table: "Preferences");
+
+ migrationBuilder.DropColumn(
+ name: "UserId",
+ schema: "jellyfin",
+ table: "Permissions");
+
+ migrationBuilder.AlterColumn<string>(
+ name: "Username",
+ schema: "jellyfin",
+ table: "Users",
+ type: "TEXT",
+ maxLength: 255,
+ nullable: false,
+ oldClrType: typeof(string),
+ oldType: "TEXT",
+ oldMaxLength: 255,
+ oldCollation: "NOCASE");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_Preferences_Preference_Preferences_Guid",
+ schema: "jellyfin",
+ table: "Preferences",
+ column: "Preference_Preferences_Guid");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_Permissions_Permission_Permissions_Guid",
+ schema: "jellyfin",
+ table: "Permissions",
+ column: "Permission_Permissions_Guid");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_DisplayPreferences_UserId",
+ schema: "jellyfin",
+ table: "DisplayPreferences",
+ column: "UserId");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_CustomItemDisplayPreferences_UserId",
+ schema: "jellyfin",
+ table: "CustomItemDisplayPreferences",
+ column: "UserId");
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_ImageInfos_Users_UserId",
+ schema: "jellyfin",
+ table: "ImageInfos",
+ column: "UserId",
+ principalSchema: "jellyfin",
+ principalTable: "Users",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Restrict);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_Permissions_Users_Permission_Permissions_Guid",
+ schema: "jellyfin",
+ table: "Permissions",
+ column: "Permission_Permissions_Guid",
+ principalSchema: "jellyfin",
+ principalTable: "Users",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Restrict);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_Preferences_Users_Preference_Preferences_Guid",
+ schema: "jellyfin",
+ table: "Preferences",
+ column: "Preference_Preferences_Guid",
+ principalSchema: "jellyfin",
+ principalTable: "Users",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Restrict);
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs b/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs
index 6a523ba68..286eb7468 100644
--- a/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs
+++ b/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs
@@ -114,8 +114,6 @@ namespace Jellyfin.Server.Implementations.Migrations
b.HasKey("Id");
- b.HasIndex("UserId");
-
b.HasIndex("UserId", "ItemId", "Client", "Key")
.IsUnique();
@@ -173,8 +171,6 @@ namespace Jellyfin.Server.Implementations.Migrations
b.HasKey("Id");
- b.HasIndex("UserId");
-
b.HasIndex("UserId", "ItemId", "Client")
.IsUnique();
@@ -288,12 +284,17 @@ namespace Jellyfin.Server.Implementations.Migrations
.IsConcurrencyToken()
.HasColumnType("INTEGER");
+ b.Property<Guid?>("UserId")
+ .HasColumnType("TEXT");
+
b.Property<bool>("Value")
.HasColumnType("INTEGER");
b.HasKey("Id");
- b.HasIndex("Permission_Permissions_Guid");
+ b.HasIndex("UserId", "Kind")
+ .IsUnique()
+ .HasFilter("[UserId] IS NOT NULL");
b.ToTable("Permissions");
});
@@ -314,6 +315,9 @@ namespace Jellyfin.Server.Implementations.Migrations
.IsConcurrencyToken()
.HasColumnType("INTEGER");
+ b.Property<Guid?>("UserId")
+ .HasColumnType("TEXT");
+
b.Property<string>("Value")
.IsRequired()
.HasMaxLength(65535)
@@ -321,7 +325,9 @@ namespace Jellyfin.Server.Implementations.Migrations
b.HasKey("Id");
- b.HasIndex("Preference_Preferences_Guid");
+ b.HasIndex("UserId", "Kind")
+ .IsUnique()
+ .HasFilter("[UserId] IS NOT NULL");
b.ToTable("Preferences");
});
@@ -428,10 +434,14 @@ namespace Jellyfin.Server.Implementations.Migrations
b.Property<string>("Username")
.IsRequired()
.HasMaxLength(255)
- .HasColumnType("TEXT");
+ .HasColumnType("TEXT")
+ .UseCollation("NOCASE");
b.HasKey("Id");
+ b.HasIndex("Username")
+ .IsUnique();
+
b.ToTable("Users");
});
@@ -466,7 +476,8 @@ namespace Jellyfin.Server.Implementations.Migrations
{
b.HasOne("Jellyfin.Data.Entities.User", null)
.WithOne("ProfileImage")
- .HasForeignKey("Jellyfin.Data.Entities.ImageInfo", "UserId");
+ .HasForeignKey("Jellyfin.Data.Entities.ImageInfo", "UserId")
+ .OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Jellyfin.Data.Entities.ItemDisplayPreferences", b =>
@@ -482,14 +493,16 @@ namespace Jellyfin.Server.Implementations.Migrations
{
b.HasOne("Jellyfin.Data.Entities.User", null)
.WithMany("Permissions")
- .HasForeignKey("Permission_Permissions_Guid");
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b =>
{
b.HasOne("Jellyfin.Data.Entities.User", null)
.WithMany("Preferences")
- .HasForeignKey("Preference_Preferences_Guid");
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Jellyfin.Data.Entities.DisplayPreferences", b =>
diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs
index a2c11cb8a..27d4f40d3 100644
--- a/Jellyfin.Server.Implementations/Users/UserManager.cs
+++ b/Jellyfin.Server.Implementations/Users/UserManager.cs
@@ -144,7 +144,13 @@ namespace Jellyfin.Server.Implementations.Users
throw new ArgumentException("The new and old names must be different.");
}
- if (Users.Any(u => u.Id != user.Id && u.Username.Equals(newName, StringComparison.OrdinalIgnoreCase)))
+ await using var dbContext = _dbProvider.CreateContext();
+
+ if (await dbContext.Users
+ .AsQueryable()
+ .Where(u => u.Username == newName && u.Id != user.Id)
+ .AnyAsync()
+ .ConfigureAwait(false))
{
throw new ArgumentException(string.Format(
CultureInfo.InvariantCulture,
@@ -251,16 +257,6 @@ namespace Jellyfin.Server.Implementations.Users
}
await using var dbContext = _dbProvider.CreateContext();
-
- // Clear all entities related to the user from the database.
- if (user.ProfileImage != null)
- {
- dbContext.Remove(user.ProfileImage);
- }
-
- dbContext.RemoveRange(user.Permissions);
- dbContext.RemoveRange(user.Preferences);
- dbContext.RemoveRange(user.AccessSchedules);
dbContext.Users.Remove(user);
await dbContext.SaveChangesAsync().ConfigureAwait(false);
_users.Remove(userId);
diff --git a/Jellyfin.Server/Filters/ParameterObsoleteFilter.cs b/Jellyfin.Server/Filters/ParameterObsoleteFilter.cs
index e54044d0e..b9ce221f5 100644
--- a/Jellyfin.Server/Filters/ParameterObsoleteFilter.cs
+++ b/Jellyfin.Server/Filters/ParameterObsoleteFilter.cs
@@ -1,7 +1,6 @@
using System;
using System.Linq;
using Jellyfin.Api.Attributes;
-using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
diff --git a/Jellyfin.Server/Formatters/CssOutputFormatter.cs b/Jellyfin.Server/Formatters/CssOutputFormatter.cs
index e8dd48e4e..cfc9d1ad3 100644
--- a/Jellyfin.Server/Formatters/CssOutputFormatter.cs
+++ b/Jellyfin.Server/Formatters/CssOutputFormatter.cs
@@ -1,5 +1,4 @@
-using System;
-using System.Text;
+using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Formatters;
diff --git a/Jellyfin.Server/Middleware/IpBasedAccessValidationMiddleware.cs b/Jellyfin.Server/Middleware/IpBasedAccessValidationMiddleware.cs
index 7d92bd7d3..0afcd61a0 100644
--- a/Jellyfin.Server/Middleware/IpBasedAccessValidationMiddleware.cs
+++ b/Jellyfin.Server/Middleware/IpBasedAccessValidationMiddleware.cs
@@ -1,9 +1,7 @@
using System.Net;
using System.Threading.Tasks;
-using Jellyfin.Networking.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Configuration;
using Microsoft.AspNetCore.Http;
namespace Jellyfin.Server.Middleware
diff --git a/Jellyfin.Server/Middleware/LanFilteringMiddleware.cs b/Jellyfin.Server/Middleware/LanFilteringMiddleware.cs
index 8065054a1..67bf24d2a 100644
--- a/Jellyfin.Server/Middleware/LanFilteringMiddleware.cs
+++ b/Jellyfin.Server/Middleware/LanFilteringMiddleware.cs
@@ -1,9 +1,6 @@
-using System;
-using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Jellyfin.Networking.Configuration;
-using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using Microsoft.AspNetCore.Http;
diff --git a/Jellyfin.Server/Migrations/Routines/AddPeopleQueryIndex.cs b/Jellyfin.Server/Migrations/Routines/AddPeopleQueryIndex.cs
index 2521d9952..6343c422d 100644
--- a/Jellyfin.Server/Migrations/Routines/AddPeopleQueryIndex.cs
+++ b/Jellyfin.Server/Migrations/Routines/AddPeopleQueryIndex.cs
@@ -41,9 +41,9 @@ namespace Jellyfin.Server.Migrations.Routines
var databasePath = Path.Join(_serverApplicationPaths.DataPath, DbFilename);
using var connection = SQLite3.Open(databasePath, ConnectionFlags.ReadWrite, null);
_logger.LogInformation("Creating index idx_TypedBaseItemsUserDataKeyType");
- connection.Execute("CREATE INDEX idx_TypedBaseItemsUserDataKeyType ON TypedBaseItems(UserDataKey, Type);");
+ connection.Execute("CREATE INDEX IF NOT EXISTS idx_TypedBaseItemsUserDataKeyType ON TypedBaseItems(UserDataKey, Type);");
_logger.LogInformation("Creating index idx_PeopleNameListOrder");
- connection.Execute("CREATE INDEX idx_PeopleNameListOrder ON People(Name, ListOrder);");
+ connection.Execute("CREATE INDEX IF NOT EXISTS idx_PeopleNameListOrder ON People(Name, ListOrder);");
}
}
-} \ No newline at end of file
+}
diff --git a/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs b/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs
index 6821630db..ee4f8b0ba 100644
--- a/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs
+++ b/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs
@@ -75,7 +75,7 @@ namespace Jellyfin.Server.Migrations.Routines
{
var existingConfigJson = JToken.Parse(File.ReadAllText(oldConfigPath));
return _defaultConfigHistory
- .Select(historicalConfigText => JToken.Parse(historicalConfigText))
+ .Select(JToken.Parse)
.Any(historicalConfigJson => JToken.DeepEquals(existingConfigJson, historicalConfigJson));
}
}
diff --git a/Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs b/Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs
index bf0225e98..378e88e25 100644
--- a/Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs
+++ b/Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs
@@ -1,6 +1,5 @@
using System;
using MediaBrowser.Common.Configuration;
-using MediaBrowser.Model.Configuration;
using Microsoft.Extensions.Logging;
namespace Jellyfin.Server.Migrations.Routines
diff --git a/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs
index 07829c696..e25d29122 100644
--- a/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs
+++ b/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs
@@ -126,13 +126,13 @@ namespace Jellyfin.Server.Migrations.Routines
ShowSidebar = dto.ShowSidebar,
ScrollDirection = dto.ScrollDirection,
ChromecastVersion = chromecastVersion,
- SkipForwardLength = dto.CustomPrefs.TryGetValue("skipForwardLength", out var length)
- ? int.Parse(length, CultureInfo.InvariantCulture)
+ SkipForwardLength = dto.CustomPrefs.TryGetValue("skipForwardLength", out var length) && int.TryParse(length, out var skipForwardLength)
+ ? skipForwardLength
: 30000,
- SkipBackwardLength = dto.CustomPrefs.TryGetValue("skipBackLength", out length)
- ? int.Parse(length, CultureInfo.InvariantCulture)
+ SkipBackwardLength = dto.CustomPrefs.TryGetValue("skipBackLength", out length) && !string.IsNullOrEmpty(length) && int.TryParse(length, out var skipBackwardLength)
+ ? skipBackwardLength
: 10000,
- EnableNextVideoInfoOverlay = dto.CustomPrefs.TryGetValue("enableNextVideoInfoOverlay", out var enabled)
+ EnableNextVideoInfoOverlay = dto.CustomPrefs.TryGetValue("enableNextVideoInfoOverlay", out var enabled) && !string.IsNullOrEmpty(enabled)
? bool.Parse(enabled)
: true,
DashboardTheme = dto.CustomPrefs.TryGetValue("dashboardtheme", out var theme) ? theme : string.Empty,
diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs
index 464e02419..c10b2ddb3 100644
--- a/Jellyfin.Server/Program.cs
+++ b/Jellyfin.Server/Program.cs
@@ -12,12 +12,10 @@ using System.Threading.Tasks;
using CommandLine;
using Emby.Server.Implementations;
using Emby.Server.Implementations.IO;
-using Jellyfin.Api.Controllers;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Extensions;
using Microsoft.AspNetCore.Hosting;
-using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
diff --git a/Jellyfin.Server/Startup.cs b/Jellyfin.Server/Startup.cs
index e56e61092..f75139884 100644
--- a/Jellyfin.Server/Startup.cs
+++ b/Jellyfin.Server/Startup.cs
@@ -1,5 +1,9 @@
+using System;
+using System.Net;
+using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Mime;
+using System.Text;
using Jellyfin.Networking.Configuration;
using Jellyfin.Server.Extensions;
using Jellyfin.Server.Implementations;
@@ -67,6 +71,12 @@ namespace Jellyfin.Server
var acceptJsonHeader = new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Json, 1.0);
var acceptXmlHeader = new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Xml, 0.9);
var acceptAnyHeader = new MediaTypeWithQualityHeaderValue("*/*", 0.8);
+ Func<IServiceProvider, HttpMessageHandler> defaultHttpClientHandlerDelegate = (_) => new SocketsHttpHandler()
+ {
+ AutomaticDecompression = DecompressionMethods.All,
+ RequestHeaderEncodingSelector = (_, _) => Encoding.UTF8
+ };
+
services
.AddHttpClient(NamedClient.Default, c =>
{
@@ -75,7 +85,7 @@ namespace Jellyfin.Server
c.DefaultRequestHeaders.Accept.Add(acceptXmlHeader);
c.DefaultRequestHeaders.Accept.Add(acceptAnyHeader);
})
- .ConfigurePrimaryHttpMessageHandler(x => new DefaultHttpClientHandler());
+ .ConfigurePrimaryHttpMessageHandler(defaultHttpClientHandlerDelegate);
services.AddHttpClient(NamedClient.MusicBrainz, c =>
{
@@ -84,7 +94,7 @@ namespace Jellyfin.Server
c.DefaultRequestHeaders.Accept.Add(acceptXmlHeader);
c.DefaultRequestHeaders.Accept.Add(acceptAnyHeader);
})
- .ConfigurePrimaryHttpMessageHandler(x => new DefaultHttpClientHandler());
+ .ConfigurePrimaryHttpMessageHandler(defaultHttpClientHandlerDelegate);
services.AddHealthChecks()
.AddDbContextCheck<JellyfinDb>();
diff --git a/Jellyfin.Server/StartupOptions.cs b/Jellyfin.Server/StartupOptions.cs
index 6d8210527..a1cecc8c6 100644
--- a/Jellyfin.Server/StartupOptions.cs
+++ b/Jellyfin.Server/StartupOptions.cs
@@ -1,10 +1,7 @@
-using System;
using System.Collections.Generic;
using CommandLine;
using Emby.Server.Implementations;
-using Emby.Server.Implementations.EntryPoints;
using Emby.Server.Implementations.Udp;
-using Emby.Server.Implementations.Updates;
using MediaBrowser.Controller.Extensions;
namespace Jellyfin.Server
diff --git a/MediaBrowser.Common/Cryptography/PasswordHash.cs b/MediaBrowser.Common/Cryptography/PasswordHash.cs
index f2ecc4741..ec21d0580 100644
--- a/MediaBrowser.Common/Cryptography/PasswordHash.cs
+++ b/MediaBrowser.Common/Cryptography/PasswordHash.cs
@@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
-using System.IO;
using System.Text;
namespace MediaBrowser.Common.Cryptography
diff --git a/MediaBrowser.Common/Extensions/SplitStringExtensions.cs b/MediaBrowser.Common/Extensions/SplitStringExtensions.cs
new file mode 100644
index 000000000..e78fa78d6
--- /dev/null
+++ b/MediaBrowser.Common/Extensions/SplitStringExtensions.cs
@@ -0,0 +1,95 @@
+/*
+MIT License
+
+Copyright (c) 2019 Gérald Barré
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ */
+#nullable enable
+#pragma warning disable CS1591
+#pragma warning disable CA1034
+using System;
+using System.Diagnostics.Contracts;
+using System.Runtime.InteropServices;
+
+namespace MediaBrowser.Common.Extensions
+{
+ /// <summary>
+ /// Extension class for splitting lines without unnecessary allocations.
+ /// </summary>
+ public static class SplitStringExtensions
+ {
+ /// <summary>
+ /// Creates a new string split enumerator.
+ /// </summary>
+ /// <param name="str">The string to split.</param>
+ /// <param name="separator">The separator to split on.</param>
+ /// <returns>The enumerator struct.</returns>
+ [Pure]
+ public static SplitEnumerator SpanSplit(this string str, char separator) => new (str.AsSpan(), separator);
+
+ /// <summary>
+ /// Creates a new span split enumerator.
+ /// </summary>
+ /// <param name="str">The span to split.</param>
+ /// <param name="separator">The separator to split on.</param>
+ /// <returns>The enumerator struct.</returns>
+ [Pure]
+ public static SplitEnumerator Split(this ReadOnlySpan<char> str, char separator) => new (str, separator);
+
+ [StructLayout(LayoutKind.Auto)]
+ public ref struct SplitEnumerator
+ {
+ private readonly char _separator;
+ private ReadOnlySpan<char> _str;
+
+ public SplitEnumerator(ReadOnlySpan<char> str, char separator)
+ {
+ _str = str;
+ _separator = separator;
+ Current = default;
+ }
+
+ public ReadOnlySpan<char> Current { get; private set; }
+
+ public readonly SplitEnumerator GetEnumerator() => this;
+
+ public bool MoveNext()
+ {
+ if (_str.Length == 0)
+ {
+ return false;
+ }
+
+ var span = _str;
+ var index = span.IndexOf(_separator);
+ if (index == -1)
+ {
+ _str = ReadOnlySpan<char>.Empty;
+ Current = span;
+ return true;
+ }
+
+ Current = span.Slice(0, index);
+ _str = span[(index + 1)..];
+ return true;
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Common/Net/DefaultHttpClientHandler.cs b/MediaBrowser.Common/Net/DefaultHttpClientHandler.cs
deleted file mode 100644
index f1c5f2477..000000000
--- a/MediaBrowser.Common/Net/DefaultHttpClientHandler.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using System.Net;
-using System.Net.Http;
-
-namespace MediaBrowser.Common.Net
-{
- /// <summary>
- /// Default http client handler.
- /// </summary>
- public class DefaultHttpClientHandler : HttpClientHandler
- {
- /// <summary>
- /// Initializes a new instance of the <see cref="DefaultHttpClientHandler"/> class.
- /// </summary>
- public DefaultHttpClientHandler()
- {
- AutomaticDecompression = DecompressionMethods.All;
- }
- }
-}
diff --git a/MediaBrowser.Common/Net/INetworkManager.cs b/MediaBrowser.Common/Net/INetworkManager.cs
index 012824f65..185df5b77 100644
--- a/MediaBrowser.Common/Net/INetworkManager.cs
+++ b/MediaBrowser.Common/Net/INetworkManager.cs
@@ -4,7 +4,6 @@ using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Net;
using System.Net.NetworkInformation;
-using MediaBrowser.Common.Net;
using Microsoft.AspNetCore.Http;
namespace MediaBrowser.Common.Net
diff --git a/MediaBrowser.Common/Net/IPHost.cs b/MediaBrowser.Common/Net/IPHost.cs
index fb3ef9b12..7156ce618 100644
--- a/MediaBrowser.Common/Net/IPHost.cs
+++ b/MediaBrowser.Common/Net/IPHost.cs
@@ -400,10 +400,7 @@ namespace MediaBrowser.Common.Net
private bool ResolveHost()
{
// When was the last time we resolved?
- if (_lastResolved == null)
- {
- _lastResolved = DateTime.UtcNow;
- }
+ _lastResolved ??= DateTime.UtcNow;
// If we haven't resolved before, or our timer has run out...
if ((_addresses.Length == 0 && !Resolved) || (DateTime.UtcNow > _lastResolved.Value.AddMinutes(Timeout)))
diff --git a/MediaBrowser.Common/Plugins/BasePluginOfT.cs b/MediaBrowser.Common/Plugins/BasePluginOfT.cs
index 99c226f50..e074cc6a0 100644
--- a/MediaBrowser.Common/Plugins/BasePluginOfT.cs
+++ b/MediaBrowser.Common/Plugins/BasePluginOfT.cs
@@ -105,10 +105,7 @@ namespace MediaBrowser.Common.Plugins
{
lock (_configurationSyncLock)
{
- if (_configuration == null)
- {
- _configuration = LoadConfiguration();
- }
+ _configuration ??= LoadConfiguration();
}
}
diff --git a/MediaBrowser.Controller/BaseItemManager/BaseItemManager.cs b/MediaBrowser.Controller/BaseItemManager/BaseItemManager.cs
index 31dd95402..a233c358e 100644
--- a/MediaBrowser.Controller/BaseItemManager/BaseItemManager.cs
+++ b/MediaBrowser.Controller/BaseItemManager/BaseItemManager.cs
@@ -1,7 +1,6 @@
using System;
using System.Linq;
using System.Threading;
-using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
diff --git a/MediaBrowser.Controller/BaseItemManager/IBaseItemManager.cs b/MediaBrowser.Controller/BaseItemManager/IBaseItemManager.cs
index e1f5d05a6..8a8736427 100644
--- a/MediaBrowser.Controller/BaseItemManager/IBaseItemManager.cs
+++ b/MediaBrowser.Controller/BaseItemManager/IBaseItemManager.cs
@@ -1,4 +1,3 @@
-using System;
using System.Threading;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Configuration;
diff --git a/MediaBrowser.Controller/Drawing/ImageHelper.cs b/MediaBrowser.Controller/Drawing/ImageHelper.cs
index 181f8e905..596fcbc8c 100644
--- a/MediaBrowser.Controller/Drawing/ImageHelper.cs
+++ b/MediaBrowser.Controller/Drawing/ImageHelper.cs
@@ -1,10 +1,7 @@
#pragma warning disable CS1591
#nullable enable
-using System;
-using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Drawing;
-using MediaBrowser.Model.Entities;
namespace MediaBrowser.Controller.Drawing
{
diff --git a/MediaBrowser.Controller/Entities/AggregateFolder.cs b/MediaBrowser.Controller/Entities/AggregateFolder.cs
index 6ebea5f44..6a92200dd 100644
--- a/MediaBrowser.Controller/Entities/AggregateFolder.cs
+++ b/MediaBrowser.Controller/Entities/AggregateFolder.cs
@@ -120,8 +120,7 @@ namespace MediaBrowser.Controller.Entities
var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, directoryService)
{
- FileInfo = FileSystem.GetDirectoryInfo(path),
- Path = path
+ FileInfo = FileSystem.GetDirectoryInfo(path)
};
// Gather child folder and files
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs
index 53d45261e..32ae15498 100644
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ b/MediaBrowser.Controller/Entities/BaseItem.cs
@@ -106,15 +106,10 @@ namespace MediaBrowser.Controller.Entities
{
get
{
- if (_themeSongIds == null)
- {
- _themeSongIds = GetExtras()
- .Where(extra => extra.ExtraType == Model.Entities.ExtraType.ThemeSong)
- .Select(song => song.Id)
- .ToArray();
- }
-
- return _themeSongIds;
+ return _themeSongIds ??= GetExtras()
+ .Where(extra => extra.ExtraType == Model.Entities.ExtraType.ThemeSong)
+ .Select(song => song.Id)
+ .ToArray();
}
private set
@@ -128,15 +123,10 @@ namespace MediaBrowser.Controller.Entities
{
get
{
- if (_themeVideoIds == null)
- {
- _themeVideoIds = GetExtras()
- .Where(extra => extra.ExtraType == Model.Entities.ExtraType.ThemeVideo)
- .Select(song => song.Id)
- .ToArray();
- }
-
- return _themeVideoIds;
+ return _themeVideoIds ??= GetExtras()
+ .Where(extra => extra.ExtraType == Model.Entities.ExtraType.ThemeVideo)
+ .Select(song => song.Id)
+ .ToArray();
}
private set
@@ -2324,7 +2314,7 @@ namespace MediaBrowser.Controller.Entities
.Where(i => i.IsLocalFile)
.Select(i => System.IO.Path.GetDirectoryName(i.Path))
.Distinct(StringComparer.OrdinalIgnoreCase)
- .SelectMany(i => directoryService.GetFilePaths(i))
+ .SelectMany(directoryService.GetFilePaths)
.ToList();
var deletedImages = ImageInfos
diff --git a/MediaBrowser.Controller/Entities/BaseItemExtensions.cs b/MediaBrowser.Controller/Entities/BaseItemExtensions.cs
index c65477d39..157ed8332 100644
--- a/MediaBrowser.Controller/Entities/BaseItemExtensions.cs
+++ b/MediaBrowser.Controller/Entities/BaseItemExtensions.cs
@@ -1,5 +1,7 @@
+#nullable enable
#pragma warning disable CS1591
+using System;
using System.Linq;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
@@ -64,9 +66,19 @@ namespace MediaBrowser.Controller.Entities
/// <param name="source">The source object.</param>
/// <param name="dest">The destination object.</param>
public static void DeepCopy<T, TU>(this T source, TU dest)
- where T : BaseItem
- where TU : BaseItem
+ where T : BaseItem
+ where TU : BaseItem
{
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ if (dest == null)
+ {
+ throw new ArgumentNullException(nameof(dest));
+ }
+
var destProps = typeof(TU).GetProperties().Where(x => x.CanWrite).ToList();
foreach (var sourceProp in typeof(T).GetProperties())
@@ -99,8 +111,8 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
/// <param name="source">The source object.</param>
public static TU DeepCopy<T, TU>(this T source)
- where T : BaseItem
- where TU : BaseItem, new()
+ where T : BaseItem
+ where TU : BaseItem, new()
{
var dest = new TU();
source.DeepCopy(dest);
diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs
index 76b6d39a9..bc5f38256 100644
--- a/MediaBrowser.Controller/Entities/CollectionFolder.cs
+++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs
@@ -61,7 +61,6 @@ namespace MediaBrowser.Controller.Entities
try
{
var result = XmlSerializer.DeserializeFromFile(typeof(LibraryOptions), GetLibraryOptionsPath(path)) as LibraryOptions;
-
if (result == null)
{
return new LibraryOptions();
@@ -271,7 +270,6 @@ namespace MediaBrowser.Controller.Entities
var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, directoryService)
{
FileInfo = FileSystem.GetDirectoryInfo(path),
- Path = path,
Parent = GetParent() as Folder,
CollectionType = CollectionType
};
@@ -355,9 +353,7 @@ namespace MediaBrowser.Controller.Entities
if (result.Count == 0)
{
- var folder = LibraryManager.FindByPath(path, true) as Folder;
-
- if (folder != null)
+ if (LibraryManager.FindByPath(path, true) is Folder folder)
{
result.Add(folder);
}
diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs
index cac5026f7..d74e6f9d8 100644
--- a/MediaBrowser.Controller/Entities/Folder.cs
+++ b/MediaBrowser.Controller/Entities/Folder.cs
@@ -2,7 +2,6 @@
using System;
using System.Collections.Generic;
-using System.Globalization;
using System.IO;
using System.Linq;
using System.Text.Json.Serialization;
@@ -1434,9 +1433,14 @@ namespace MediaBrowser.Controller.Entities
var linkedChildren = LinkedChildren;
foreach (var i in linkedChildren)
{
- if (i.ItemId.HasValue && i.ItemId.Value == itemId)
+ if (i.ItemId.HasValue)
{
- return true;
+ if (i.ItemId.Value == itemId)
+ {
+ return true;
+ }
+
+ continue;
}
var child = GetLinkedChild(i);
@@ -1764,20 +1768,15 @@ namespace MediaBrowser.Controller.Entities
{
EnableImages = false
}
- });
-
- double unplayedCount = unplayedQueryResult.TotalRecordCount;
+ }).TotalRecordCount;
- dto.UnplayedItemCount = unplayedQueryResult.TotalRecordCount;
+ dto.UnplayedItemCount = unplayedQueryResult;
- if (itemDto != null && itemDto.RecursiveItemCount.HasValue)
+ if (itemDto?.RecursiveItemCount > 0)
{
- if (itemDto.RecursiveItemCount.Value > 0)
- {
- var unplayedPercentage = (unplayedCount / itemDto.RecursiveItemCount.Value) * 100;
- dto.PlayedPercentage = 100 - unplayedPercentage;
- dto.Played = dto.PlayedPercentage.Value >= 100;
- }
+ var unplayedPercentage = ((double)unplayedQueryResult / itemDto.RecursiveItemCount.Value) * 100;
+ dto.PlayedPercentage = 100 - unplayedPercentage;
+ dto.Played = dto.PlayedPercentage.Value >= 100;
}
else
{
diff --git a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
index 05e4229ca..507f400f1 100644
--- a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
+++ b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
@@ -217,8 +217,7 @@ namespace MediaBrowser.Controller.Entities.Movies
private IEnumerable<BaseItem> FlattenItems(BaseItem item, List<Guid> expandedFolders)
{
- var boxset = item as BoxSet;
- if (boxset != null)
+ if (item is BoxSet boxset)
{
if (!expandedFolders.Contains(item.Id))
{
diff --git a/MediaBrowser.Controller/Entities/PeopleHelper.cs b/MediaBrowser.Controller/Entities/PeopleHelper.cs
index 1f3758a73..687ce1ec8 100644
--- a/MediaBrowser.Controller/Entities/PeopleHelper.cs
+++ b/MediaBrowser.Controller/Entities/PeopleHelper.cs
@@ -100,23 +100,5 @@ namespace MediaBrowser.Controller.Entities
existing.SetProviderId(id.Key, id.Value);
}
}
-
- public static bool ContainsPerson(List<PersonInfo> people, string name)
- {
- if (string.IsNullOrEmpty(name))
- {
- throw new ArgumentNullException(nameof(name));
- }
-
- foreach (var i in people)
- {
- if (string.Equals(i.Name, name, StringComparison.OrdinalIgnoreCase))
- {
- return true;
- }
- }
-
- return false;
- }
}
}
diff --git a/MediaBrowser.Controller/Entities/Photo.cs b/MediaBrowser.Controller/Entities/Photo.cs
index 2fc66176f..0f82f742f 100644
--- a/MediaBrowser.Controller/Entities/Photo.cs
+++ b/MediaBrowser.Controller/Entities/Photo.cs
@@ -24,8 +24,7 @@ namespace MediaBrowser.Controller.Entities
var parents = GetParents();
foreach (var parent in parents)
{
- var photoAlbum = parent as PhotoAlbum;
- if (photoAlbum != null)
+ if (parent is PhotoAlbum photoAlbum)
{
return photoAlbum;
}
diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs
index dc12fbbea..70663ef47 100644
--- a/MediaBrowser.Controller/Entities/TV/Episode.cs
+++ b/MediaBrowser.Controller/Entities/TV/Episode.cs
@@ -99,7 +99,15 @@ namespace MediaBrowser.Controller.Entities.TV
take--;
}
- list.InsertRange(0, seriesUserDataKeys.Take(take).Select(i => i + ParentIndexNumber.Value.ToString("000") + IndexNumber.Value.ToString("000")));
+ var newList = seriesUserDataKeys.GetRange(0, take);
+ var suffix = ParentIndexNumber.Value.ToString("000", CultureInfo.InvariantCulture) + IndexNumber.Value.ToString("000", CultureInfo.InvariantCulture);
+ for (int i = 0; i < take; i++)
+ {
+ newList[i] = newList[i] + suffix;
+ }
+
+ newList.AddRange(list);
+ list = newList;
}
return list;
diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs
index 93bdd6e70..5b8168d3d 100644
--- a/MediaBrowser.Controller/Entities/TV/Season.cs
+++ b/MediaBrowser.Controller/Entities/TV/Season.cs
@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
+using System.Globalization;
using System.Linq;
using System.Text.Json.Serialization;
using Jellyfin.Data.Entities;
@@ -56,7 +57,15 @@ namespace MediaBrowser.Controller.Entities.TV
var series = Series;
if (series != null)
{
- list.InsertRange(0, series.GetUserDataKeys().Select(i => i + (IndexNumber ?? 0).ToString("000")));
+ var newList = series.GetUserDataKeys();
+ var suffix = (IndexNumber ?? 0).ToString("000", CultureInfo.InvariantCulture);
+ for (int i = 0; i < newList.Count; i++)
+ {
+ newList[i] = newList[i] + suffix;
+ }
+
+ newList.AddRange(list);
+ list = newList;
}
return list;
diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs
index a410c1b66..06a405121 100644
--- a/MediaBrowser.Controller/Entities/TV/Series.cs
+++ b/MediaBrowser.Controller/Entities/TV/Series.cs
@@ -169,14 +169,12 @@ namespace MediaBrowser.Controller.Entities.TV
{
var list = base.GetUserDataKeys();
- var key = this.GetProviderId(MetadataProvider.Imdb);
- if (!string.IsNullOrEmpty(key))
+ if (this.TryGetProviderId(MetadataProvider.Imdb, out var key))
{
list.Insert(0, key);
}
- key = this.GetProviderId(MetadataProvider.Tvdb);
- if (!string.IsNullOrEmpty(key))
+ if (this.TryGetProviderId(MetadataProvider.Tvdb, out key))
{
list.Insert(0, key);
}
@@ -208,7 +206,7 @@ namespace MediaBrowser.Controller.Entities.TV
query.AncestorWithPresentationUniqueKey = null;
query.SeriesPresentationUniqueKey = seriesKey;
query.IncludeItemTypes = new[] { nameof(Season) };
- query.OrderBy = new[] { ItemSortBy.SortName }.Select(i => new ValueTuple<string, SortOrder>(i, SortOrder.Ascending)).ToArray();
+ query.OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) };
if (user != null && !user.DisplayMissingEpisodes)
{
@@ -228,7 +226,7 @@ namespace MediaBrowser.Controller.Entities.TV
query.SeriesPresentationUniqueKey = seriesKey;
if (query.OrderBy.Count == 0)
{
- query.OrderBy = new[] { ItemSortBy.SortName }.Select(i => new ValueTuple<string, SortOrder>(i, SortOrder.Ascending)).ToArray();
+ query.OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) };
}
if (query.IncludeItemTypes.Length == 0)
@@ -254,7 +252,7 @@ namespace MediaBrowser.Controller.Entities.TV
AncestorWithPresentationUniqueKey = null,
SeriesPresentationUniqueKey = seriesKey,
IncludeItemTypes = new[] { nameof(Episode), nameof(Season) },
- OrderBy = new[] { ItemSortBy.SortName }.Select(i => new ValueTuple<string, SortOrder>(i, SortOrder.Ascending)).ToArray(),
+ OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) },
DtoOptions = options
};
@@ -318,20 +316,13 @@ namespace MediaBrowser.Controller.Entities.TV
cancellationToken.ThrowIfCancellationRequested();
- var skipItem = false;
-
- var episode = item as Episode;
-
- if (episode != null
+ bool skipItem = item is Episode episode
&& refreshOptions.MetadataRefreshMode != MetadataRefreshMode.FullRefresh
&& !refreshOptions.ReplaceAllMetadata
&& episode.IsMissingEpisode
&& episode.LocationType == LocationType.Virtual
&& episode.PremiereDate.HasValue
- && (DateTime.UtcNow - episode.PremiereDate.Value).TotalDays > 30)
- {
- skipItem = true;
- }
+ && (DateTime.UtcNow - episode.PremiereDate.Value).TotalDays > 30;
if (!skipItem)
{
@@ -365,7 +356,7 @@ namespace MediaBrowser.Controller.Entities.TV
AncestorWithPresentationUniqueKey = queryFromSeries ? null : seriesKey,
SeriesPresentationUniqueKey = queryFromSeries ? seriesKey : null,
IncludeItemTypes = new[] { nameof(Episode) },
- OrderBy = new[] { ItemSortBy.SortName }.Select(i => new ValueTuple<string, SortOrder>(i, SortOrder.Ascending)).ToArray(),
+ OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) },
DtoOptions = options
};
if (user != null)
diff --git a/MediaBrowser.Controller/Entities/UserView.cs b/MediaBrowser.Controller/Entities/UserView.cs
index b1da4d64c..fec83dd94 100644
--- a/MediaBrowser.Controller/Entities/UserView.cs
+++ b/MediaBrowser.Controller/Entities/UserView.cs
@@ -75,10 +75,7 @@ namespace MediaBrowser.Controller.Entities
public override List<BaseItem> GetChildren(User user, bool includeLinkedChildren, InternalItemsQuery query)
{
- if (query == null)
- {
- query = new InternalItemsQuery(user);
- }
+ query ??= new InternalItemsQuery(user);
query.EnableTotalRecordCount = false;
var result = GetItemList(query);
diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
index 4e33a6bbd..78a64d8c9 100644
--- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs
+++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
@@ -217,8 +217,7 @@ namespace MediaBrowser.Controller.Entities
private QueryResult<BaseItem> GetMovieLatest(Folder parent, User user, InternalItemsQuery query)
{
- query.OrderBy = new[] { ItemSortBy.DateCreated, ItemSortBy.SortName }.Select(i => new ValueTuple<string, SortOrder>(i, SortOrder.Descending)).ToArray();
-
+ query.OrderBy = new[] { (ItemSortBy.DateCreated, SortOrder.Descending), (ItemSortBy.SortName, SortOrder.Descending) };
query.Recursive = true;
query.Parent = parent;
query.SetUser(user);
@@ -230,7 +229,7 @@ namespace MediaBrowser.Controller.Entities
private QueryResult<BaseItem> GetMovieResume(Folder parent, User user, InternalItemsQuery query)
{
- query.OrderBy = new[] { ItemSortBy.DatePlayed, ItemSortBy.SortName }.Select(i => new ValueTuple<string, SortOrder>(i, SortOrder.Descending)).ToArray();
+ query.OrderBy = new[] { (ItemSortBy.DatePlayed, SortOrder.Descending), (ItemSortBy.SortName, SortOrder.Descending) };
query.IsResumable = true;
query.Recursive = true;
query.Parent = parent;
@@ -327,8 +326,7 @@ namespace MediaBrowser.Controller.Entities
private QueryResult<BaseItem> GetTvLatest(Folder parent, User user, InternalItemsQuery query)
{
- query.OrderBy = new[] { ItemSortBy.DateCreated, ItemSortBy.SortName }.Select(i => new ValueTuple<string, SortOrder>(i, SortOrder.Descending)).ToArray();
-
+ query.OrderBy = new[] { (ItemSortBy.DateCreated, SortOrder.Descending), (ItemSortBy.SortName, SortOrder.Descending) };
query.Recursive = true;
query.Parent = parent;
query.SetUser(user);
@@ -356,7 +354,7 @@ namespace MediaBrowser.Controller.Entities
private QueryResult<BaseItem> GetTvResume(Folder parent, User user, InternalItemsQuery query)
{
- query.OrderBy = new[] { ItemSortBy.DatePlayed, ItemSortBy.SortName }.Select(i => new ValueTuple<string, SortOrder>(i, SortOrder.Descending)).ToArray();
+ query.OrderBy = new[] { (ItemSortBy.DatePlayed, SortOrder.Descending), (ItemSortBy.SortName, SortOrder.Descending) };
query.IsResumable = true;
query.Recursive = true;
query.Parent = parent;
diff --git a/MediaBrowser.Controller/Events/Updates/PluginUninstalledEventArgs.cs b/MediaBrowser.Controller/Events/Updates/PluginUninstalledEventArgs.cs
index a111e6d82..0f27be9bb 100644
--- a/MediaBrowser.Controller/Events/Updates/PluginUninstalledEventArgs.cs
+++ b/MediaBrowser.Controller/Events/Updates/PluginUninstalledEventArgs.cs
@@ -1,5 +1,4 @@
using Jellyfin.Data.Events;
-using MediaBrowser.Common.Plugins;
using MediaBrowser.Model.Plugins;
namespace MediaBrowser.Controller.Events.Updates
diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs
index 20bfa697e..6a65a8e47 100644
--- a/MediaBrowser.Controller/IServerApplicationHost.cs
+++ b/MediaBrowser.Controller/IServerApplicationHost.cs
@@ -3,10 +3,7 @@
using System;
using System.Collections.Generic;
using System.Net;
-using System.Threading;
-using System.Threading.Tasks;
using MediaBrowser.Common;
-using MediaBrowser.Common.Plugins;
using MediaBrowser.Model.System;
using Microsoft.AspNetCore.Http;
diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs
index 80fcf71f3..6d9b568da 100644
--- a/MediaBrowser.Controller/Library/ILibraryManager.cs
+++ b/MediaBrowser.Controller/Library/ILibraryManager.cs
@@ -467,6 +467,15 @@ namespace MediaBrowser.Controller.Library
void UpdatePeople(BaseItem item, List<PersonInfo> people);
/// <summary>
+ /// Asynchronously updates the people.
+ /// </summary>
+ /// <param name="item">The item.</param>
+ /// <param name="people">The people.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>The async task.</returns>
+ Task UpdatePeopleAsync(BaseItem item, List<PersonInfo> people, CancellationToken cancellationToken);
+
+ /// <summary>
/// Gets the item ids.
/// </summary>
/// <param name="query">The query.</param>
diff --git a/MediaBrowser.Controller/Library/ItemResolveArgs.cs b/MediaBrowser.Controller/Library/ItemResolveArgs.cs
index 12a311dc3..5f9aed341 100644
--- a/MediaBrowser.Controller/Library/ItemResolveArgs.cs
+++ b/MediaBrowser.Controller/Library/ItemResolveArgs.cs
@@ -14,14 +14,14 @@ namespace MediaBrowser.Controller.Library
/// These are arguments relating to the file system that are collected once and then referred to
/// whenever needed. Primarily for entity resolution.
/// </summary>
- public class ItemResolveArgs : EventArgs
+ public class ItemResolveArgs
{
/// <summary>
/// The _app paths.
/// </summary>
private readonly IServerApplicationPaths _appPaths;
- public IDirectoryService DirectoryService { get; private set; }
+ private LibraryOptions _libraryOptions;
/// <summary>
/// Initializes a new instance of the <see cref="ItemResolveArgs" /> class.
@@ -34,17 +34,18 @@ namespace MediaBrowser.Controller.Library
DirectoryService = directoryService;
}
+ public IDirectoryService DirectoryService { get; }
+
/// <summary>
/// Gets the file system children.
/// </summary>
/// <value>The file system children.</value>
public FileSystemMetadata[] FileSystemChildren { get; set; }
- public LibraryOptions LibraryOptions { get; set; }
-
- public LibraryOptions GetLibraryOptions()
+ public LibraryOptions LibraryOptions
{
- return LibraryOptions ?? (LibraryOptions = Parent == null ? new LibraryOptions() : BaseItem.LibraryManager.GetLibraryOptions(Parent));
+ get => _libraryOptions ??= Parent == null ? new LibraryOptions() : BaseItem.LibraryManager.GetLibraryOptions(Parent);
+ set => _libraryOptions = value;
}
/// <summary>
@@ -60,10 +61,10 @@ namespace MediaBrowser.Controller.Library
public FileSystemMetadata FileInfo { get; set; }
/// <summary>
- /// Gets or sets the path.
+ /// Gets the path.
/// </summary>
/// <value>The path.</value>
- public string Path { get; set; }
+ public string Path => FileInfo.FullName;
/// <summary>
/// Gets a value indicating whether this instance is directory.
@@ -139,7 +140,7 @@ namespace MediaBrowser.Controller.Library
/// Adds the additional location.
/// </summary>
/// <param name="path">The path.</param>
- /// <exception cref="ArgumentNullException"></exception>
+ /// <exception cref="ArgumentNullException"><paramref name="path"/> is <c>null</c> or empty.</exception>
public void AddAdditionalLocation(string path)
{
if (string.IsNullOrEmpty(path))
@@ -147,11 +148,7 @@ namespace MediaBrowser.Controller.Library
throw new ArgumentException("The path was empty or null.", nameof(path));
}
- if (AdditionalLocations == null)
- {
- AdditionalLocations = new List<string>();
- }
-
+ AdditionalLocations ??= new List<string>();
AdditionalLocations.Add(path);
}
@@ -175,7 +172,7 @@ namespace MediaBrowser.Controller.Library
/// </summary>
/// <param name="name">The name.</param>
/// <returns>FileSystemInfo.</returns>
- /// <exception cref="ArgumentNullException"></exception>
+ /// <exception cref="ArgumentNullException"><paramref name="name"/> is <c>null</c> or empty.</exception>
public FileSystemMetadata GetFileSystemEntryByName(string name)
{
if (string.IsNullOrEmpty(name))
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index 92b9a8c7e..2b5364775 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -10,8 +10,6 @@ using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using Jellyfin.Data.Enums;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Extensions;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto;
@@ -27,9 +25,7 @@ namespace MediaBrowser.Controller.MediaEncoding
private static readonly CultureInfo _usCulture = new CultureInfo("en-US");
private readonly IMediaEncoder _mediaEncoder;
- private readonly IFileSystem _fileSystem;
private readonly ISubtitleEncoder _subtitleEncoder;
- private readonly IConfiguration _configuration;
private static readonly string[] _videoProfiles = new[]
{
@@ -44,14 +40,10 @@ namespace MediaBrowser.Controller.MediaEncoding
public EncodingHelper(
IMediaEncoder mediaEncoder,
- IFileSystem fileSystem,
- ISubtitleEncoder subtitleEncoder,
- IConfiguration configuration)
+ ISubtitleEncoder subtitleEncoder)
{
_mediaEncoder = mediaEncoder;
- _fileSystem = fileSystem;
_subtitleEncoder = subtitleEncoder;
- _configuration = configuration;
}
public string GetH264Encoder(EncodingJobInfo state, EncodingOptions encodingOptions)
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
index dacd6dea6..d47a689f4 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
@@ -9,7 +9,6 @@ using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.IO;
using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Session;
diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
index 5cbb57990..05dd1a69b 100644
--- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
+++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
@@ -7,7 +7,6 @@ using System.Threading.Tasks;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.IO;
using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.System;
diff --git a/MediaBrowser.Controller/MediaEncoding/JobLogger.cs b/MediaBrowser.Controller/MediaEncoding/JobLogger.cs
index cc8820f39..227c5f258 100644
--- a/MediaBrowser.Controller/MediaEncoding/JobLogger.cs
+++ b/MediaBrowser.Controller/MediaEncoding/JobLogger.cs
@@ -3,7 +3,6 @@
using System;
using System.Globalization;
using System.IO;
-using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
diff --git a/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs b/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs
index 281d50372..89e01c08b 100644
--- a/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs
+++ b/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs
@@ -1,10 +1,5 @@
#pragma warning disable CS1591
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using MediaBrowser.Model.IO;
namespace MediaBrowser.Controller.MediaEncoding
{
diff --git a/MediaBrowser.Controller/Persistence/IItemRepository.cs b/MediaBrowser.Controller/Persistence/IItemRepository.cs
index 45c6805f0..ed473c749 100644
--- a/MediaBrowser.Controller/Persistence/IItemRepository.cs
+++ b/MediaBrowser.Controller/Persistence/IItemRepository.cs
@@ -153,8 +153,7 @@ namespace MediaBrowser.Controller.Persistence
/// <summary>
/// Updates the inherited values.
/// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- void UpdateInheritedValues(CancellationToken cancellationToken);
+ void UpdateInheritedValues();
int GetCount(InternalItemsQuery query);
diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs
index 977b14cb0..3c93cfc79 100644
--- a/MediaBrowser.Controller/Playlists/Playlist.cs
+++ b/MediaBrowser.Controller/Playlists/Playlist.cs
@@ -14,7 +14,6 @@ using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
namespace MediaBrowser.Controller.Playlists
@@ -127,10 +126,7 @@ namespace MediaBrowser.Controller.Playlists
private List<BaseItem> GetPlayableItems(User user, InternalItemsQuery query)
{
- if (query == null)
- {
- query = new InternalItemsQuery(user);
- }
+ query ??= new InternalItemsQuery(user);
query.IsFolder = false;
@@ -164,7 +160,7 @@ namespace MediaBrowser.Controller.Playlists
Recursive = true,
IncludeItemTypes = new[] { nameof(Audio) },
GenreIds = new[] { musicGenre.Id },
- OrderBy = new[] { ItemSortBy.AlbumArtist, ItemSortBy.Album, ItemSortBy.SortName }.Select(i => new ValueTuple<string, SortOrder>(i, SortOrder.Ascending)).ToArray(),
+ OrderBy = new[] { (ItemSortBy.AlbumArtist, SortOrder.Ascending), (ItemSortBy.Album, SortOrder.Ascending), (ItemSortBy.SortName, SortOrder.Ascending) },
DtoOptions = options
});
}
@@ -176,7 +172,7 @@ namespace MediaBrowser.Controller.Playlists
Recursive = true,
IncludeItemTypes = new[] { nameof(Audio) },
ArtistIds = new[] { musicArtist.Id },
- OrderBy = new[] { ItemSortBy.AlbumArtist, ItemSortBy.Album, ItemSortBy.SortName }.Select(i => new ValueTuple<string, SortOrder>(i, SortOrder.Ascending)).ToArray(),
+ OrderBy = new[] { (ItemSortBy.AlbumArtist, SortOrder.Ascending), (ItemSortBy.Album, SortOrder.Ascending), (ItemSortBy.SortName, SortOrder.Ascending) },
DtoOptions = options
});
}
diff --git a/MediaBrowser.Controller/Providers/IRemoteImageProvider.cs b/MediaBrowser.Controller/Providers/IRemoteImageProvider.cs
index ee8f5b860..de1631dcf 100644
--- a/MediaBrowser.Controller/Providers/IRemoteImageProvider.cs
+++ b/MediaBrowser.Controller/Providers/IRemoteImageProvider.cs
@@ -2,7 +2,6 @@ using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
-using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
diff --git a/MediaBrowser.Controller/Providers/IRemoteSearchProvider.cs b/MediaBrowser.Controller/Providers/IRemoteSearchProvider.cs
index 9592baa7c..e401ed211 100644
--- a/MediaBrowser.Controller/Providers/IRemoteSearchProvider.cs
+++ b/MediaBrowser.Controller/Providers/IRemoteSearchProvider.cs
@@ -3,7 +3,6 @@
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
-using MediaBrowser.Common.Net;
namespace MediaBrowser.Controller.Providers
{
diff --git a/MediaBrowser.Controller/Providers/ItemInfo.cs b/MediaBrowser.Controller/Providers/ItemInfo.cs
index b50def043..3a97127ea 100644
--- a/MediaBrowser.Controller/Providers/ItemInfo.cs
+++ b/MediaBrowser.Controller/Providers/ItemInfo.cs
@@ -14,8 +14,7 @@ namespace MediaBrowser.Controller.Providers
ContainingFolderPath = item.ContainingFolderPath;
IsInMixedFolder = item.IsInMixedFolder;
- var video = item as Video;
- if (video != null)
+ if (item is Video video)
{
VideoType = video.VideoType;
IsPlaceHolder = video.IsPlaceHolder;
diff --git a/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs b/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs
index b92b83701..db0ef7072 100644
--- a/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs
+++ b/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs
@@ -45,10 +45,7 @@ namespace MediaBrowser.Controller.Providers
if (copy.RefreshPaths != null && copy.RefreshPaths.Length > 0)
{
- if (RefreshPaths == null)
- {
- RefreshPaths = Array.Empty<string>();
- }
+ RefreshPaths ??= Array.Empty<string>();
RefreshPaths = copy.RefreshPaths.ToArray();
}
diff --git a/MediaBrowser.Controller/Providers/MetadataResult.cs b/MediaBrowser.Controller/Providers/MetadataResult.cs
index 864cb3050..98c7eadfe 100644
--- a/MediaBrowser.Controller/Providers/MetadataResult.cs
+++ b/MediaBrowser.Controller/Providers/MetadataResult.cs
@@ -37,10 +37,7 @@ namespace MediaBrowser.Controller.Providers
public void AddPerson(PersonInfo p)
{
- if (People == null)
- {
- People = new List<PersonInfo>();
- }
+ People ??= new List<PersonInfo>();
PeopleHelper.AddPerson(People, p);
}
@@ -54,16 +51,15 @@ namespace MediaBrowser.Controller.Providers
{
People = new List<PersonInfo>();
}
-
- People.Clear();
+ else
+ {
+ People.Clear();
+ }
}
public UserItemData GetOrAddUserData(string userId)
{
- if (UserDataList == null)
- {
- UserDataList = new List<UserItemData>();
- }
+ UserDataList ??= new List<UserItemData>();
UserItemData userData = null;
diff --git a/MediaBrowser.Controller/Resolvers/BaseItemResolver.cs b/MediaBrowser.Controller/Resolvers/BaseItemResolver.cs
index 25128a5cd..a904c7424 100644
--- a/MediaBrowser.Controller/Resolvers/BaseItemResolver.cs
+++ b/MediaBrowser.Controller/Resolvers/BaseItemResolver.cs
@@ -11,6 +11,12 @@ namespace MediaBrowser.Controller.Resolvers
where T : BaseItem, new()
{
/// <summary>
+ /// Gets the priority.
+ /// </summary>
+ /// <value>The priority.</value>
+ public virtual ResolverPriority Priority => ResolverPriority.First;
+
+ /// <summary>
/// Resolves the specified args.
/// </summary>
/// <param name="args">The args.</param>
@@ -21,12 +27,6 @@ namespace MediaBrowser.Controller.Resolvers
}
/// <summary>
- /// Gets the priority.
- /// </summary>
- /// <value>The priority.</value>
- public virtual ResolverPriority Priority => ResolverPriority.First;
-
- /// <summary>
/// Sets initial values on the newly resolved item.
/// </summary>
/// <param name="item">The item.</param>
diff --git a/MediaBrowser.Controller/Session/AuthenticationRequest.cs b/MediaBrowser.Controller/Session/AuthenticationRequest.cs
index cc321fd22..8c3ac58f2 100644
--- a/MediaBrowser.Controller/Session/AuthenticationRequest.cs
+++ b/MediaBrowser.Controller/Session/AuthenticationRequest.cs
@@ -12,6 +12,7 @@ namespace MediaBrowser.Controller.Session
public string Password { get; set; }
+ [Obsolete("Send full password in Password field")]
public string PasswordSha1 { get; set; }
public string App { get; set; }
diff --git a/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs b/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs
index feb26bc10..6d63286ef 100644
--- a/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs
+++ b/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs
@@ -2,7 +2,6 @@
using System;
using System.Collections.Generic;
-using System.IO;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Entities;
diff --git a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs
index 5f620634f..32e5ac761 100644
--- a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs
+++ b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs
@@ -468,8 +468,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
{
var val = reader.ReadElementContentAsString();
- var hasDisplayOrder = item as IHasDisplayOrder;
- if (hasDisplayOrder != null)
+ if (item is IHasDisplayOrder hasDisplayOrder)
{
if (!string.IsNullOrWhiteSpace(val))
{
diff --git a/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs
index dfbce5f49..98ed3dcf7 100644
--- a/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs
+++ b/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs
@@ -296,8 +296,7 @@ namespace MediaBrowser.LocalMetadata.Savers
writer.WriteEndElement();
}
- var hasDisplayOrder = item as IHasDisplayOrder;
- if (hasDisplayOrder != null && !string.IsNullOrEmpty(hasDisplayOrder.DisplayOrder))
+ if (item is IHasDisplayOrder hasDisplayOrder && !string.IsNullOrEmpty(hasDisplayOrder.DisplayOrder))
{
writer.WriteElementString("DisplayOrder", hasDisplayOrder.DisplayOrder);
}
@@ -312,8 +311,7 @@ namespace MediaBrowser.LocalMetadata.Savers
writer.WriteElementString("ProductionYear", item.ProductionYear.Value.ToString(_usCulture));
}
- var hasAspectRatio = item as IHasAspectRatio;
- if (hasAspectRatio != null)
+ if (item is IHasAspectRatio hasAspectRatio)
{
if (!string.IsNullOrEmpty(hasAspectRatio.AspectRatio))
{
diff --git a/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs b/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs
index 9108d9649..6ebaa4fff 100644
--- a/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs
+++ b/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs
@@ -61,33 +61,25 @@ namespace MediaBrowser.MediaEncoding.BdInfo
foreach (var stream in playlist.SortedStreams)
{
- var videoStream = stream as TSVideoStream;
-
- if (videoStream != null)
+ if (stream is TSVideoStream videoStream)
{
AddVideoStream(mediaStreams, videoStream);
continue;
}
- var audioStream = stream as TSAudioStream;
-
- if (audioStream != null)
+ if (stream is TSAudioStream audioStream)
{
AddAudioStream(mediaStreams, audioStream);
continue;
}
- var textStream = stream as TSTextStream;
-
- if (textStream != null)
+ if (stream is TSTextStream textStream)
{
AddSubtitleStream(mediaStreams, textStream);
continue;
}
- var graphicsStream = stream as TSGraphicsStream;
-
- if (graphicsStream != null)
+ if (stream is TSGraphicsStream graphicsStream)
{
AddSubtitleStream(mediaStreams, graphicsStream);
}
diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
index 205933ae2..62c0c0bb1 100644
--- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
@@ -16,7 +16,6 @@ using MediaBrowser.Common.Json;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.MediaEncoding.Probing;
-using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
@@ -53,7 +52,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
private readonly IServerConfigurationManager _configurationManager;
private readonly IFileSystem _fileSystem;
private readonly ILocalizationManager _localization;
- private readonly Lazy<EncodingHelper> _encodingHelperFactory;
private readonly string _startupOptionFFmpegPath;
private readonly SemaphoreSlim _thumbnailResourcePool = new SemaphoreSlim(2, 2);
@@ -77,14 +75,12 @@ namespace MediaBrowser.MediaEncoding.Encoder
IServerConfigurationManager configurationManager,
IFileSystem fileSystem,
ILocalizationManager localization,
- Lazy<EncodingHelper> encodingHelperFactory,
IConfiguration config)
{
_logger = logger;
_configurationManager = configurationManager;
_fileSystem = fileSystem;
_localization = localization;
- _encodingHelperFactory = encodingHelperFactory;
_startupOptionFFmpegPath = config.GetValue<string>(Controller.Extensions.ConfigurationExtensions.FfmpegPathKey) ?? string.Empty;
_jsonSerializerOptions = JsonDefaults.Options;
}
diff --git a/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs b/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs
index 8a7c032c5..7b7744163 100644
--- a/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs
+++ b/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs
@@ -1,6 +1,5 @@
using System.Collections.Generic;
using System.Text.Json.Serialization;
-using MediaBrowser.Common.Json.Converters;
namespace MediaBrowser.MediaEncoding.Probing
{
diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
index a87104cd6..884ec0a29 100644
--- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
+++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
@@ -29,7 +29,7 @@ namespace MediaBrowser.MediaEncoding.Probing
private readonly ILogger _logger;
private readonly ILocalizationManager _localization;
- private List<string> _splitWhiteList = null;
+ private string[] _splitWhiteList;
public ProbeResultNormalizer(ILogger logger, ILocalizationManager localization)
{
@@ -37,6 +37,8 @@ namespace MediaBrowser.MediaEncoding.Probing
_localization = localization;
}
+ private IReadOnlyList<string> SplitWhitelist => _splitWhiteList ??= new string[] { "AC/DC" };
+
public MediaInfo GetMediaInfo(InternalMediaInfoResult data, VideoType? videoType, bool isAudio, string path, MediaProtocol protocol)
{
var info = new MediaInfo
@@ -57,7 +59,7 @@ namespace MediaBrowser.MediaEncoding.Probing
.Where(i => i.Type != MediaStreamType.Subtitle || !string.IsNullOrWhiteSpace(i.Codec))
.ToList();
- info.MediaAttachments = internalStreams.Select(s => GetMediaAttachment(s))
+ info.MediaAttachments = internalStreams.Select(GetMediaAttachment)
.Where(i => i != null)
.ToList();
@@ -1187,43 +1189,28 @@ namespace MediaBrowser.MediaEncoding.Probing
FetchStudios(audio, tags, "label");
// These support mulitple values, but for now we only store the first.
- var mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Album Artist Id"));
- if (mb == null)
- {
- mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ALBUMARTISTID"));
- }
+ var mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Album Artist Id"))
+ ?? GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ALBUMARTISTID"));
audio.SetProviderId(MetadataProvider.MusicBrainzAlbumArtist, mb);
- mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Artist Id"));
- if (mb == null)
- {
- mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ARTISTID"));
- }
+ mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Artist Id"))
+ ?? GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ARTISTID"));
audio.SetProviderId(MetadataProvider.MusicBrainzArtist, mb);
- mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Album Id"));
- if (mb == null)
- {
- mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ALBUMID"));
- }
+ mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Album Id"))
+ ?? GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ALBUMID"));
audio.SetProviderId(MetadataProvider.MusicBrainzAlbum, mb);
- mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Release Group Id"));
- if (mb == null)
- {
- mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_RELEASEGROUPID"));
- }
+ mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Release Group Id"))
+ ?? GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_RELEASEGROUPID"));
audio.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, mb);
- mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Release Track Id"));
- if (mb == null)
- {
- mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_RELEASETRACKID"));
- }
+ mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Release Track Id"))
+ ?? GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_RELEASETRACKID"));
audio.SetProviderId(MetadataProvider.MusicBrainzTrack, mb);
}
@@ -1269,7 +1256,7 @@ namespace MediaBrowser.MediaEncoding.Probing
var artistsFound = new List<string>();
- foreach (var whitelistArtist in GetSplitWhitelist())
+ foreach (var whitelistArtist in SplitWhitelist)
{
var originalVal = val;
val = val.Replace(whitelistArtist, "|", StringComparison.OrdinalIgnoreCase);
@@ -1288,19 +1275,6 @@ namespace MediaBrowser.MediaEncoding.Probing
return artistsFound;
}
- private IEnumerable<string> GetSplitWhitelist()
- {
- if (_splitWhiteList == null)
- {
- _splitWhiteList = new List<string>
- {
- "AC/DC"
- };
- }
-
- return _splitWhiteList;
- }
-
/// <summary>
/// Gets the studios from the tags collection.
/// </summary>
diff --git a/MediaBrowser.Model/Dlna/ContainerProfile.cs b/MediaBrowser.Model/Dlna/ContainerProfile.cs
index d83c8f2f3..740966088 100644
--- a/MediaBrowser.Model/Dlna/ContainerProfile.cs
+++ b/MediaBrowser.Model/Dlna/ContainerProfile.cs
@@ -1,4 +1,3 @@
-#nullable disable
#pragma warning disable CS1591
using System;
@@ -9,25 +8,15 @@ namespace MediaBrowser.Model.Dlna
{
public class ContainerProfile
{
- public ContainerProfile()
- {
- Conditions = Array.Empty<ProfileCondition>();
- }
-
[XmlAttribute("type")]
public DlnaProfileType Type { get; set; }
- public ProfileCondition[] Conditions { get; set; }
+ public ProfileCondition[]? Conditions { get; set; } = Array.Empty<ProfileCondition>();
[XmlAttribute("container")]
- public string Container { get; set; }
-
- public string[] GetContainers()
- {
- return SplitValue(Container);
- }
+ public string Container { get; set; } = string.Empty;
- public static string[] SplitValue(string value)
+ public static string[] SplitValue(string? value)
{
if (string.IsNullOrEmpty(value))
{
@@ -37,14 +26,14 @@ namespace MediaBrowser.Model.Dlna
return value.Split(',', StringSplitOptions.RemoveEmptyEntries);
}
- public bool ContainsContainer(string container)
+ public bool ContainsContainer(string? container)
{
- var containers = GetContainers();
+ var containers = SplitValue(Container);
return ContainsContainer(containers, container);
}
- public static bool ContainsContainer(string profileContainers, string inputContainer)
+ public static bool ContainsContainer(string? profileContainers, string? inputContainer)
{
var isNegativeList = false;
if (profileContainers != null && profileContainers.StartsWith('-'))
@@ -56,46 +45,30 @@ namespace MediaBrowser.Model.Dlna
return ContainsContainer(SplitValue(profileContainers), isNegativeList, inputContainer);
}
- public static bool ContainsContainer(string[] profileContainers, string inputContainer)
+ public static bool ContainsContainer(string[]? profileContainers, string? inputContainer)
{
return ContainsContainer(profileContainers, false, inputContainer);
}
- public static bool ContainsContainer(string[] profileContainers, bool isNegativeList, string inputContainer)
+ public static bool ContainsContainer(string[]? profileContainers, bool isNegativeList, string? inputContainer)
{
- if (profileContainers.Length == 0)
+ if (profileContainers == null || profileContainers.Length == 0)
{
+ // Empty profiles always support all containers/codecs
return true;
}
- if (isNegativeList)
- {
- var allInputContainers = SplitValue(inputContainer);
+ var allInputContainers = SplitValue(inputContainer);
- foreach (var container in allInputContainers)
- {
- if (profileContainers.Contains(container, StringComparer.OrdinalIgnoreCase))
- {
- return false;
- }
- }
-
- return true;
- }
- else
+ foreach (var container in allInputContainers)
{
- var allInputContainers = SplitValue(inputContainer);
-
- foreach (var container in allInputContainers)
+ if (profileContainers.Contains(container, StringComparer.OrdinalIgnoreCase))
{
- if (profileContainers.Contains(container, StringComparer.OrdinalIgnoreCase))
- {
- return true;
- }
+ return !isNegativeList;
}
-
- return false;
}
+
+ return isNegativeList;
}
}
}
diff --git a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs
index ec106f105..600a44157 100644
--- a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs
+++ b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs
@@ -10,14 +10,8 @@ namespace MediaBrowser.Model.Dlna
{
public class ContentFeatureBuilder
{
- private readonly DeviceProfile _profile;
-
- public ContentFeatureBuilder(DeviceProfile profile)
- {
- _profile = profile;
- }
-
- public string BuildImageHeader(
+ public static string BuildImageHeader(
+ DeviceProfile profile,
string container,
int? width,
int? height,
@@ -38,27 +32,31 @@ namespace MediaBrowser.Model.Dlna
";DLNA.ORG_FLAGS={0}",
DlnaMaps.FlagsToString(flagValue));
- ResponseProfile mediaProfile = _profile.GetImageMediaProfile(
- container,
- width,
- height);
-
if (string.IsNullOrEmpty(orgPn))
{
+ ResponseProfile mediaProfile = profile.GetImageMediaProfile(
+ container,
+ width,
+ height);
+
orgPn = mediaProfile?.OrgPn;
+
+ if (string.IsNullOrEmpty(orgPn))
+ {
+ orgPn = GetImageOrgPnValue(container, width, height);
+ }
}
if (string.IsNullOrEmpty(orgPn))
{
- orgPn = GetImageOrgPnValue(container, width, height);
+ return orgOp.TrimStart(';') + orgCi + dlnaflags;
}
- string contentFeatures = string.IsNullOrEmpty(orgPn) ? string.Empty : "DLNA.ORG_PN=" + orgPn;
-
- return (contentFeatures + orgOp + orgCi + dlnaflags).Trim(';');
+ return "DLNA.ORG_PN=" + orgPn + orgOp + orgCi + dlnaflags;
}
- public string BuildAudioHeader(
+ public static string BuildAudioHeader(
+ DeviceProfile profile,
string container,
string audioCodec,
int? audioBitrate,
@@ -94,7 +92,7 @@ namespace MediaBrowser.Model.Dlna
";DLNA.ORG_FLAGS={0}",
DlnaMaps.FlagsToString(flagValue));
- ResponseProfile mediaProfile = _profile.GetAudioMediaProfile(
+ ResponseProfile mediaProfile = profile.GetAudioMediaProfile(
container,
audioCodec,
audioChannels,
@@ -109,12 +107,16 @@ namespace MediaBrowser.Model.Dlna
orgPn = GetAudioOrgPnValue(container, audioBitrate, audioSampleRate, audioChannels);
}
- string contentFeatures = string.IsNullOrEmpty(orgPn) ? string.Empty : "DLNA.ORG_PN=" + orgPn;
+ if (string.IsNullOrEmpty(orgPn))
+ {
+ return orgOp.TrimStart(';') + orgCi + dlnaflags;
+ }
- return (contentFeatures + orgOp + orgCi + dlnaflags).Trim(';');
+ return "DLNA.ORG_PN=" + orgPn + orgOp + orgCi + dlnaflags;
}
- public List<string> BuildVideoHeader(
+ public static List<string> BuildVideoHeader(
+ DeviceProfile profile,
string container,
string videoCodec,
string audioCodec,
@@ -163,7 +165,7 @@ namespace MediaBrowser.Model.Dlna
";DLNA.ORG_FLAGS={0}",
DlnaMaps.FlagsToString(flagValue));
- ResponseProfile mediaProfile = _profile.GetVideoMediaProfile(
+ ResponseProfile mediaProfile = profile.GetVideoMediaProfile(
container,
audioCodec,
videoCodec,
@@ -192,9 +194,9 @@ namespace MediaBrowser.Model.Dlna
}
else
{
- foreach (string s in GetVideoOrgPnValue(container, videoCodec, audioCodec, width, height, timestamp))
+ foreach (var s in GetVideoOrgPnValue(container, videoCodec, audioCodec, width, height, timestamp))
{
- orgPnValues.Add(s);
+ orgPnValues.Add(s.ToString());
break;
}
}
@@ -203,20 +205,20 @@ namespace MediaBrowser.Model.Dlna
foreach (string orgPn in orgPnValues)
{
- string contentFeatures = string.IsNullOrEmpty(orgPn) ? string.Empty : "DLNA.ORG_PN=" + orgPn;
-
- var value = (contentFeatures + orgOp + orgCi + dlnaflags).Trim(';');
-
- contentFeatureList.Add(value);
+ if (string.IsNullOrEmpty(orgPn))
+ {
+ contentFeatureList.Add(orgOp.TrimStart(';') + orgCi + dlnaflags);
+ continue;
+ }
+ else
+ {
+ contentFeatureList.Add("DLNA.ORG_PN=" + orgPn + orgCi + dlnaflags);
+ }
}
if (orgPnValues.Count == 0)
{
- string contentFeatures = string.Empty;
-
- var value = (contentFeatures + orgOp + orgCi + dlnaflags).Trim(';');
-
- contentFeatureList.Add(value);
+ contentFeatureList.Add(orgOp.TrimStart(';') + orgCi + dlnaflags);
}
return contentFeatureList;
@@ -224,19 +226,14 @@ namespace MediaBrowser.Model.Dlna
private static string GetImageOrgPnValue(string container, int? width, int? height)
{
- MediaFormatProfile? format = new MediaFormatProfileResolver()
- .ResolveImageFormat(
- container,
- width,
- height);
+ MediaFormatProfile? format = MediaFormatProfileResolver.ResolveImageFormat(container, width, height);
return format.HasValue ? format.Value.ToString() : null;
}
private static string GetAudioOrgPnValue(string container, int? audioBitrate, int? audioSampleRate, int? audioChannels)
{
- MediaFormatProfile? format = new MediaFormatProfileResolver()
- .ResolveAudioFormat(
+ MediaFormatProfile? format = MediaFormatProfileResolver.ResolveAudioFormat(
container,
audioBitrate,
audioSampleRate,
@@ -245,9 +242,9 @@ namespace MediaBrowser.Model.Dlna
return format.HasValue ? format.Value.ToString() : null;
}
- private static string[] GetVideoOrgPnValue(string container, string videoCodec, string audioCodec, int? width, int? height, TransportStreamTimestamp timestamp)
+ private static MediaFormatProfile[] GetVideoOrgPnValue(string container, string videoCodec, string audioCodec, int? width, int? height, TransportStreamTimestamp timestamp)
{
- return new MediaFormatProfileResolver().ResolveVideoFormat(container, videoCodec, audioCodec, width, height, timestamp);
+ return MediaFormatProfileResolver.ResolveVideoFormat(container, videoCodec, audioCodec, width, height, timestamp);
}
}
}
diff --git a/MediaBrowser.Model/Dlna/DeviceProfile.cs b/MediaBrowser.Model/Dlna/DeviceProfile.cs
index ff5186658..feb3d880e 100644
--- a/MediaBrowser.Model/Dlna/DeviceProfile.cs
+++ b/MediaBrowser.Model/Dlna/DeviceProfile.cs
@@ -1,6 +1,6 @@
-#nullable disable
#pragma warning disable CA1819 // Properties should not return arrays
using System;
+using System.ComponentModel;
using System.Linq;
using System.Xml.Serialization;
using MediaBrowser.Model.MediaInfo;
@@ -8,226 +8,219 @@ using MediaBrowser.Model.MediaInfo;
namespace MediaBrowser.Model.Dlna
{
/// <summary>
- /// Defines the <see cref="DeviceProfile" />.
+ /// A <see cref="DeviceProfile" /> represents a set of metadata which determines which content a certain device is able to play.
+ /// <br/>
+ /// Specifically, it defines the supported <see cref="ContainerProfiles">containers</see> and
+ /// <see cref="CodecProfiles">codecs</see> (video and/or audio, including codec profiles and levels)
+ /// the device is able to direct play (without transcoding or remuxing),
+ /// as well as which <see cref="TranscodingProfiles">containers/codecs to transcode to</see> in case it isn't.
/// </summary>
[XmlRoot("Profile")]
public class DeviceProfile
{
/// <summary>
- /// Initializes a new instance of the <see cref="DeviceProfile"/> class.
+ /// Gets or sets the name of this device profile.
/// </summary>
- public DeviceProfile()
- {
- DirectPlayProfiles = Array.Empty<DirectPlayProfile>();
- TranscodingProfiles = Array.Empty<TranscodingProfile>();
- ResponseProfiles = Array.Empty<ResponseProfile>();
- CodecProfiles = Array.Empty<CodecProfile>();
- ContainerProfiles = Array.Empty<ContainerProfile>();
- SubtitleProfiles = Array.Empty<SubtitleProfile>();
-
- XmlRootAttributes = Array.Empty<XmlAttribute>();
-
- SupportedMediaTypes = "Audio,Photo,Video";
- MaxStreamingBitrate = 8000000;
- MaxStaticBitrate = 8000000;
- MusicStreamingTranscodingBitrate = 128000;
- }
-
- /// <summary>
- /// Gets or sets the Name.
- /// </summary>
- public string Name { get; set; }
+ public string? Name { get; set; }
/// <summary>
/// Gets or sets the Id.
/// </summary>
[XmlIgnore]
- public string Id { get; set; }
+ public string? Id { get; set; }
/// <summary>
/// Gets or sets the Identification.
/// </summary>
- public DeviceIdentification Identification { get; set; }
+ public DeviceIdentification? Identification { get; set; }
/// <summary>
- /// Gets or sets the FriendlyName.
+ /// Gets or sets the friendly name of the device profile, which can be shown to users.
/// </summary>
- public string FriendlyName { get; set; }
+ public string? FriendlyName { get; set; }
/// <summary>
- /// Gets or sets the Manufacturer.
+ /// Gets or sets the manufacturer of the device which this profile represents.
/// </summary>
- public string Manufacturer { get; set; }
+ public string? Manufacturer { get; set; }
/// <summary>
- /// Gets or sets the ManufacturerUrl.
+ /// Gets or sets an url for the manufacturer of the device which this profile represents.
/// </summary>
- public string ManufacturerUrl { get; set; }
+ public string? ManufacturerUrl { get; set; }
/// <summary>
- /// Gets or sets the ModelName.
+ /// Gets or sets the model name of the device which this profile represents.
/// </summary>
- public string ModelName { get; set; }
+ public string? ModelName { get; set; }
/// <summary>
- /// Gets or sets the ModelDescription.
+ /// Gets or sets the model description of the device which this profile represents.
/// </summary>
- public string ModelDescription { get; set; }
+ public string? ModelDescription { get; set; }
/// <summary>
- /// Gets or sets the ModelNumber.
+ /// Gets or sets the model number of the device which this profile represents.
/// </summary>
- public string ModelNumber { get; set; }
+ public string? ModelNumber { get; set; }
/// <summary>
/// Gets or sets the ModelUrl.
/// </summary>
- public string ModelUrl { get; set; }
+ public string? ModelUrl { get; set; }
/// <summary>
- /// Gets or sets the SerialNumber.
+ /// Gets or sets the serial number of the device which this profile represents.
/// </summary>
- public string SerialNumber { get; set; }
+ public string? SerialNumber { get; set; }
/// <summary>
/// Gets or sets a value indicating whether EnableAlbumArtInDidl.
/// </summary>
+ [DefaultValue(false)]
public bool EnableAlbumArtInDidl { get; set; }
/// <summary>
/// Gets or sets a value indicating whether EnableSingleAlbumArtLimit.
/// </summary>
+ [DefaultValue(false)]
public bool EnableSingleAlbumArtLimit { get; set; }
/// <summary>
/// Gets or sets a value indicating whether EnableSingleSubtitleLimit.
/// </summary>
+ [DefaultValue(false)]
public bool EnableSingleSubtitleLimit { get; set; }
/// <summary>
/// Gets or sets the SupportedMediaTypes.
/// </summary>
- public string SupportedMediaTypes { get; set; }
+ public string SupportedMediaTypes { get; set; } = "Audio,Photo,Video";
/// <summary>
/// Gets or sets the UserId.
/// </summary>
- public string UserId { get; set; }
+ public string? UserId { get; set; }
/// <summary>
/// Gets or sets the AlbumArtPn.
/// </summary>
- public string AlbumArtPn { get; set; }
+ public string? AlbumArtPn { get; set; }
/// <summary>
/// Gets or sets the MaxAlbumArtWidth.
/// </summary>
- public int MaxAlbumArtWidth { get; set; }
+ public int? MaxAlbumArtWidth { get; set; }
/// <summary>
/// Gets or sets the MaxAlbumArtHeight.
/// </summary>
- public int MaxAlbumArtHeight { get; set; }
+ public int? MaxAlbumArtHeight { get; set; }
/// <summary>
- /// Gets or sets the MaxIconWidth.
+ /// Gets or sets the maximum allowed width of embedded icons.
/// </summary>
public int? MaxIconWidth { get; set; }
/// <summary>
- /// Gets or sets the MaxIconHeight.
+ /// Gets or sets the maximum allowed height of embedded icons.
/// </summary>
public int? MaxIconHeight { get; set; }
/// <summary>
- /// Gets or sets the MaxStreamingBitrate.
+ /// Gets or sets the maximum allowed bitrate for all streamed content.
/// </summary>
- public int? MaxStreamingBitrate { get; set; }
+ public int? MaxStreamingBitrate { get; set; } = 8000000;
/// <summary>
- /// Gets or sets the MaxStaticBitrate.
+ /// Gets or sets the maximum allowed bitrate for statically streamed content (= direct played files).
/// </summary>
- public int? MaxStaticBitrate { get; set; }
+ public int? MaxStaticBitrate { get; set; } = 8000000;
/// <summary>
- /// Gets or sets the MusicStreamingTranscodingBitrate.
+ /// Gets or sets the maximum allowed bitrate for transcoded music streams.
/// </summary>
- public int? MusicStreamingTranscodingBitrate { get; set; }
+ public int? MusicStreamingTranscodingBitrate { get; set; } = 128000;
/// <summary>
- /// Gets or sets the MaxStaticMusicBitrate.
+ /// Gets or sets the maximum allowed bitrate for statically streamed (= direct played) music files.
/// </summary>
- public int? MaxStaticMusicBitrate { get; set; }
+ public int? MaxStaticMusicBitrate { get; set; } = 8000000;
/// <summary>
/// Gets or sets the content of the aggregationFlags element in the urn:schemas-sonycom:av namespace.
/// </summary>
- public string SonyAggregationFlags { get; set; }
+ public string? SonyAggregationFlags { get; set; }
/// <summary>
/// Gets or sets the ProtocolInfo.
/// </summary>
- public string ProtocolInfo { get; set; }
+ public string? ProtocolInfo { get; set; }
/// <summary>
/// Gets or sets the TimelineOffsetSeconds.
/// </summary>
+ [DefaultValue(0)]
public int TimelineOffsetSeconds { get; set; }
/// <summary>
/// Gets or sets a value indicating whether RequiresPlainVideoItems.
/// </summary>
+ [DefaultValue(false)]
public bool RequiresPlainVideoItems { get; set; }
/// <summary>
/// Gets or sets a value indicating whether RequiresPlainFolders.
/// </summary>
+ [DefaultValue(false)]
public bool RequiresPlainFolders { get; set; }
/// <summary>
/// Gets or sets a value indicating whether EnableMSMediaReceiverRegistrar.
/// </summary>
+ [DefaultValue(false)]
public bool EnableMSMediaReceiverRegistrar { get; set; }
/// <summary>
/// Gets or sets a value indicating whether IgnoreTranscodeByteRangeRequests.
/// </summary>
+ [DefaultValue(false)]
public bool IgnoreTranscodeByteRangeRequests { get; set; }
/// <summary>
/// Gets or sets the XmlRootAttributes.
/// </summary>
- public XmlAttribute[] XmlRootAttributes { get; set; }
+ public XmlAttribute[] XmlRootAttributes { get; set; } = Array.Empty<XmlAttribute>();
/// <summary>
/// Gets or sets the direct play profiles.
/// </summary>
- public DirectPlayProfile[] DirectPlayProfiles { get; set; }
+ public DirectPlayProfile[] DirectPlayProfiles { get; set; } = Array.Empty<DirectPlayProfile>();
/// <summary>
/// Gets or sets the transcoding profiles.
/// </summary>
- public TranscodingProfile[] TranscodingProfiles { get; set; }
+ public TranscodingProfile[] TranscodingProfiles { get; set; } = Array.Empty<TranscodingProfile>();
/// <summary>
- /// Gets or sets the ContainerProfiles.
+ /// Gets or sets the container profiles.
/// </summary>
- public ContainerProfile[] ContainerProfiles { get; set; }
+ public ContainerProfile[] ContainerProfiles { get; set; } = Array.Empty<ContainerProfile>();
/// <summary>
- /// Gets or sets the CodecProfiles.
+ /// Gets or sets the codec profiles.
/// </summary>
- public CodecProfile[] CodecProfiles { get; set; }
+ public CodecProfile[] CodecProfiles { get; set; } = Array.Empty<CodecProfile>();
/// <summary>
/// Gets or sets the ResponseProfiles.
/// </summary>
- public ResponseProfile[] ResponseProfiles { get; set; }
+ public ResponseProfile[] ResponseProfiles { get; set; } = Array.Empty<ResponseProfile>();
/// <summary>
- /// Gets or sets the SubtitleProfiles.
+ /// Gets or sets the subtitle profiles.
/// </summary>
- public SubtitleProfile[] SubtitleProfiles { get; set; }
+ public SubtitleProfile[] SubtitleProfiles { get; set; } = Array.Empty<SubtitleProfile>();
/// <summary>
/// The GetSupportedMediaTypes.
@@ -244,13 +237,13 @@ namespace MediaBrowser.Model.Dlna
/// <param name="container">The container.</param>
/// <param name="audioCodec">The audio Codec.</param>
/// <returns>A <see cref="TranscodingProfile"/>.</returns>
- public TranscodingProfile GetAudioTranscodingProfile(string container, string audioCodec)
+ public TranscodingProfile? GetAudioTranscodingProfile(string? container, string? audioCodec)
{
container = (container ?? string.Empty).TrimStart('.');
foreach (var i in TranscodingProfiles)
{
- if (i.Type != MediaBrowser.Model.Dlna.DlnaProfileType.Audio)
+ if (i.Type != DlnaProfileType.Audio)
{
continue;
}
@@ -278,13 +271,13 @@ namespace MediaBrowser.Model.Dlna
/// <param name="audioCodec">The audio Codec.</param>
/// <param name="videoCodec">The video Codec.</param>
/// <returns>The <see cref="TranscodingProfile"/>.</returns>
- public TranscodingProfile GetVideoTranscodingProfile(string container, string audioCodec, string videoCodec)
+ public TranscodingProfile? GetVideoTranscodingProfile(string? container, string? audioCodec, string? videoCodec)
{
container = (container ?? string.Empty).TrimStart('.');
foreach (var i in TranscodingProfiles)
{
- if (i.Type != MediaBrowser.Model.Dlna.DlnaProfileType.Video)
+ if (i.Type != DlnaProfileType.Video)
{
continue;
}
@@ -299,7 +292,7 @@ namespace MediaBrowser.Model.Dlna
continue;
}
- if (!string.Equals(videoCodec, i.VideoCodec ?? string.Empty, StringComparison.OrdinalIgnoreCase))
+ if (!string.Equals(videoCodec, i.VideoCodec, StringComparison.OrdinalIgnoreCase))
{
continue;
}
@@ -320,7 +313,7 @@ namespace MediaBrowser.Model.Dlna
/// <param name="audioSampleRate">The audio sample rate.</param>
/// <param name="audioBitDepth">The audio bit depth.</param>
/// <returns>The <see cref="ResponseProfile"/>.</returns>
- public ResponseProfile GetAudioMediaProfile(string container, string audioCodec, int? audioChannels, int? audioBitrate, int? audioSampleRate, int? audioBitDepth)
+ public ResponseProfile? GetAudioMediaProfile(string container, string? audioCodec, int? audioChannels, int? audioBitrate, int? audioSampleRate, int? audioBitDepth)
{
foreach (var i in ResponseProfiles)
{
@@ -384,7 +377,7 @@ namespace MediaBrowser.Model.Dlna
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <returns>The <see cref="ResponseProfile"/>.</returns>
- public ResponseProfile GetImageMediaProfile(string container, int? width, int? height)
+ public ResponseProfile? GetImageMediaProfile(string container, int? width, int? height)
{
foreach (var i in ResponseProfiles)
{
@@ -442,10 +435,10 @@ namespace MediaBrowser.Model.Dlna
/// <param name="videoCodecTag">The video Codec tag.</param>
/// <param name="isAvc">True if Avc.</param>
/// <returns>The <see cref="ResponseProfile"/>.</returns>
- public ResponseProfile GetVideoMediaProfile(
+ public ResponseProfile? GetVideoMediaProfile(
string container,
- string audioCodec,
- string videoCodec,
+ string? audioCodec,
+ string? videoCodec,
int? width,
int? height,
int? bitDepth,
diff --git a/MediaBrowser.Model/Dlna/DirectPlayProfile.cs b/MediaBrowser.Model/Dlna/DirectPlayProfile.cs
index 88cb83991..fa3ad098f 100644
--- a/MediaBrowser.Model/Dlna/DirectPlayProfile.cs
+++ b/MediaBrowser.Model/Dlna/DirectPlayProfile.cs
@@ -1,6 +1,6 @@
-#nullable disable
#pragma warning disable CS1591
+using System.ComponentModel.DataAnnotations;
using System.Xml.Serialization;
namespace MediaBrowser.Model.Dlna
@@ -8,13 +8,13 @@ namespace MediaBrowser.Model.Dlna
public class DirectPlayProfile
{
[XmlAttribute("container")]
- public string Container { get; set; }
+ public string? Container { get; set; }
[XmlAttribute("audioCodec")]
- public string AudioCodec { get; set; }
+ public string? AudioCodec { get; set; }
[XmlAttribute("videoCodec")]
- public string VideoCodec { get; set; }
+ public string? VideoCodec { get; set; }
[XmlAttribute("type")]
public DlnaProfileType Type { get; set; }
diff --git a/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs b/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs
index f61b8d59e..7ce248509 100644
--- a/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs
+++ b/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs
@@ -9,16 +9,9 @@ using MediaBrowser.Model.MediaInfo;
namespace MediaBrowser.Model.Dlna
{
- public class MediaFormatProfileResolver
+ public static class MediaFormatProfileResolver
{
- public string[] ResolveVideoFormat(string container, string videoCodec, string audioCodec, int? width, int? height, TransportStreamTimestamp timestampType)
- {
- return ResolveVideoFormatInternal(container, videoCodec, audioCodec, width, height, timestampType)
- .Select(i => i.ToString())
- .ToArray();
- }
-
- private MediaFormatProfile[] ResolveVideoFormatInternal(string container, string videoCodec, string audioCodec, int? width, int? height, TransportStreamTimestamp timestampType)
+ public static MediaFormatProfile[] ResolveVideoFormat(string container, string videoCodec, string audioCodec, int? width, int? height, TransportStreamTimestamp timestampType)
{
if (string.Equals(container, "asf", StringComparison.OrdinalIgnoreCase))
{
@@ -84,7 +77,7 @@ namespace MediaBrowser.Model.Dlna
return Array.Empty<MediaFormatProfile>();
}
- private MediaFormatProfile[] ResolveVideoMPEG2TSFormat(string videoCodec, string audioCodec, int? width, int? height, TransportStreamTimestamp timestampType)
+ private static MediaFormatProfile[] ResolveVideoMPEG2TSFormat(string videoCodec, string audioCodec, int? width, int? height, TransportStreamTimestamp timestampType)
{
string suffix = string.Empty;
@@ -209,12 +202,12 @@ namespace MediaBrowser.Model.Dlna
return Array.Empty<MediaFormatProfile>();
}
- private MediaFormatProfile ValueOf(string value)
+ private static MediaFormatProfile ValueOf(string value)
{
return (MediaFormatProfile)Enum.Parse(typeof(MediaFormatProfile), value, true);
}
- private MediaFormatProfile? ResolveVideoMP4Format(string videoCodec, string audioCodec, int? width, int? height)
+ private static MediaFormatProfile? ResolveVideoMP4Format(string videoCodec, string audioCodec, int? width, int? height)
{
if (string.Equals(videoCodec, "h264", StringComparison.OrdinalIgnoreCase))
{
@@ -287,7 +280,7 @@ namespace MediaBrowser.Model.Dlna
return null;
}
- private MediaFormatProfile? ResolveVideo3GPFormat(string videoCodec, string audioCodec)
+ private static MediaFormatProfile? ResolveVideo3GPFormat(string videoCodec, string audioCodec)
{
if (string.Equals(videoCodec, "h264", StringComparison.OrdinalIgnoreCase))
{
@@ -317,7 +310,7 @@ namespace MediaBrowser.Model.Dlna
return null;
}
- private MediaFormatProfile? ResolveVideoASFFormat(string videoCodec, string audioCodec, int? width, int? height)
+ private static MediaFormatProfile? ResolveVideoASFFormat(string videoCodec, string audioCodec, int? width, int? height)
{
if (string.Equals(videoCodec, "wmv", StringComparison.OrdinalIgnoreCase) &&
(string.IsNullOrEmpty(audioCodec) || string.Equals(audioCodec, "wma", StringComparison.OrdinalIgnoreCase) || string.Equals(videoCodec, "wmapro", StringComparison.OrdinalIgnoreCase)))
@@ -371,7 +364,7 @@ namespace MediaBrowser.Model.Dlna
return null;
}
- public MediaFormatProfile? ResolveAudioFormat(string container, int? bitrate, int? frequency, int? channels)
+ public static MediaFormatProfile? ResolveAudioFormat(string container, int? bitrate, int? frequency, int? channels)
{
if (string.Equals(container, "asf", StringComparison.OrdinalIgnoreCase))
{
@@ -413,7 +406,7 @@ namespace MediaBrowser.Model.Dlna
return null;
}
- private MediaFormatProfile ResolveAudioASFFormat(int? bitrate)
+ private static MediaFormatProfile ResolveAudioASFFormat(int? bitrate)
{
if (bitrate.HasValue && bitrate.Value <= 193)
{
@@ -423,7 +416,7 @@ namespace MediaBrowser.Model.Dlna
return MediaFormatProfile.WMA_FULL;
}
- private MediaFormatProfile? ResolveAudioLPCMFormat(int? frequency, int? channels)
+ private static MediaFormatProfile? ResolveAudioLPCMFormat(int? frequency, int? channels)
{
if (frequency.HasValue && channels.HasValue)
{
@@ -453,7 +446,7 @@ namespace MediaBrowser.Model.Dlna
return MediaFormatProfile.LPCM16_48_STEREO;
}
- private MediaFormatProfile ResolveAudioMP4Format(int? bitrate)
+ private static MediaFormatProfile ResolveAudioMP4Format(int? bitrate)
{
if (bitrate.HasValue && bitrate.Value <= 320)
{
@@ -463,7 +456,7 @@ namespace MediaBrowser.Model.Dlna
return MediaFormatProfile.AAC_ISO;
}
- private MediaFormatProfile ResolveAudioADTSFormat(int? bitrate)
+ private static MediaFormatProfile ResolveAudioADTSFormat(int? bitrate)
{
if (bitrate.HasValue && bitrate.Value <= 320)
{
@@ -473,7 +466,7 @@ namespace MediaBrowser.Model.Dlna
return MediaFormatProfile.AAC_ADTS;
}
- public MediaFormatProfile? ResolveImageFormat(string container, int? width, int? height)
+ public static MediaFormatProfile? ResolveImageFormat(string container, int? width, int? height)
{
if (string.Equals(container, "jpeg", StringComparison.OrdinalIgnoreCase) ||
string.Equals(container, "jpg", StringComparison.OrdinalIgnoreCase))
@@ -499,7 +492,7 @@ namespace MediaBrowser.Model.Dlna
return null;
}
- private MediaFormatProfile ResolveImageJPGFormat(int? width, int? height)
+ private static MediaFormatProfile ResolveImageJPGFormat(int? width, int? height)
{
if (width.HasValue && height.HasValue)
{
@@ -524,7 +517,7 @@ namespace MediaBrowser.Model.Dlna
return MediaFormatProfile.JPEG_SM;
}
- private MediaFormatProfile ResolveImagePNGFormat(int? width, int? height)
+ private static MediaFormatProfile ResolveImagePNGFormat(int? width, int? height)
{
if (width.HasValue && height.HasValue)
{
diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs
index 398d47d5f..f4c69fe8f 100644
--- a/MediaBrowser.Model/Dlna/StreamBuilder.cs
+++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs
@@ -297,7 +297,7 @@ namespace MediaBrowser.Model.Dlna
int? inputAudioSampleRate = audioStream?.SampleRate;
int? inputAudioBitDepth = audioStream?.BitDepth;
- if (directPlayMethods.Count() > 0)
+ if (directPlayMethods.Any())
{
string audioCodec = audioStream?.Codec;
diff --git a/MediaBrowser.Model/Dlna/TranscodingProfile.cs b/MediaBrowser.Model/Dlna/TranscodingProfile.cs
index f05e31047..214578a85 100644
--- a/MediaBrowser.Model/Dlna/TranscodingProfile.cs
+++ b/MediaBrowser.Model/Dlna/TranscodingProfile.cs
@@ -1,6 +1,7 @@
-#nullable disable
#pragma warning disable CS1591
+using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
using System.Xml.Serialization;
namespace MediaBrowser.Model.Dlna
@@ -8,47 +9,56 @@ namespace MediaBrowser.Model.Dlna
public class TranscodingProfile
{
[XmlAttribute("container")]
- public string Container { get; set; }
+ public string Container { get; set; } = string.Empty;
[XmlAttribute("type")]
public DlnaProfileType Type { get; set; }
[XmlAttribute("videoCodec")]
- public string VideoCodec { get; set; }
+ public string VideoCodec { get; set; } = string.Empty;
[XmlAttribute("audioCodec")]
- public string AudioCodec { get; set; }
+ public string AudioCodec { get; set; } = string.Empty;
[XmlAttribute("protocol")]
- public string Protocol { get; set; }
+ public string Protocol { get; set; } = string.Empty;
+ [DefaultValue(false)]
[XmlAttribute("estimateContentLength")]
public bool EstimateContentLength { get; set; }
+ [DefaultValue(false)]
[XmlAttribute("enableMpegtsM2TsMode")]
public bool EnableMpegtsM2TsMode { get; set; }
+ [DefaultValue(TranscodeSeekInfo.Auto)]
[XmlAttribute("transcodeSeekInfo")]
public TranscodeSeekInfo TranscodeSeekInfo { get; set; }
+ [DefaultValue(false)]
[XmlAttribute("copyTimestamps")]
public bool CopyTimestamps { get; set; }
+ [DefaultValue(EncodingContext.Streaming)]
[XmlAttribute("context")]
public EncodingContext Context { get; set; }
+ [DefaultValue(false)]
[XmlAttribute("enableSubtitlesInManifest")]
public bool EnableSubtitlesInManifest { get; set; }
[XmlAttribute("maxAudioChannels")]
- public string MaxAudioChannels { get; set; }
+ public string? MaxAudioChannels { get; set; }
+ [DefaultValue(0)]
[XmlAttribute("minSegments")]
public int MinSegments { get; set; }
+ [DefaultValue(0)]
[XmlAttribute("segmentLength")]
public int SegmentLength { get; set; }
+ [DefaultValue(false)]
[XmlAttribute("breakOnNonKeyFrames")]
public bool BreakOnNonKeyFrames { get; set; }
diff --git a/MediaBrowser.Model/Dlna/UpnpDeviceInfo.cs b/MediaBrowser.Model/Dlna/UpnpDeviceInfo.cs
index d71013f01..987a3a908 100644
--- a/MediaBrowser.Model/Dlna/UpnpDeviceInfo.cs
+++ b/MediaBrowser.Model/Dlna/UpnpDeviceInfo.cs
@@ -16,5 +16,7 @@ namespace MediaBrowser.Model.Dlna
public IPAddress LocalIpAddress { get; set; }
public int LocalPort { get; set; }
+
+ public IPAddress RemoteIpAddress { get; set; }
}
}
diff --git a/MediaBrowser.Model/Dto/NameIdPair.cs b/MediaBrowser.Model/Dto/NameIdPair.cs
index 7f18b4502..31516947f 100644
--- a/MediaBrowser.Model/Dto/NameIdPair.cs
+++ b/MediaBrowser.Model/Dto/NameIdPair.cs
@@ -1,8 +1,6 @@
#nullable disable
#pragma warning disable CS1591
-using System;
-
namespace MediaBrowser.Model.Dto
{
public class NameIdPair
diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs
index ade9d7e8d..e644c9ba7 100644
--- a/MediaBrowser.Model/Entities/MediaStream.cs
+++ b/MediaBrowser.Model/Entities/MediaStream.cs
@@ -163,7 +163,7 @@ namespace MediaBrowser.Model.Entities
foreach (var tag in attributes)
{
// Keep Tags that are not already in Title.
- if (Title.IndexOf(tag, StringComparison.OrdinalIgnoreCase) == -1)
+ if (!Title.Contains(tag, StringComparison.OrdinalIgnoreCase))
{
result.Append(" - ").Append(tag);
}
@@ -202,7 +202,7 @@ namespace MediaBrowser.Model.Entities
foreach (var tag in attributes)
{
// Keep Tags that are not already in Title.
- if (Title.IndexOf(tag, StringComparison.OrdinalIgnoreCase) == -1)
+ if (!Title.Contains(tag, StringComparison.OrdinalIgnoreCase))
{
result.Append(" - ").Append(tag);
}
@@ -522,9 +522,9 @@ namespace MediaBrowser.Model.Entities
// sub = external .sub file
- return codec.IndexOf("pgs", StringComparison.OrdinalIgnoreCase) == -1 &&
- codec.IndexOf("dvd", StringComparison.OrdinalIgnoreCase) == -1 &&
- codec.IndexOf("dvbsub", StringComparison.OrdinalIgnoreCase) == -1 &&
+ return !codec.Contains("pgs", StringComparison.OrdinalIgnoreCase) &&
+ !codec.Contains("dvd", StringComparison.OrdinalIgnoreCase) &&
+ !codec.Contains("dvbsub", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(codec, "sub", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(codec, "dvb_subtitle", StringComparison.OrdinalIgnoreCase);
}
diff --git a/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs b/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs
index 09d14dc6a..ce4b0ec92 100644
--- a/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs
+++ b/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs
@@ -123,10 +123,7 @@ namespace MediaBrowser.Model.Entities
else
{
// Ensure it exists
- if (instance.ProviderIds == null)
- {
- instance.ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- }
+ instance.ProviderIds ??= new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
instance.ProviderIds[name] = value;
}
diff --git a/MediaBrowser.Model/IO/FileSystemMetadata.cs b/MediaBrowser.Model/IO/FileSystemMetadata.cs
index 118c78e80..fb74886bf 100644
--- a/MediaBrowser.Model/IO/FileSystemMetadata.cs
+++ b/MediaBrowser.Model/IO/FileSystemMetadata.cs
@@ -38,12 +38,6 @@ namespace MediaBrowser.Model.IO
public long Length { get; set; }
/// <summary>
- /// Gets or sets the name of the directory.
- /// </summary>
- /// <value>The name of the directory.</value>
- public string DirectoryName { get; set; }
-
- /// <summary>
/// Gets or sets the last write time UTC.
/// </summary>
/// <value>The last write time UTC.</value>
diff --git a/MediaBrowser.Model/LiveTv/TunerHostInfo.cs b/MediaBrowser.Model/LiveTv/TunerHostInfo.cs
index 7d4bbb2d0..05576a0f8 100644
--- a/MediaBrowser.Model/LiveTv/TunerHostInfo.cs
+++ b/MediaBrowser.Model/LiveTv/TunerHostInfo.cs
@@ -1,9 +1,6 @@
#nullable disable
#pragma warning disable CS1591
-using System;
-using MediaBrowser.Model.Dto;
-
namespace MediaBrowser.Model.LiveTv
{
public class TunerHostInfo
diff --git a/MediaBrowser.Model/Notifications/NotificationOptions.cs b/MediaBrowser.Model/Notifications/NotificationOptions.cs
index 94bb5d6e3..09beb2ef7 100644
--- a/MediaBrowser.Model/Notifications/NotificationOptions.cs
+++ b/MediaBrowser.Model/Notifications/NotificationOptions.cs
@@ -5,8 +5,6 @@ using System;
using System.Linq;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
-using MediaBrowser.Model.Extensions;
-using MediaBrowser.Model.Users;
namespace MediaBrowser.Model.Notifications
{
@@ -95,16 +93,17 @@ namespace MediaBrowser.Model.Notifications
{
NotificationOption opt = GetOptions(notificationType);
- return opt == null ||
- !opt.DisabledServices.Contains(service, StringComparer.OrdinalIgnoreCase);
+ return opt == null
+ || !opt.DisabledServices.Contains(service, StringComparer.OrdinalIgnoreCase);
}
public bool IsEnabledToMonitorUser(string type, Guid userId)
{
NotificationOption opt = GetOptions(type);
- return opt != null && opt.Enabled &&
- !opt.DisabledMonitorUsers.Contains(userId.ToString(string.Empty), StringComparer.OrdinalIgnoreCase);
+ return opt != null
+ && opt.Enabled
+ && !opt.DisabledMonitorUsers.Contains(userId.ToString("N"), StringComparer.OrdinalIgnoreCase);
}
public bool IsEnabledToSendToUser(string type, string userId, User user)
diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs
index 437b43eca..401c7e99f 100644
--- a/MediaBrowser.Providers/Manager/MetadataService.cs
+++ b/MediaBrowser.Providers/Manager/MetadataService.cs
@@ -201,7 +201,7 @@ namespace MediaBrowser.Providers.Manager
}
// Save to database
- await SaveItemAsync(metadataResult, libraryOptions, updateType, cancellationToken).ConfigureAwait(false);
+ await SaveItemAsync(metadataResult, updateType, cancellationToken).ConfigureAwait(false);
}
await AfterMetadataRefresh(itemOfType, refreshOptions, cancellationToken).ConfigureAwait(false);
@@ -216,66 +216,18 @@ namespace MediaBrowser.Providers.Manager
lookupInfo.Year = result.ProductionYear;
}
- protected async Task SaveItemAsync(MetadataResult<TItemType> result, LibraryOptions libraryOptions, ItemUpdateType reason, CancellationToken cancellationToken)
+ protected async Task SaveItemAsync(MetadataResult<TItemType> result, ItemUpdateType reason, CancellationToken cancellationToken)
{
if (result.Item.SupportsPeople && result.People != null)
{
var baseItem = result.Item;
- LibraryManager.UpdatePeople(baseItem, result.People);
- await SavePeopleMetadataAsync(result.People, libraryOptions, cancellationToken).ConfigureAwait(false);
+ await LibraryManager.UpdatePeopleAsync(baseItem, result.People, cancellationToken).ConfigureAwait(false);
}
await result.Item.UpdateToRepositoryAsync(reason, cancellationToken).ConfigureAwait(false);
}
- private async Task SavePeopleMetadataAsync(List<PersonInfo> people, LibraryOptions libraryOptions, CancellationToken cancellationToken)
- {
- var personsToSave = new List<BaseItem>();
-
- foreach (var person in people)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- if (person.ProviderIds.Count > 0 || !string.IsNullOrWhiteSpace(person.ImageUrl))
- {
- var itemUpdateType = ItemUpdateType.MetadataDownload;
- var saveEntity = false;
- var personEntity = LibraryManager.GetPerson(person.Name);
- foreach (var id in person.ProviderIds)
- {
- if (!string.Equals(personEntity.GetProviderId(id.Key), id.Value, StringComparison.OrdinalIgnoreCase))
- {
- personEntity.SetProviderId(id.Key, id.Value);
- saveEntity = true;
- }
- }
-
- if (!string.IsNullOrWhiteSpace(person.ImageUrl) && !personEntity.HasImage(ImageType.Primary))
- {
- personEntity.SetImage(
- new ItemImageInfo
- {
- Path = person.ImageUrl,
- Type = ImageType.Primary
- },
- 0);
-
- saveEntity = true;
- itemUpdateType = ItemUpdateType.ImageUpdate;
- }
-
- if (saveEntity)
- {
- personsToSave.Add(personEntity);
- await LibraryManager.RunMetadataSavers(personEntity, itemUpdateType).ConfigureAwait(false);
- }
- }
- }
-
- LibraryManager.CreateItems(personsToSave, null, CancellationToken.None);
- }
-
protected virtual Task AfterMetadataRefresh(TItemType item, MetadataRefreshOptions refreshOptions, CancellationToken cancellationToken)
{
item.AfterMetadataRefresh();
@@ -329,8 +281,7 @@ namespace MediaBrowser.Providers.Manager
return true;
}
- var folder = item as Folder;
- if (folder != null)
+ if (item is Folder folder)
{
return folder.SupportsDateLastMediaAdded || folder.SupportsCumulativeRunTimeTicks;
}
@@ -384,8 +335,7 @@ namespace MediaBrowser.Providers.Manager
private ItemUpdateType UpdateCumulativeRunTimeTicks(TItemType item, IList<BaseItem> children)
{
- var folder = item as Folder;
- if (folder != null && folder.SupportsCumulativeRunTimeTicks)
+ if (item is Folder folder && folder.SupportsCumulativeRunTimeTicks)
{
long ticks = 0;
diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs
index b4b0b826f..08c92e15a 100644
--- a/MediaBrowser.Providers/Manager/ProviderManager.cs
+++ b/MediaBrowser.Providers/Manager/ProviderManager.cs
@@ -25,7 +25,6 @@ using MediaBrowser.Controller.Subtitles;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Net;
using MediaBrowser.Model.Providers;
using Microsoft.Extensions.Logging;
using Priority_Queue;
@@ -1022,26 +1021,26 @@ namespace MediaBrowser.Providers.Manager
// TODO: Need to hunt down the conditions for this happening
_activeRefreshes.AddOrUpdate(
id,
- (_) => throw new Exception(
+ (_) => throw new InvalidOperationException(
string.Format(
CultureInfo.InvariantCulture,
"Cannot update refresh progress of item '{0}' ({1}) because a refresh for this item is not running",
item.GetType().Name,
item.Id.ToString("N", CultureInfo.InvariantCulture))),
- (_, __) => progress);
+ (_, _) => progress);
RefreshProgress?.Invoke(this, new GenericEventArgs<Tuple<BaseItem, double>>(new Tuple<BaseItem, double>(item, progress)));
}
/// <inheritdoc/>
- public void QueueRefresh(Guid id, MetadataRefreshOptions options, RefreshPriority priority)
+ public void QueueRefresh(Guid itemId, MetadataRefreshOptions options, RefreshPriority priority)
{
if (_disposed)
{
return;
}
- _refreshQueue.Enqueue(new Tuple<Guid, MetadataRefreshOptions>(id, options), (int)priority);
+ _refreshQueue.Enqueue(new Tuple<Guid, MetadataRefreshOptions>(itemId, options), (int)priority);
lock (_refreshQueueLock)
{
@@ -1074,17 +1073,16 @@ namespace MediaBrowser.Providers.Manager
try
{
var item = libraryManager.GetItemById(refreshItem.Item1);
- if (item != null)
+ if (item == null)
{
- // Try to throttle this a little bit.
- await Task.Delay(100, cancellationToken).ConfigureAwait(false);
+ continue;
+ }
- var task = item is MusicArtist artist
- ? RefreshArtist(artist, refreshItem.Item2, cancellationToken)
- : RefreshItem(item, refreshItem.Item2, cancellationToken);
+ var task = item is MusicArtist artist
+ ? RefreshArtist(artist, refreshItem.Item2, cancellationToken)
+ : RefreshItem(item, refreshItem.Item2, cancellationToken);
- await task.ConfigureAwait(false);
- }
+ await task.ConfigureAwait(false);
}
catch (OperationCanceledException)
{
diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
index de7860b2e..cdb07a15d 100644
--- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj
+++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
@@ -19,7 +19,7 @@
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="5.0.0" />
- <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
+ <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="OptimizedPriorityQueue" Version="5.0.0" />
<PackageReference Include="PlaylistsNET" Version="1.1.3" />
<PackageReference Include="TMDbLib" Version="1.8.1" />
diff --git a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs
index 64ad1bddf..03e45fb86 100644
--- a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs
+++ b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs
@@ -137,9 +137,7 @@ namespace MediaBrowser.Providers.MediaInfo
return false;
}
- var audio = item as Audio;
-
- return audio != null;
+ return item is Audio;
}
}
}
diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
index b07711197..f049cc81f 100644
--- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
+++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
@@ -111,6 +111,8 @@ namespace MediaBrowser.Providers.MediaInfo
}
}
+ streamFileNames ??= Array.Empty<string>();
+
mediaInfoResult = await GetMediaInfo(item, cancellationToken).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
diff --git a/MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs b/MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs
index 912aedb0d..44ab5aa5b 100644
--- a/MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs
+++ b/MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs
@@ -172,9 +172,7 @@ namespace MediaBrowser.Providers.MediaInfo
SubtitleFetcherOrder = subtitleFetcherOrder
};
- var episode = video as Episode;
-
- if (episode != null)
+ if (video is Episode episode)
{
request.IndexNumberEnd = episode.IndexNumberEnd;
request.SeriesName = episode.SeriesName;
diff --git a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs
index c36c3af6a..30af6710a 100644
--- a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs
+++ b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs
@@ -154,9 +154,7 @@ namespace MediaBrowser.Providers.MediaInfo
return false;
}
- var video = item as Video;
-
- if (video != null && !video.IsPlaceHolder && video.IsCompleteMedia)
+ if (item is Video video && !video.IsPlaceHolder && video.IsCompleteMedia)
{
return true;
}
diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs
index 2adb11908..85a28747f 100644
--- a/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs
@@ -14,7 +14,6 @@ using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
-using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Providers.Plugins.AudioDb
{
diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs
index 00feeec1f..25bb3f9ce 100644
--- a/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs
+++ b/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs
@@ -19,7 +19,6 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Providers;
-using MediaBrowser.Model.Serialization;
using MediaBrowser.Providers.Music;
namespace MediaBrowser.Providers.Plugins.AudioDb
diff --git a/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs
index b8095ff04..db8536cc9 100644
--- a/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs
@@ -14,7 +14,6 @@ using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
-using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Providers.Plugins.AudioDb
{
diff --git a/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs
index 59ecbc017..cbb61fa35 100644
--- a/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs
+++ b/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs
@@ -18,7 +18,6 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Providers;
-using MediaBrowser.Model.Serialization;
using MediaBrowser.Providers.Music;
namespace MediaBrowser.Providers.Plugins.AudioDb
diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs
index d35805a84..46d303890 100644
--- a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs
@@ -6,9 +6,7 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Net.Http;
-using System.Text;
using System.Text.Json;
-using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common;
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs
index 833d1ae38..d22c1b50a 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs
@@ -7,8 +7,6 @@ using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
-using TMDbLib.Objects.Find;
-using TMDbLib.Objects.Search;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
@@ -16,6 +14,8 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
+using TMDbLib.Objects.Find;
+using TMDbLib.Objects.Search;
namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
{
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs
index 3f57c4bc4..bf42ceade 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs
@@ -49,37 +49,36 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People
public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken)
{
var person = (Person)item;
- var personTmdbId = Convert.ToInt32(person.GetProviderId(MetadataProvider.Tmdb), CultureInfo.InvariantCulture);
- if (personTmdbId > 0)
+ if (!person.TryGetProviderId(MetadataProvider.Tmdb, out var personTmdbId))
{
- var personResult = await _tmdbClientManager.GetPersonAsync(personTmdbId, cancellationToken).ConfigureAwait(false);
- if (personResult?.Images?.Profiles == null)
- {
- return Enumerable.Empty<RemoteImageInfo>();
- }
+ return Enumerable.Empty<RemoteImageInfo>();
+ }
- var remoteImages = new List<RemoteImageInfo>();
- var language = item.GetPreferredMetadataLanguage();
+ var personResult = await _tmdbClientManager.GetPersonAsync(int.Parse(personTmdbId, CultureInfo.InvariantCulture), cancellationToken).ConfigureAwait(false);
+ if (personResult?.Images?.Profiles == null)
+ {
+ return Enumerable.Empty<RemoteImageInfo>();
+ }
- for (var i = 0; i < personResult.Images.Profiles.Count; i++)
- {
- var image = personResult.Images.Profiles[i];
- remoteImages.Add(new RemoteImageInfo
- {
- ProviderName = Name,
- Type = ImageType.Primary,
- Width = image.Width,
- Height = image.Height,
- Language = TmdbUtils.AdjustImageLanguage(image.Iso_639_1, language),
- Url = _tmdbClientManager.GetProfileUrl(image.FilePath)
- });
- }
+ var remoteImages = new RemoteImageInfo[personResult.Images.Profiles.Count];
+ var language = item.GetPreferredMetadataLanguage();
- return remoteImages.OrderByLanguageDescending(language);
+ for (var i = 0; i < personResult.Images.Profiles.Count; i++)
+ {
+ var image = personResult.Images.Profiles[i];
+ remoteImages[i] = new RemoteImageInfo
+ {
+ ProviderName = Name,
+ Type = ImageType.Primary,
+ Width = image.Width,
+ Height = image.Height,
+ Language = TmdbUtils.AdjustImageLanguage(image.Iso_639_1, language),
+ Url = _tmdbClientManager.GetProfileUrl(image.FilePath)
+ };
}
- return Enumerable.Empty<RemoteImageInfo>();
+ return remoteImages.OrderByLanguageDescending(language);
}
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs
index 4384c203e..1757c8267 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs
@@ -30,11 +30,9 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People
public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(PersonLookupInfo searchInfo, CancellationToken cancellationToken)
{
- var personTmdbId = Convert.ToInt32(searchInfo.GetProviderId(MetadataProvider.Tmdb), CultureInfo.InvariantCulture);
-
- if (personTmdbId <= 0)
+ if (searchInfo.TryGetProviderId(MetadataProvider.Tmdb, out var personTmdbId))
{
- var personResult = await _tmdbClientManager.GetPersonAsync(personTmdbId, cancellationToken).ConfigureAwait(false);
+ var personResult = await _tmdbClientManager.GetPersonAsync(int.Parse(personTmdbId, CultureInfo.InvariantCulture), cancellationToken).ConfigureAwait(false);
if (personResult != null)
{
@@ -51,19 +49,15 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People
}
result.SetProviderId(MetadataProvider.Tmdb, personResult.Id.ToString(CultureInfo.InvariantCulture));
- result.SetProviderId(MetadataProvider.Imdb, personResult.ExternalIds.ImdbId);
+ if (!string.IsNullOrEmpty(personResult.ExternalIds.ImdbId))
+ {
+ result.SetProviderId(MetadataProvider.Imdb, personResult.ExternalIds.ImdbId);
+ }
return new[] { result };
}
}
- // TODO why? Because of the old rate limit?
- if (searchInfo.IsAutomated)
- {
- // Don't hammer moviedb searching by name
- return Enumerable.Empty<RemoteSearchResult>();
- }
-
var personSearchResult = await _tmdbClientManager.SearchPersonAsync(searchInfo.Name, cancellationToken).ConfigureAwait(false);
var remoteSearchResults = new List<RemoteSearchResult>();
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs
index d92336624..ba18c542f 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs
@@ -65,7 +65,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
// TODO use image languages if All Languages isn't toggled, but there's currently no way to get that value in here
var episodeResult = await _tmdbClientManager
- .GetEpisodeAsync(seriesTmdbId, seasonNumber.Value, episodeNumber.Value, null, null, cancellationToken)
+ .GetEpisodeAsync(seriesTmdbId, seasonNumber.Value, episodeNumber.Value, series.DisplayOrder, null, null, cancellationToken)
.ConfigureAwait(false);
var stills = episodeResult?.Images?.Stills;
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs
index b455e5634..8ec8f6464 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs
@@ -92,7 +92,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
}
var episodeResult = await _tmdbClientManager
- .GetEpisodeAsync(seriesTmdbId, seasonNumber.Value, episodeNumber.Value, info.MetadataLanguage, TmdbUtils.GetImageLanguagesParam(info.MetadataLanguage), cancellationToken)
+ .GetEpisodeAsync(seriesTmdbId, seasonNumber.Value, episodeNumber.Value, info.SeriesDisplayOrder, info.MetadataLanguage, TmdbUtils.GetImageLanguagesParam(info.MetadataLanguage), cancellationToken)
.ConfigureAwait(false);
if (episodeResult == null)
@@ -121,9 +121,20 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
CommunityRating = Convert.ToSingle(episodeResult.VoteAverage)
};
- if (!string.IsNullOrEmpty(episodeResult.ExternalIds?.TvdbId))
+ var externalIds = episodeResult.ExternalIds;
+ if (!string.IsNullOrEmpty(externalIds?.TvdbId))
{
- item.SetProviderId(MetadataProvider.Tvdb, episodeResult.ExternalIds.TvdbId);
+ item.SetProviderId(MetadataProvider.Tvdb, externalIds.TvdbId);
+ }
+
+ if (!string.IsNullOrEmpty(externalIds?.ImdbId))
+ {
+ item.SetProviderId(MetadataProvider.Imdb, externalIds.ImdbId);
+ }
+
+ if (!string.IsNullOrEmpty(externalIds?.TvrageId))
+ {
+ item.SetProviderId(MetadataProvider.TvRage, externalIds.TvrageId);
}
if (episodeResult.Videos?.Results != null)
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs b/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs
index bf0f027fc..05e5d3ced 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs
@@ -125,7 +125,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb
tmdbId,
language: TmdbUtils.NormalizeLanguage(language),
includeImageLanguage: imageLanguages,
- extraMethods: TvShowMethods.Credits | TvShowMethods.Images | TvShowMethods.Keywords | TvShowMethods.ExternalIds | TvShowMethods.Videos | TvShowMethods.ContentRatings,
+ extraMethods: TvShowMethods.Credits | TvShowMethods.Images | TvShowMethods.Keywords | TvShowMethods.ExternalIds | TvShowMethods.Videos | TvShowMethods.ContentRatings | TvShowMethods.EpisodeGroups,
cancellationToken: cancellationToken).ConfigureAwait(false);
if (series != null)
@@ -137,6 +137,56 @@ namespace MediaBrowser.Providers.Plugins.Tmdb
}
/// <summary>
+ /// Gets a tv show episode group from the TMDb API based on the show id and the display order.
+ /// </summary>
+ /// <param name="tvShowId">The tv show's TMDb id.</param>
+ /// <param name="displayOrder">The display order.</param>
+ /// <param name="language">The tv show's language.</param>
+ /// <param name="imageLanguages">A comma-separated list of image languages.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>The TMDb tv show episode group information or null if not found.</returns>
+ private async Task<TvGroupCollection> GetSeriesGroupAsync(int tvShowId, string displayOrder, string language, string imageLanguages, CancellationToken cancellationToken)
+ {
+ TvGroupType? groupType =
+ string.Equals(displayOrder, "absolute", StringComparison.Ordinal) ? TvGroupType.Absolute :
+ string.Equals(displayOrder, "dvd", StringComparison.Ordinal) ? TvGroupType.DVD :
+ null;
+
+ if (groupType == null)
+ {
+ return null;
+ }
+
+ var key = $"group-{tvShowId.ToString(CultureInfo.InvariantCulture)}-{displayOrder}-{language}";
+ if (_memoryCache.TryGetValue(key, out TvGroupCollection group))
+ {
+ return group;
+ }
+
+ await EnsureClientConfigAsync().ConfigureAwait(false);
+
+ var series = await GetSeriesAsync(tvShowId, language, imageLanguages, cancellationToken).ConfigureAwait(false);
+ var episodeGroupId = series?.EpisodeGroups.Results.Find(g => g.Type == groupType)?.Id;
+
+ if (episodeGroupId == null)
+ {
+ return null;
+ }
+
+ group = await _tmDbClient.GetTvEpisodeGroupsAsync(
+ episodeGroupId,
+ language: TmdbUtils.NormalizeLanguage(language),
+ cancellationToken: cancellationToken).ConfigureAwait(false);
+
+ if (group != null)
+ {
+ _memoryCache.Set(key, group, TimeSpan.FromHours(CacheDurationInHours));
+ }
+
+ return group;
+ }
+
+ /// <summary>
/// Gets a tv season from the TMDb API based on the tv show's TMDb id.
/// </summary>
/// <param name="tvShowId">The tv season's TMDb id.</param>
@@ -177,13 +227,14 @@ namespace MediaBrowser.Providers.Plugins.Tmdb
/// <param name="tvShowId">The tv show's TMDb id.</param>
/// <param name="seasonNumber">The season number.</param>
/// <param name="episodeNumber">The episode number.</param>
+ /// <param name="displayOrder">The display order.</param>
/// <param name="language">The episode's language.</param>
/// <param name="imageLanguages">A comma-separated list of image languages.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The TMDb tv episode information or null if not found.</returns>
- public async Task<TvEpisode> GetEpisodeAsync(int tvShowId, int seasonNumber, int episodeNumber, string language, string imageLanguages, CancellationToken cancellationToken)
+ public async Task<TvEpisode> GetEpisodeAsync(int tvShowId, int seasonNumber, int episodeNumber, string displayOrder, string language, string imageLanguages, CancellationToken cancellationToken)
{
- var key = $"episode-{tvShowId.ToString(CultureInfo.InvariantCulture)}-s{seasonNumber.ToString(CultureInfo.InvariantCulture)}e{episodeNumber.ToString(CultureInfo.InvariantCulture)}-{language}";
+ var key = $"episode-{tvShowId.ToString(CultureInfo.InvariantCulture)}-s{seasonNumber.ToString(CultureInfo.InvariantCulture)}e{episodeNumber.ToString(CultureInfo.InvariantCulture)}-{displayOrder}-{language}";
if (_memoryCache.TryGetValue(key, out TvEpisode episode))
{
return episode;
@@ -191,6 +242,19 @@ namespace MediaBrowser.Providers.Plugins.Tmdb
await EnsureClientConfigAsync().ConfigureAwait(false);
+ var group = await GetSeriesGroupAsync(tvShowId, displayOrder, language, imageLanguages, cancellationToken);
+ if (group != null)
+ {
+ var season = group.Groups.Find(s => s.Order == seasonNumber);
+ // Episode order starts at 0
+ var ep = season?.Episodes.Find(e => e.Order == episodeNumber - 1);
+ if (ep != null)
+ {
+ seasonNumber = ep.SeasonNumber;
+ episodeNumber = ep.EpisodeNumber;
+ }
+ }
+
episode = await _tmDbClient.GetTvEpisodeAsync(
tvShowId,
seasonNumber,
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs b/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs
index 15a44c7ed..2498ce9c4 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs
@@ -63,19 +63,19 @@ namespace MediaBrowser.Providers.Plugins.Tmdb
/// <returns>The Jellyfin person type.</returns>
public static string MapCrewToPersonType(Crew crew)
{
- if (crew.Department.Equals("production", StringComparison.InvariantCultureIgnoreCase)
- && crew.Job.Contains("director", StringComparison.InvariantCultureIgnoreCase))
+ if (crew.Department.Equals("production", StringComparison.OrdinalIgnoreCase)
+ && crew.Job.Contains("director", StringComparison.OrdinalIgnoreCase))
{
return PersonType.Director;
}
- if (crew.Department.Equals("production", StringComparison.InvariantCultureIgnoreCase)
- && crew.Job.Contains("producer", StringComparison.InvariantCultureIgnoreCase))
+ if (crew.Department.Equals("production", StringComparison.OrdinalIgnoreCase)
+ && crew.Job.Contains("producer", StringComparison.OrdinalIgnoreCase))
{
return PersonType.Producer;
}
- if (crew.Department.Equals("writing", StringComparison.InvariantCultureIgnoreCase))
+ if (crew.Department.Equals("writing", StringComparison.OrdinalIgnoreCase))
{
return PersonType.Writer;
}
diff --git a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs
index 1f3d9acff..bf0c853ae 100644
--- a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs
+++ b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs
@@ -207,7 +207,7 @@ namespace MediaBrowser.Providers.Subtitles
{
var mediaFolderPath = Path.GetFullPath(Path.Combine(video.ContainingFolderPath, saveFileName));
// TODO: Add some error handling to the API user: return BadRequest("Could not save subtitle, bad path.");
- if (mediaFolderPath.StartsWith(video.ContainingFolderPath))
+ if (mediaFolderPath.StartsWith(video.ContainingFolderPath, StringComparison.Ordinal))
{
savePaths.Add(mediaFolderPath);
}
@@ -216,7 +216,7 @@ namespace MediaBrowser.Providers.Subtitles
var internalPath = Path.GetFullPath(Path.Combine(video.GetInternalMetadataPath(), saveFileName));
// TODO: Add some error to the user: return BadRequest("Could not save subtitle, bad path.");
- if (internalPath.StartsWith(video.GetInternalMetadataPath()))
+ if (internalPath.StartsWith(video.GetInternalMetadataPath(), StringComparison.Ordinal))
{
savePaths.Add(internalPath);
}
@@ -234,7 +234,7 @@ namespace MediaBrowser.Providers.Subtitles
private async Task TrySaveToFiles(Stream stream, List<string> savePaths)
{
- Exception exceptionToThrow = null;
+ List<Exception> exs = null;
foreach (var savePath in savePaths)
{
@@ -256,10 +256,7 @@ namespace MediaBrowser.Providers.Subtitles
}
catch (Exception ex)
{
- if (exceptionToThrow == null)
- {
- exceptionToThrow = ex;
- }
+ (exs ??= new List<Exception>()).Add(ex);
}
finally
{
@@ -269,9 +266,9 @@ namespace MediaBrowser.Providers.Subtitles
stream.Position = 0;
}
- if (exceptionToThrow != null)
+ if (exs != null)
{
- throw exceptionToThrow;
+ throw new AggregateException(exs);
}
}
diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
index ff9f11eab..302c93f0b 100644
--- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
+++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
@@ -662,6 +662,21 @@ namespace MediaBrowser.XbmcMetadata.Parsers
break;
}
+ case "ratings":
+ {
+ if (!reader.IsEmptyElement)
+ {
+ using var subtree = reader.ReadSubtree();
+ FetchFromRatingsNode(subtree, item);
+ }
+ else
+ {
+ reader.Read();
+ }
+
+ break;
+ }
+
case "aired":
case "formed":
case "premiered":
@@ -1080,6 +1095,92 @@ namespace MediaBrowser.XbmcMetadata.Parsers
}
}
+ private void FetchFromRatingsNode(XmlReader reader, T item)
+ {
+ reader.MoveToContent();
+ reader.Read();
+
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
+ {
+ if (reader.NodeType == XmlNodeType.Element)
+ {
+ switch (reader.Name)
+ {
+ case "rating":
+ {
+ if (reader.IsEmptyElement)
+ {
+ reader.Read();
+ continue;
+ }
+
+ var ratingName = reader.GetAttribute("name");
+
+ using var subtree = reader.ReadSubtree();
+ FetchFromRatingNode(subtree, item, ratingName);
+
+ break;
+ }
+
+ default:
+ reader.Skip();
+ break;
+ }
+ }
+ else
+ {
+ reader.Read();
+ }
+ }
+ }
+
+ private void FetchFromRatingNode(XmlReader reader, T item, string? ratingName)
+ {
+ reader.MoveToContent();
+ reader.Read();
+
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
+ {
+ if (reader.NodeType == XmlNodeType.Element)
+ {
+ switch (reader.Name)
+ {
+ case "value":
+ var val = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ if (float.TryParse(val, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var ratingValue))
+ {
+ // if ratingName contains tomato --> assume critic rating
+ if (ratingName != null &&
+ ratingName.Contains("tomato", StringComparison.OrdinalIgnoreCase) &&
+ !ratingName.Contains("audience", StringComparison.OrdinalIgnoreCase))
+ {
+ item.CriticRating = ratingValue;
+ }
+ else
+ {
+ item.CommunityRating = ratingValue;
+ }
+ }
+ }
+
+ break;
+ default:
+ reader.Skip();
+ break;
+ }
+ }
+ else
+ {
+ reader.Read();
+ }
+ }
+ }
+
/// <summary>
/// Gets the persons from XML node.
/// </summary>
@@ -1232,6 +1333,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
"discart" => ImageType.Disc,
"landscape" => ImageType.Thumb,
"clearart" => ImageType.Art,
+ "fanart" => ImageType.Backdrop,
// unknown type (including "poster") --> primary
_ => ImageType.Primary,
};
diff --git a/RSSDP/DeviceAvailableEventArgs.cs b/RSSDP/DeviceAvailableEventArgs.cs
index b7d22a7df..04b14c4dc 100644
--- a/RSSDP/DeviceAvailableEventArgs.cs
+++ b/RSSDP/DeviceAvailableEventArgs.cs
@@ -8,7 +8,7 @@ namespace Rssdp
/// </summary>
public sealed class DeviceAvailableEventArgs : EventArgs
{
- public IPAddress LocalIpAddress { get; set; }
+ public IPAddress RemoteIpAddress { get; set; }
private readonly DiscoveredSsdpDevice _DiscoveredDevice;
diff --git a/RSSDP/SsdpCommunicationsServer.cs b/RSSDP/SsdpCommunicationsServer.cs
index 8f1f0fa61..f448ad38b 100644
--- a/RSSDP/SsdpCommunicationsServer.cs
+++ b/RSSDP/SsdpCommunicationsServer.cs
@@ -415,10 +415,7 @@ namespace Rssdp.Infrastructure
{
lock (_SendSocketSynchroniser)
{
- if (_sendSockets == null)
- {
- _sendSockets = CreateSocketAndListenForResponsesAsync();
- }
+ _sendSockets ??= CreateSocketAndListenForResponsesAsync();
}
}
}
diff --git a/RSSDP/SsdpDeviceLocator.cs b/RSSDP/SsdpDeviceLocator.cs
index 0cdc5ce3d..188e298e2 100644
--- a/RSSDP/SsdpDeviceLocator.cs
+++ b/RSSDP/SsdpDeviceLocator.cs
@@ -211,7 +211,7 @@ namespace Rssdp.Infrastructure
/// Raises the <see cref="DeviceAvailable"/> event.
/// </summary>
/// <seealso cref="DeviceAvailable"/>
- protected virtual void OnDeviceAvailable(DiscoveredSsdpDevice device, bool isNewDevice, IPAddress localIpAddress)
+ protected virtual void OnDeviceAvailable(DiscoveredSsdpDevice device, bool isNewDevice, IPAddress IpAddress)
{
if (this.IsDisposed)
{
@@ -223,7 +223,7 @@ namespace Rssdp.Infrastructure
{
handlers(this, new DeviceAvailableEventArgs(device, isNewDevice)
{
- LocalIpAddress = localIpAddress
+ RemoteIpAddress = IpAddress
});
}
}
@@ -289,7 +289,7 @@ namespace Rssdp.Infrastructure
}
}
- private void AddOrUpdateDiscoveredDevice(DiscoveredSsdpDevice device, IPAddress localIpAddress)
+ private void AddOrUpdateDiscoveredDevice(DiscoveredSsdpDevice device, IPAddress IpAddress)
{
bool isNewDevice = false;
lock (_Devices)
@@ -307,17 +307,17 @@ namespace Rssdp.Infrastructure
}
}
- DeviceFound(device, isNewDevice, localIpAddress);
+ DeviceFound(device, isNewDevice, IpAddress);
}
- private void DeviceFound(DiscoveredSsdpDevice device, bool isNewDevice, IPAddress localIpAddress)
+ private void DeviceFound(DiscoveredSsdpDevice device, bool isNewDevice, IPAddress IpAddress)
{
if (!NotificationTypeMatchesFilter(device))
{
return;
}
- OnDeviceAvailable(device, isNewDevice, localIpAddress);
+ OnDeviceAvailable(device, isNewDevice, IpAddress);
}
private bool NotificationTypeMatchesFilter(DiscoveredSsdpDevice device)
@@ -350,7 +350,7 @@ namespace Rssdp.Infrastructure
return _CommunicationsServer.SendMulticastMessage(message, null, cancellationToken);
}
- private void ProcessSearchResponseMessage(HttpResponseMessage message, IPAddress localIpAddress)
+ private void ProcessSearchResponseMessage(HttpResponseMessage message, IPAddress IpAddress)
{
if (!message.IsSuccessStatusCode)
{
@@ -370,11 +370,11 @@ namespace Rssdp.Infrastructure
ResponseHeaders = message.Headers
};
- AddOrUpdateDiscoveredDevice(device, localIpAddress);
+ AddOrUpdateDiscoveredDevice(device, IpAddress);
}
}
- private void ProcessNotificationMessage(HttpRequestMessage message, IPAddress localIpAddress)
+ private void ProcessNotificationMessage(HttpRequestMessage message, IPAddress IpAddress)
{
if (String.Compare(message.Method.Method, "Notify", StringComparison.OrdinalIgnoreCase) != 0)
{
@@ -384,7 +384,7 @@ namespace Rssdp.Infrastructure
var notificationType = GetFirstHeaderStringValue("NTS", message);
if (String.Compare(notificationType, SsdpConstants.SsdpKeepAliveNotification, StringComparison.OrdinalIgnoreCase) == 0)
{
- ProcessAliveNotification(message, localIpAddress);
+ ProcessAliveNotification(message, IpAddress);
}
else if (String.Compare(notificationType, SsdpConstants.SsdpByeByeNotification, StringComparison.OrdinalIgnoreCase) == 0)
{
@@ -392,7 +392,7 @@ namespace Rssdp.Infrastructure
}
}
- private void ProcessAliveNotification(HttpRequestMessage message, IPAddress localIpAddress)
+ private void ProcessAliveNotification(HttpRequestMessage message, IPAddress IpAddress)
{
var location = GetFirstHeaderUriValue("Location", message);
if (location != null)
@@ -407,7 +407,7 @@ namespace Rssdp.Infrastructure
ResponseHeaders = message.Headers
};
- AddOrUpdateDiscoveredDevice(device, localIpAddress);
+ AddOrUpdateDiscoveredDevice(device, IpAddress);
}
}
@@ -628,7 +628,7 @@ namespace Rssdp.Infrastructure
private void CommsServer_RequestReceived(object sender, RequestReceivedEventArgs e)
{
- ProcessNotificationMessage(e.Message, e.LocalIpAddress);
+ ProcessNotificationMessage(e.Message, e.ReceivedFrom.Address);
}
}
}
diff --git a/apiclient/.openapi-generator-ignore b/apiclient/.openapi-generator-ignore
deleted file mode 100644
index f3802cf54..000000000
--- a/apiclient/.openapi-generator-ignore
+++ /dev/null
@@ -1,2 +0,0 @@
-# Prevent generator from creating these files:
-git_push.sh
diff --git a/apiclient/templates/typescript/axios/generate.sh b/apiclient/templates/typescript/axios/generate.sh
deleted file mode 100644
index 9599f85db..000000000
--- a/apiclient/templates/typescript/axios/generate.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/bash
-
-artifactsDirectory="${1}"
-
-java -jar openapi-generator-cli.jar generate \
- --input-spec ${artifactsDirectory}/openapispec/openapi.json \
- --generator-name typescript-axios \
- --output ./apiclient/generated/typescript/axios \
- --template-dir ./apiclient/templates/typescript/axios \
- --ignore-file-override ./apiclient/.openapi-generator-ignore \
- --additional-properties=useSingleRequestParameter="true",withSeparateModelsAndApi="true",modelPackage="models",apiPackage="api",npmName="axios"
diff --git a/apiclient/templates/typescript/axios/package.mustache b/apiclient/templates/typescript/axios/package.mustache
deleted file mode 100644
index 7bfab08cb..000000000
--- a/apiclient/templates/typescript/axios/package.mustache
+++ /dev/null
@@ -1,30 +0,0 @@
-{
- "name": "@jellyfin/client-axios",
- "version": "10.7.0{{snapshotVersion}}",
- "description": "Jellyfin api client using axios",
- "author": "Jellyfin Contributors",
- "keywords": [
- "axios",
- "typescript",
- "jellyfin"
- ],
- "license": "GPL-3.0-only",
- "main": "./dist/index.js",
- "typings": "./dist/index.d.ts",
- "scripts": {
- "build": "tsc --outDir dist/",
- "prepublishOnly": "npm run build"
- },
- "dependencies": {
- "axios": "^0.19.2"
- },
- "devDependencies": {
- "@types/node": "^12.11.5",
- "typescript": "^3.6.4"
- }{{#npmRepository}},{{/npmRepository}}
-{{#npmRepository}}
- "publishConfig": {
- "registry": "{{npmRepository}}"
- }
-{{/npmRepository}}
-}
diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
index f288561b7..397b863b7 100644
--- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
+++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
@@ -15,9 +15,9 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="AutoFixture" Version="4.15.0" />
- <PackageReference Include="AutoFixture.AutoMoq" Version="4.15.0" />
- <PackageReference Include="AutoFixture.Xunit2" Version="4.15.0" />
+ <PackageReference Include="AutoFixture" Version="4.17.0" />
+ <PackageReference Include="AutoFixture.AutoMoq" Version="4.17.0" />
+ <PackageReference Include="AutoFixture.Xunit2" Version="4.17.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="5.0.5" />
<PackageReference Include="Microsoft.Extensions.Options" Version="5.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
diff --git a/tests/Jellyfin.Common.Tests/Json/JsonGuidConverterTests.cs b/tests/Jellyfin.Common.Tests/Json/JsonGuidConverterTests.cs
index 1e1cde957..dbfad3c2f 100644
--- a/tests/Jellyfin.Common.Tests/Json/JsonGuidConverterTests.cs
+++ b/tests/Jellyfin.Common.Tests/Json/JsonGuidConverterTests.cs
@@ -1,5 +1,4 @@
using System;
-using System.Globalization;
using System.Text.Json;
using MediaBrowser.Common.Json.Converters;
using Xunit;
diff --git a/tests/Jellyfin.Common.Tests/Json/JsonNullableGuidConverterTests.cs b/tests/Jellyfin.Common.Tests/Json/JsonNullableGuidConverterTests.cs
index 22bc7afb9..cb3b66c4c 100644
--- a/tests/Jellyfin.Common.Tests/Json/JsonNullableGuidConverterTests.cs
+++ b/tests/Jellyfin.Common.Tests/Json/JsonNullableGuidConverterTests.cs
@@ -1,5 +1,4 @@
using System;
-using System.Globalization;
using System.Text.Json;
using MediaBrowser.Common.Json.Converters;
using Xunit;
diff --git a/tests/Jellyfin.Dlna.Tests/DlnaManagerTests.cs b/tests/Jellyfin.Dlna.Tests/DlnaManagerTests.cs
new file mode 100644
index 000000000..668bd8f87
--- /dev/null
+++ b/tests/Jellyfin.Dlna.Tests/DlnaManagerTests.cs
@@ -0,0 +1,131 @@
+using Emby.Dlna;
+using Emby.Dlna.PlayTo;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Controller;
+using MediaBrowser.Model.Dlna;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Serialization;
+using Microsoft.Extensions.Logging;
+using Moq;
+using Xunit;
+
+namespace Jellyfin.Dlna.Tests
+{
+ public class DlnaManagerTests
+ {
+ private DlnaManager GetManager()
+ {
+ var xmlSerializer = new Mock<IXmlSerializer>();
+ var fileSystem = new Mock<IFileSystem>();
+ var appPaths = new Mock<IApplicationPaths>();
+ var loggerFactory = new Mock<ILoggerFactory>();
+ var appHost = new Mock<IServerApplicationHost>();
+
+ return new DlnaManager(xmlSerializer.Object, fileSystem.Object, appPaths.Object, loggerFactory.Object, appHost.Object);
+ }
+
+ [Fact]
+ public void IsMatch_GivenMatchingName_ReturnsTrue()
+ {
+ var device = new DeviceInfo()
+ {
+ Name = "My Device",
+ Manufacturer = "LG Electronics",
+ ManufacturerUrl = "http://www.lge.com",
+ ModelDescription = "LG WebOSTV DMRplus",
+ ModelName = "LG TV",
+ ModelNumber = "1.0",
+ };
+
+ var profile = new DeviceProfile()
+ {
+ Name = "Test Profile",
+ FriendlyName = "My Device",
+ Manufacturer = "LG Electronics",
+ ManufacturerUrl = "http://www.lge.com",
+ ModelDescription = "LG WebOSTV DMRplus",
+ ModelName = "LG TV",
+ ModelNumber = "1.0",
+ Identification = new ()
+ {
+ FriendlyName = "My Device",
+ Manufacturer = "LG Electronics",
+ ManufacturerUrl = "http://www.lge.com",
+ ModelDescription = "LG WebOSTV DMRplus",
+ ModelName = "LG TV",
+ ModelNumber = "1.0",
+ }
+ };
+
+ var profile2 = new DeviceProfile()
+ {
+ Name = "Test Profile",
+ FriendlyName = "My Device",
+ Identification = new DeviceIdentification()
+ {
+ FriendlyName = "My Device",
+ }
+ };
+
+ var deviceMatch = GetManager().IsMatch(device.ToDeviceIdentification(), profile2.Identification);
+ var deviceMatch2 = GetManager().IsMatch(device.ToDeviceIdentification(), profile.Identification);
+
+ Assert.True(deviceMatch);
+ Assert.True(deviceMatch2);
+ }
+
+ [Fact]
+ public void IsMatch_GivenNamesAndManufacturersDoNotMatch_ReturnsFalse()
+ {
+ var device = new DeviceInfo()
+ {
+ Name = "My Device",
+ Manufacturer = "JVC"
+ };
+
+ var profile = new DeviceProfile()
+ {
+ Name = "Test Profile",
+ FriendlyName = "My Device",
+ Manufacturer = "LG Electronics",
+ ManufacturerUrl = "http://www.lge.com",
+ ModelDescription = "LG WebOSTV DMRplus",
+ ModelName = "LG TV",
+ ModelNumber = "1.0",
+ Identification = new ()
+ {
+ FriendlyName = "My Device",
+ Manufacturer = "LG Electronics",
+ ManufacturerUrl = "http://www.lge.com",
+ ModelDescription = "LG WebOSTV DMRplus",
+ ModelName = "LG TV",
+ ModelNumber = "1.0",
+ }
+ };
+
+ var deviceMatch = GetManager().IsMatch(device.ToDeviceIdentification(), profile.Identification);
+
+ Assert.False(deviceMatch);
+ }
+
+ [Fact]
+ public void IsMatch_GivenNamesAndRegExMatch_ReturnsTrue()
+ {
+ var device = new DeviceInfo()
+ {
+ Name = "My Device"
+ };
+
+ var profile = new DeviceProfile()
+ {
+ Name = "Test Profile",
+ FriendlyName = "My .*",
+ Identification = new ()
+ };
+
+ var deviceMatch = GetManager().IsMatch(device.ToDeviceIdentification(), profile.Identification);
+
+ Assert.True(deviceMatch);
+ }
+ }
+}
diff --git a/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj b/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj
index c2c0dca1b..f7c21f072 100644
--- a/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj
+++ b/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj
@@ -10,7 +10,8 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
+ <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1" />
+ <PackageReference Include="Moq" Version="4.16.1" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
<PackageReference Include="coverlet.collector" Version="3.0.3" />
diff --git a/tests/Jellyfin.Model.Tests/Dlna/ContainerProfileTests.cs b/tests/Jellyfin.Model.Tests/Dlna/ContainerProfileTests.cs
new file mode 100644
index 000000000..cca056c28
--- /dev/null
+++ b/tests/Jellyfin.Model.Tests/Dlna/ContainerProfileTests.cs
@@ -0,0 +1,19 @@
+using MediaBrowser.Model.Dlna;
+using Xunit;
+
+namespace Jellyfin.Model.Tests.Dlna
+{
+ public class ContainerProfileTests
+ {
+ private readonly ContainerProfile _emptyContainerProfile = new ContainerProfile();
+
+ [Theory]
+ [InlineData(null)]
+ [InlineData("")]
+ [InlineData("mp4")]
+ public void ContainsContainer_EmptyContainerProfile_True(string? containers)
+ {
+ Assert.True(_emptyContainerProfile.ContainsContainer(containers));
+ }
+ }
+}
diff --git a/tests/Jellyfin.Model.Tests/Extensions/StringHelperTests.cs b/tests/Jellyfin.Model.Tests/Extensions/StringHelperTests.cs
index 51633e157..5864a0509 100644
--- a/tests/Jellyfin.Model.Tests/Extensions/StringHelperTests.cs
+++ b/tests/Jellyfin.Model.Tests/Extensions/StringHelperTests.cs
@@ -1,4 +1,3 @@
-using System;
using MediaBrowser.Model.Extensions;
using Xunit;
diff --git a/tests/Jellyfin.Naming.Tests/AudioBook/AudioBookListResolverTests.cs b/tests/Jellyfin.Naming.Tests/AudioBook/AudioBookListResolverTests.cs
index e5768b620..d9e77dd2e 100644
--- a/tests/Jellyfin.Naming.Tests/AudioBook/AudioBookListResolverTests.cs
+++ b/tests/Jellyfin.Naming.Tests/AudioBook/AudioBookListResolverTests.cs
@@ -1,5 +1,4 @@
using System;
-using System.Collections.Generic;
using System.Linq;
using Emby.Naming.AudioBook;
using Emby.Naming.Common;
diff --git a/tests/Jellyfin.Naming.Tests/AudioBook/AudioBookResolverTests.cs b/tests/Jellyfin.Naming.Tests/AudioBook/AudioBookResolverTests.cs
index ad63adadc..53b35c2d6 100644
--- a/tests/Jellyfin.Naming.Tests/AudioBook/AudioBookResolverTests.cs
+++ b/tests/Jellyfin.Naming.Tests/AudioBook/AudioBookResolverTests.cs
@@ -1,4 +1,3 @@
-using System;
using System.Collections.Generic;
using Emby.Naming.AudioBook;
using Emby.Naming.Common;
diff --git a/tests/Jellyfin.Naming.Tests/Subtitles/SubtitleParserTests.cs b/tests/Jellyfin.Naming.Tests/Subtitles/SubtitleParserTests.cs
index f3abacb4f..2446660f3 100644
--- a/tests/Jellyfin.Naming.Tests/Subtitles/SubtitleParserTests.cs
+++ b/tests/Jellyfin.Naming.Tests/Subtitles/SubtitleParserTests.cs
@@ -1,4 +1,3 @@
-using System;
using Emby.Naming.Common;
using Emby.Naming.Subtitles;
using Xunit;
diff --git a/tests/Jellyfin.Naming.Tests/Video/ExtraTests.cs b/tests/Jellyfin.Naming.Tests/Video/ExtraTests.cs
index d34f65409..2f173b0ce 100644
--- a/tests/Jellyfin.Naming.Tests/Video/ExtraTests.cs
+++ b/tests/Jellyfin.Naming.Tests/Video/ExtraTests.cs
@@ -1,4 +1,3 @@
-using System;
using Emby.Naming.Common;
using Emby.Naming.Video;
using MediaBrowser.Model.Entities;
diff --git a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs
index 9b0da2b3c..671b8598d 100644
--- a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs
+++ b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs
@@ -384,6 +384,9 @@ namespace Jellyfin.Networking.Tests
[InlineData("jellyfin.org", "eth16", false, "eth16")]
// User on external network, no binding - so result is the 1st external.
[InlineData("jellyfin.org", "", false, "eth11")]
+ // Dns failure - should skip the test.
+ // https://en.wikipedia.org/wiki/.test
+ [InlineData("invalid.domain.test", "", false, "eth11")]
// User assumed to be internal, no binding - so result is the 1st internal.
[InlineData("", "", false, "eth16")]
public void TestBindInterfaces(string source, string bindAddresses, bool ipv6enabled, string result)
@@ -416,10 +419,13 @@ namespace Jellyfin.Networking.Tests
_ = nm.TryParseInterface(result, out Collection<IPObject>? resultObj);
- if (resultObj != null)
+ // Check to see if dns resolution is working. If not, skip test.
+ _ = IPHost.TryParse(source, out var host);
+
+ if (resultObj != null && host?.HasAddress == true)
{
result = ((IPNetAddress)resultObj[0]).ToString(true);
- var intf = nm.GetBindInterface(source, out int? _);
+ var intf = nm.GetBindInterface(source, out _);
Assert.Equal(intf, result);
}
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Data/SqliteItemRepositoryTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Data/SqliteItemRepositoryTests.cs
new file mode 100644
index 000000000..71f8c5181
--- /dev/null
+++ b/tests/Jellyfin.Server.Implementations.Tests/Data/SqliteItemRepositoryTests.cs
@@ -0,0 +1,250 @@
+using System;
+using System.Collections.Generic;
+using AutoFixture;
+using AutoFixture.AutoMoq;
+using Emby.Server.Implementations.Data;
+using MediaBrowser.Controller;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Model.Entities;
+using Moq;
+using Xunit;
+
+namespace Jellyfin.Server.Implementations.Tests.Data
+{
+ public class SqliteItemRepositoryTests
+ {
+ public const string VirtualMetaDataPath = "%MetadataPath%";
+ public const string MetaDataPath = "/meta/data/path";
+
+ private readonly IFixture _fixture;
+ private readonly SqliteItemRepository _sqliteItemRepository;
+
+ public SqliteItemRepositoryTests()
+ {
+ var appHost = new Mock<IServerApplicationHost>();
+ appHost.Setup(x => x.ExpandVirtualPath(It.IsAny<string>()))
+ .Returns((string x) => x.Replace(VirtualMetaDataPath, MetaDataPath, StringComparison.Ordinal));
+ appHost.Setup(x => x.ReverseVirtualPath(It.IsAny<string>()))
+ .Returns((string x) => x.Replace(MetaDataPath, VirtualMetaDataPath, StringComparison.Ordinal));
+
+ _fixture = new Fixture().Customize(new AutoMoqCustomization { ConfigureMembers = true });
+ _fixture.Inject(appHost);
+ _sqliteItemRepository = _fixture.Create<SqliteItemRepository>();
+ }
+
+ public static IEnumerable<object[]> ItemImageInfoFromValueString_Valid_TestData()
+ {
+ yield return new object[]
+ {
+ "/mnt/series/Family Guy/Season 1/Family Guy - S01E01-thumb.jpg*637452096478512963*Primary*1920*1080*WjQbtJtSO8nhNZ%L_Io#R/oaS6o}-;adXAoIn7j[%hW9s:WGw[nN",
+ new ItemImageInfo
+ {
+ Path = "/mnt/series/Family Guy/Season 1/Family Guy - S01E01-thumb.jpg",
+ Type = ImageType.Primary,
+ DateModified = new DateTime(637452096478512963, DateTimeKind.Utc),
+ Width = 1920,
+ Height = 1080,
+ BlurHash = "WjQbtJtSO8nhNZ%L_Io#R*oaS6o}-;adXAoIn7j[%hW9s:WGw[nN"
+ }
+ };
+
+ yield return new object[]
+ {
+ "https://image.tmdb.org/t/p/original/zhB5CHEgqqh4wnEqDNJLfWXJlcL.jpg*0*Primary*0*0",
+ new ItemImageInfo
+ {
+ Path = "https://image.tmdb.org/t/p/original/zhB5CHEgqqh4wnEqDNJLfWXJlcL.jpg",
+ Type = ImageType.Primary,
+ }
+ };
+
+ yield return new object[]
+ {
+ "https://image.tmdb.org/t/p/original/zhB5CHEgqqh4wnEqDNJLfWXJlcL.jpg*0*Primary",
+ new ItemImageInfo
+ {
+ Path = "https://image.tmdb.org/t/p/original/zhB5CHEgqqh4wnEqDNJLfWXJlcL.jpg",
+ Type = ImageType.Primary,
+ }
+ };
+
+ yield return new object[]
+ {
+ "https://image.tmdb.org/t/p/original/zhB5CHEgqqh4wnEqDNJLfWXJlcL.jpg*0*Primary*600",
+ new ItemImageInfo
+ {
+ Path = "https://image.tmdb.org/t/p/original/zhB5CHEgqqh4wnEqDNJLfWXJlcL.jpg",
+ Type = ImageType.Primary,
+ }
+ };
+
+ yield return new object[]
+ {
+ "%MetadataPath%/library/68/68578562b96c80a7ebd530848801f645/poster.jpg*637264380567586027*Primary*600*336",
+ new ItemImageInfo
+ {
+ Path = "/meta/data/path/library/68/68578562b96c80a7ebd530848801f645/poster.jpg",
+ Type = ImageType.Primary,
+ DateModified = new DateTime(637264380567586027, DateTimeKind.Utc),
+ Width = 600,
+ Height = 336
+ }
+ };
+ }
+
+ [Theory]
+ [MemberData(nameof(ItemImageInfoFromValueString_Valid_TestData))]
+ public void ItemImageInfoFromValueString_Valid_Success(string value, ItemImageInfo expected)
+ {
+ var result = _sqliteItemRepository.ItemImageInfoFromValueString(value);
+ Assert.Equal(expected.Path, result.Path);
+ Assert.Equal(expected.Type, result.Type);
+ Assert.Equal(expected.DateModified, result.DateModified);
+ Assert.Equal(expected.Width, result.Width);
+ Assert.Equal(expected.Height, result.Height);
+ Assert.Equal(expected.BlurHash, result.BlurHash);
+ }
+
+ [Theory]
+ [InlineData("")]
+ [InlineData("*")]
+ [InlineData("https://image.tmdb.org/t/p/original/zhB5CHEgqqh4wnEqDNJLfWXJlcL.jpg*0")]
+ public void ItemImageInfoFromValueString_Invalid_Null(string value)
+ {
+ Assert.Null(_sqliteItemRepository.ItemImageInfoFromValueString(value));
+ }
+
+ public static IEnumerable<object[]> DeserializeImages_Valid_TestData()
+ {
+ yield return new object[]
+ {
+ "/mnt/series/Family Guy/Season 1/Family Guy - S01E01-thumb.jpg*637452096478512963*Primary*1920*1080*WjQbtJtSO8nhNZ%L_Io#R/oaS6o}-;adXAoIn7j[%hW9s:WGw[nN",
+ new ItemImageInfo[]
+ {
+ new ItemImageInfo()
+ {
+ Path = "/mnt/series/Family Guy/Season 1/Family Guy - S01E01-thumb.jpg",
+ Type = ImageType.Primary,
+ DateModified = new DateTime(637452096478512963, DateTimeKind.Utc),
+ Width = 1920,
+ Height = 1080,
+ BlurHash = "WjQbtJtSO8nhNZ%L_Io#R*oaS6o}-;adXAoIn7j[%hW9s:WGw[nN"
+ }
+ }
+ };
+
+ yield return new object[]
+ {
+ "%MetadataPath%/library/2a/2a27372f1e9bc757b1db99721bbeae1e/poster.jpg*637261226720645297*Primary*0*0|%MetadataPath%/library/2a/2a27372f1e9bc757b1db99721bbeae1e/logo.png*637261226720805297*Logo*0*0|%MetadataPath%/library/2a/2a27372f1e9bc757b1db99721bbeae1e/landscape.jpg*637261226721285297*Thumb*0*0|%MetadataPath%/library/2a/2a27372f1e9bc757b1db99721bbeae1e/backdrop.jpg*637261226721685297*Backdrop*0*0",
+ new ItemImageInfo[]
+ {
+ new ItemImageInfo()
+ {
+ Path = "/meta/data/path/library/2a/2a27372f1e9bc757b1db99721bbeae1e/poster.jpg",
+ Type = ImageType.Primary,
+ DateModified = new DateTime(637261226720645297, DateTimeKind.Utc),
+ },
+ new ItemImageInfo()
+ {
+ Path = "/meta/data/path/library/2a/2a27372f1e9bc757b1db99721bbeae1e/logo.png",
+ Type = ImageType.Logo,
+ DateModified = new DateTime(637261226720805297, DateTimeKind.Utc),
+ },
+ new ItemImageInfo()
+ {
+ Path = "/meta/data/path/library/2a/2a27372f1e9bc757b1db99721bbeae1e/landscape.jpg",
+ Type = ImageType.Thumb,
+ DateModified = new DateTime(637261226721285297, DateTimeKind.Utc),
+ },
+ new ItemImageInfo()
+ {
+ Path = "/meta/data/path/library/2a/2a27372f1e9bc757b1db99721bbeae1e/backdrop.jpg",
+ Type = ImageType.Backdrop,
+ DateModified = new DateTime(637261226721685297, DateTimeKind.Utc),
+ }
+ }
+ };
+ }
+
+ [Theory]
+ [MemberData(nameof(DeserializeImages_Valid_TestData))]
+ public void DeserializeImages_Valid_Success(string value, ItemImageInfo[] expected)
+ {
+ var result = _sqliteItemRepository.DeserializeImages(value);
+ Assert.Equal(expected.Length, result.Length);
+ for (int i = 0; i < expected.Length; i++)
+ {
+ Assert.Equal(expected[i].Path, result[i].Path);
+ Assert.Equal(expected[i].Type, result[i].Type);
+ Assert.Equal(expected[i].DateModified, result[i].DateModified);
+ Assert.Equal(expected[i].Width, result[i].Width);
+ Assert.Equal(expected[i].Height, result[i].Height);
+ Assert.Equal(expected[i].BlurHash, result[i].BlurHash);
+ }
+ }
+
+ [Theory]
+ [MemberData(nameof(DeserializeImages_Valid_TestData))]
+ public void SerializeImages_Valid_Success(string expected, ItemImageInfo[] value)
+ {
+ Assert.Equal(expected, _sqliteItemRepository.SerializeImages(value));
+ }
+
+ public static IEnumerable<object[]> DeserializeProviderIds_Valid_TestData()
+ {
+ yield return new object[]
+ {
+ "Imdb=tt0119567",
+ new Dictionary<string, string>()
+ {
+ { "Imdb", "tt0119567" },
+ }
+ };
+
+ yield return new object[]
+ {
+ "Imdb=tt0119567|Tmdb=330|TmdbCollection=328",
+ new Dictionary<string, string>()
+ {
+ { "Imdb", "tt0119567" },
+ { "Tmdb", "330" },
+ { "TmdbCollection", "328" },
+ }
+ };
+
+ yield return new object[]
+ {
+ "MusicBrainzAlbum=9d363e43-f24f-4b39-bc5a-7ef305c677c7|MusicBrainzReleaseGroup=63eba062-847c-3b73-8b0f-6baf27bba6fa|AudioDbArtist=111352|AudioDbAlbum=2116560|MusicBrainzAlbumArtist=20244d07-534f-4eff-b4d4-930878889970",
+ new Dictionary<string, string>()
+ {
+ { "MusicBrainzAlbum", "9d363e43-f24f-4b39-bc5a-7ef305c677c7" },
+ { "MusicBrainzReleaseGroup", "63eba062-847c-3b73-8b0f-6baf27bba6fa" },
+ { "AudioDbArtist", "111352" },
+ { "AudioDbAlbum", "2116560" },
+ { "MusicBrainzAlbumArtist", "20244d07-534f-4eff-b4d4-930878889970" },
+ }
+ };
+ }
+
+ [Theory]
+ [MemberData(nameof(DeserializeProviderIds_Valid_TestData))]
+ public void DeserializeProviderIds_Valid_Success(string value, Dictionary<string, string> expected)
+ {
+ var result = new ProviderIdsExtensionsTestsObject();
+ SqliteItemRepository.DeserializeProviderIds(value, result);
+ Assert.Equal(expected, result.ProviderIds);
+ }
+
+ [Theory]
+ [MemberData(nameof(DeserializeProviderIds_Valid_TestData))]
+ public void SerializeProviderIds_Valid_Success(string expected, Dictionary<string, string> values)
+ {
+ Assert.Equal(expected, SqliteItemRepository.SerializeProviderIds(values));
+ }
+
+ private class ProviderIdsExtensionsTestsObject : IHasProviderIds
+ {
+ public Dictionary<string, string> ProviderIds { get; set; } = new Dictionary<string, string>();
+ }
+ }
+}
diff --git a/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs b/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs
index 5a535ac51..30e6542f9 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs
+++ b/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs
@@ -1,4 +1,3 @@
-using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Runtime.InteropServices;
@@ -43,6 +42,16 @@ namespace Jellyfin.Server.Implementations.Tests.IO
}
}
+ [Theory]
+ [InlineData("ValidFileName", "ValidFileName")]
+ [InlineData("AC/DC", "AC DC")]
+ [InlineData("Invalid\0", "Invalid ")]
+ [InlineData("AC/DC\0KD/A", "AC DC KD A")]
+ public void GetValidFilename_ReturnsValidFilename(string filename, string expectedFileName)
+ {
+ Assert.Equal(expectedFileName, _sut.GetValidFilename(filename));
+ }
+
[SkippableFact]
public void GetFileInfo_DanglingSymlink_ExistsFalse()
{
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 ee59dad5a..27713d58a 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj
+++ b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj
@@ -22,8 +22,8 @@
</ItemGroup>
<ItemGroup>
- <PackageReference Include="AutoFixture" Version="4.15.0" />
- <PackageReference Include="AutoFixture.AutoMoq" Version="4.15.0" />
+ <PackageReference Include="AutoFixture" Version="4.17.0" />
+ <PackageReference Include="AutoFixture.AutoMoq" Version="4.17.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
<PackageReference Include="Moq" Version="4.16.1" />
<PackageReference Include="xunit" Version="2.4.1" />
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Library/EpisodeResolverTest.cs b/tests/Jellyfin.Server.Implementations.Tests/Library/EpisodeResolverTest.cs
index 876519215..c393742eb 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Library/EpisodeResolverTest.cs
+++ b/tests/Jellyfin.Server.Implementations.Tests/Library/EpisodeResolverTest.cs
@@ -6,6 +6,7 @@ using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.IO;
using Moq;
using Xunit;
@@ -28,7 +29,10 @@ namespace Jellyfin.Server.Implementations.Tests.Library
{
Parent = parent,
CollectionType = CollectionType.TvShows,
- Path = "All My Children/Season 01/Extras/All My Children S01E01 - Behind The Scenes.mkv"
+ FileInfo = new FileSystemMetadata()
+ {
+ FullName = "All My Children/Season 01/Extras/All My Children S01E01 - Behind The Scenes.mkv"
+ }
};
Assert.Null(episodeResolver.Resolve(itemResolveArgs));
@@ -48,7 +52,10 @@ namespace Jellyfin.Server.Implementations.Tests.Library
{
Parent = series,
CollectionType = CollectionType.TvShows,
- Path = "Extras/Extras S01E01.mkv"
+ FileInfo = new FileSystemMetadata()
+ {
+ FullName = "Extras/Extras S01E01.mkv"
+ }
};
Assert.NotNull(episodeResolver.Resolve(itemResolveArgs));
}
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs
index e5508243f..c5cc056f5 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs
+++ b/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs
@@ -33,6 +33,7 @@ namespace Jellyfin.Server.Implementations.Tests.Library
[InlineData("C:\\Users\\jeff\\myfile.mkv", "C:\\Users/jeff", "/home/jeff/", "/home/jeff/myfile.mkv")]
[InlineData("C:\\Users\\jeff\\myfile.mkv", "C:\\Users/jeff/", "/home/jeff/", "/home/jeff/myfile.mkv")]
[InlineData("C:\\Users\\jeff\\myfile.mkv", "C:\\Users/jeff/", "/", "/myfile.mkv")]
+ [InlineData("/o", "/o", "/s", "/s")] // regression test for #5977
public void TryReplaceSubPath_ValidArgs_Correct(string path, string subPath, string newSubPath, string? expectedResult)
{
Assert.True(PathExtensions.TryReplaceSubPath(path, subPath, newSubPath, out var result));
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 8d4d9e3d2..938385a2a 100644
--- a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj
+++ b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj
@@ -9,9 +9,9 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="AutoFixture" Version="4.15.0" />
- <PackageReference Include="AutoFixture.AutoMoq" Version="4.15.0" />
- <PackageReference Include="AutoFixture.Xunit2" Version="4.15.0" />
+ <PackageReference Include="AutoFixture" Version="4.17.0" />
+ <PackageReference Include="AutoFixture.AutoMoq" Version="4.17.0" />
+ <PackageReference Include="AutoFixture.Xunit2" Version="4.17.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="5.0.5" />
<PackageReference Include="Microsoft.Extensions.Options" Version="5.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
@@ -22,6 +22,13 @@
<PackageReference Include="Moq" Version="4.16.0" />
</ItemGroup>
+ <ItemGroup>
+ <!-- Don't run tests in parallel -->
+ <None Update="xunit.runner.json">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </None>
+ </ItemGroup>
+
<!-- Code Analyzers -->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
diff --git a/tests/Jellyfin.Server.Integration.Tests/OpenApiSpecTests.cs b/tests/Jellyfin.Server.Integration.Tests/OpenApiSpecTests.cs
index 3cbd638f9..0ade345a1 100644
--- a/tests/Jellyfin.Server.Integration.Tests/OpenApiSpecTests.cs
+++ b/tests/Jellyfin.Server.Integration.Tests/OpenApiSpecTests.cs
@@ -1,8 +1,6 @@
using System.IO;
using System.Reflection;
-using System.Text.Json;
using System.Threading.Tasks;
-using MediaBrowser.Model.Branding;
using Xunit;
using Xunit.Abstractions;
diff --git a/tests/Jellyfin.Server.Integration.Tests/TestAppHost.cs b/tests/Jellyfin.Server.Integration.Tests/TestAppHost.cs
index 4e5d0fcb6..0a463cfa3 100644
--- a/tests/Jellyfin.Server.Integration.Tests/TestAppHost.cs
+++ b/tests/Jellyfin.Server.Integration.Tests/TestAppHost.cs
@@ -1,7 +1,6 @@
using System.Collections.Generic;
using System.Reflection;
using Emby.Server.Implementations;
-using Jellyfin.Server;
using MediaBrowser.Controller;
using MediaBrowser.Model.IO;
using Microsoft.Extensions.Configuration;
diff --git a/tests/Jellyfin.Server.Integration.Tests/xunit.runner.json b/tests/Jellyfin.Server.Integration.Tests/xunit.runner.json
new file mode 100644
index 000000000..809e880c7
--- /dev/null
+++ b/tests/Jellyfin.Server.Integration.Tests/xunit.runner.json
@@ -0,0 +1,4 @@
+{
+ "parallelizeAssembly": false,
+ "parallelizeTestCollections": false
+}
diff --git a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj
index 4a5cf1fee..72e40ebcb 100644
--- a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj
+++ b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj
@@ -10,9 +10,9 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="AutoFixture" Version="4.15.0" />
- <PackageReference Include="AutoFixture.AutoMoq" Version="4.15.0" />
- <PackageReference Include="AutoFixture.Xunit2" Version="4.15.0" />
+ <PackageReference Include="AutoFixture" Version="4.17.0" />
+ <PackageReference Include="AutoFixture.AutoMoq" Version="4.17.0" />
+ <PackageReference Include="AutoFixture.Xunit2" Version="4.17.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="5.0.5" />
<PackageReference Include="Microsoft.Extensions.Options" Version="5.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
diff --git a/tests/Jellyfin.Server.Tests/ParseNetworkTests.cs b/tests/Jellyfin.Server.Tests/ParseNetworkTests.cs
index 0b714e80a..146b16cf9 100644
--- a/tests/Jellyfin.Server.Tests/ParseNetworkTests.cs
+++ b/tests/Jellyfin.Server.Tests/ParseNetworkTests.cs
@@ -1,4 +1,3 @@
-using System;
using System.Globalization;
using System.Text;
using Jellyfin.Networking.Configuration;
diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs
index b58151b3b..30a48857a 100644
--- a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs
+++ b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs
@@ -156,7 +156,7 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers
Assert.Equal("Justice League Collection", item.CollectionName);
// Images
- Assert.Equal(6, result.RemoteImages.Count);
+ Assert.Equal(7, result.RemoteImages.Count);
var posters = result.RemoteImages.Where(x => x.type == ImageType.Primary).ToList();
Assert.Single(posters);
@@ -182,6 +182,10 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers
Assert.Single(discArt);
Assert.Equal("https://assets.fanart.tv/fanart/movies/141052/moviedisc/justice-league-5a3af26360617.png", discArt[0].url);
+ var backdrop = result.RemoteImages.Where(x => x.type == ImageType.Backdrop).ToList();
+ Assert.Single(backdrop);
+ Assert.Equal("https://assets.fanart.tv/fanart/movies/141052/moviebackground/justice-league-5793f518c6d6e.jpg", backdrop[0].url);
+
// Local Image - contains only one item depending on operating system
Assert.Single(result.Images);
Assert.Equal(_localImageFileMetadata.Name, result.Images[0].FileInfo.Name);
diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicArtistNfoParserTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicArtistNfoParserTests.cs
index 3d8e13e96..8ca3dd96e 100644
--- a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicArtistNfoParserTests.cs
+++ b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicArtistNfoParserTests.cs
@@ -1,5 +1,4 @@
using System;
-using System.Linq;
using System.Threading;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Entities.Audio;
diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Test Data/Justice League.nfo b/tests/Jellyfin.XbmcMetadata.Tests/Test Data/Justice League.nfo
index b0c5e3c57..4e8c79dca 100644
--- a/tests/Jellyfin.XbmcMetadata.Tests/Test Data/Justice League.nfo
+++ b/tests/Jellyfin.XbmcMetadata.Tests/Test Data/Justice League.nfo
@@ -82,8 +82,8 @@
<thumb aspect="discart" preview="https://assets.fanart.tv/preview/movies/141052/moviedisc/justice-league-5a0b913c233be.png">https://assets.fanart.tv/fanart/movies/141052/moviedisc/justice-league-5a0b913c233be.png</thumb>
<thumb aspect="discart" preview="https://assets.fanart.tv/preview/movies/141052/moviedisc/justice-league-5a87e0cdb1209.png">https://assets.fanart.tv/fanart/movies/141052/moviedisc/justice-league-5a87e0cdb1209.png</thumb>
<thumb aspect="discart" preview="https://assets.fanart.tv/preview/movies/141052/moviedisc/justice-league-59dc595362ef1.png">https://assets.fanart.tv/fanart/movies/141052/moviedisc/justice-league-59dc595362ef1.png</thumb>
+ <thumb aspect="fanart">https://assets.fanart.tv/fanart/movies/141052/moviebackground/justice-league-5793f518c6d6e.jpg</thumb>
<fanart>
- <thumb preview="https://assets.fanart.tv/preview/movies/141052/moviebackground/justice-league-5793f518c6d6e.jpg">https://assets.fanart.tv/fanart/movies/141052/moviebackground/justice-league-5793f518c6d6e.jpg</thumb>
<thumb preview="https://assets.fanart.tv/preview/movies/141052/moviebackground/justice-league-5a5332c7b5e77.jpg">https://assets.fanart.tv/fanart/movies/141052/moviebackground/justice-league-5a5332c7b5e77.jpg</thumb>
<thumb preview="https://assets.fanart.tv/preview/movies/141052/moviebackground/justice-league-5a53cf2dac1c8.jpg">https://assets.fanart.tv/fanart/movies/141052/moviebackground/justice-league-5a53cf2dac1c8.jpg</thumb>
<thumb preview="https://assets.fanart.tv/preview/movies/141052/moviebackground/justice-league-5976ba93eb5d3.jpg">https://assets.fanart.tv/fanart/movies/141052/moviebackground/justice-league-5976ba93eb5d3.jpg</thumb>