aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.ci/azure-pipelines-abi.yml (renamed from .ci/azure-pipelines-compat.yml)31
-rw-r--r--.ci/azure-pipelines-main.yml44
-rw-r--r--.ci/azure-pipelines-test.yml6
-rw-r--r--.ci/azure-pipelines.yml16
-rw-r--r--.github/dependabot.yml9
-rw-r--r--.vscode/tasks.json12
-rw-r--r--CONTRIBUTORS.md1
-rw-r--r--Dockerfile.arm2
-rw-r--r--DvdLib/Ifo/Program.cs2
-rw-r--r--Emby.Dlna/ContentDirectory/ContentDirectory.cs16
-rw-r--r--Emby.Dlna/ContentDirectory/ControlHandler.cs33
-rw-r--r--Emby.Dlna/Didl/DidlBuilder.cs14
-rw-r--r--Emby.Dlna/DlnaManager.cs4
-rw-r--r--Emby.Dlna/Main/DlnaEntryPoint.cs20
-rw-r--r--Emby.Dlna/PlayTo/Device.cs20
-rw-r--r--Emby.Dlna/PlayTo/PlayToController.cs29
-rw-r--r--Emby.Dlna/PlayTo/PlayToManager.cs3
-rw-r--r--Emby.Dlna/Service/BaseService.cs2
-rw-r--r--Emby.Dlna/Ssdp/DeviceDiscovery.cs16
-rw-r--r--Emby.Dlna/Ssdp/Extensions.cs14
-rw-r--r--Emby.Drawing/ImageProcessor.cs38
-rw-r--r--Emby.Drawing/NullImageEncoder.cs6
-rw-r--r--Emby.Naming/Common/NamingOptions.cs2
-rw-r--r--Emby.Notifications/Api/NotificationsService.cs10
-rw-r--r--Emby.Notifications/NotificationEntryPoint.cs2
-rw-r--r--Emby.Notifications/NotificationManager.cs10
-rw-r--r--Emby.Photos/PhotoProvider.cs4
-rw-r--r--Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs74
-rw-r--r--Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs4
-rw-r--r--Emby.Server.Implementations/ApplicationHost.cs16
-rw-r--r--Emby.Server.Implementations/Browser/BrowserLauncher.cs2
-rw-r--r--Emby.Server.Implementations/Channels/ChannelManager.cs17
-rw-r--r--Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs2
-rw-r--r--Emby.Server.Implementations/Collections/CollectionManager.cs7
-rw-r--r--Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs2
-rw-r--r--Emby.Server.Implementations/Cryptography/CryptographyProvider.cs4
-rw-r--r--Emby.Server.Implementations/Data/BaseSqliteRepository.cs4
-rw-r--r--Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs2
-rw-r--r--Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs2
-rw-r--r--Emby.Server.Implementations/Data/SqliteItemRepository.cs44
-rw-r--r--Emby.Server.Implementations/Data/SqliteUserDataRepository.cs1
-rw-r--r--Emby.Server.Implementations/Data/SqliteUserRepository.cs240
-rw-r--r--Emby.Server.Implementations/Devices/DeviceId.cs4
-rw-r--r--Emby.Server.Implementations/Devices/DeviceManager.cs37
-rw-r--r--Emby.Server.Implementations/Dto/DtoService.cs112
-rw-r--r--Emby.Server.Implementations/Emby.Server.Implementations.csproj11
-rw-r--r--Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs2
-rw-r--r--Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs7
-rw-r--r--Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs23
-rw-r--r--Emby.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs77
-rw-r--r--Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs69
-rw-r--r--Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs14
-rw-r--r--Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs2
-rw-r--r--Emby.Server.Implementations/HttpServer/HttpListenerHost.cs8
-rw-r--r--Emby.Server.Implementations/HttpServer/HttpResultFactory.cs17
-rw-r--r--Emby.Server.Implementations/HttpServer/ResponseFilter.cs1
-rw-r--r--Emby.Server.Implementations/HttpServer/Security/AuthService.cs26
-rw-r--r--Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs4
-rw-r--r--Emby.Server.Implementations/HttpServer/Security/SessionContext.cs2
-rw-r--r--Emby.Server.Implementations/HttpServer/WebSocketConnection.cs42
-rw-r--r--Emby.Server.Implementations/IO/LibraryMonitor.cs2
-rw-r--r--Emby.Server.Implementations/IO/ManagedFileSystem.cs2
-rw-r--r--Emby.Server.Implementations/IStartupOptions.cs9
-rw-r--r--Emby.Server.Implementations/Images/ArtistImageProvider.cs60
-rw-r--r--Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs13
-rw-r--r--Emby.Server.Implementations/Images/CollectionFolderImageProvider.cs (renamed from Emby.Server.Implementations/UserViews/CollectionFolderImageProvider.cs)4
-rw-r--r--Emby.Server.Implementations/Images/DynamicImageProvider.cs (renamed from Emby.Server.Implementations/UserViews/DynamicImageProvider.cs)8
-rw-r--r--Emby.Server.Implementations/Images/FolderImageProvider.cs (renamed from Emby.Server.Implementations/UserViews/FolderImageProvider.cs)14
-rw-r--r--Emby.Server.Implementations/Images/GenreImageProvider.cs (renamed from Emby.Server.Implementations/Playlists/PlaylistImageProvider.cs)79
-rw-r--r--Emby.Server.Implementations/Images/PlaylistImageProvider.cs66
-rw-r--r--Emby.Server.Implementations/Library/LibraryManager.cs128
-rw-r--r--Emby.Server.Implementations/Library/MediaSourceManager.cs55
-rw-r--r--Emby.Server.Implementations/Library/MediaStreamSelector.cs2
-rw-r--r--Emby.Server.Implementations/Library/MusicManager.cs18
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs2
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Movies/BoxSetResolver.cs2
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs4
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs2
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs6
-rw-r--r--Emby.Server.Implementations/Library/SearchEngine.cs5
-rw-r--r--Emby.Server.Implementations/Library/UserDataManager.cs6
-rw-r--r--Emby.Server.Implementations/Library/UserManager.cs1132
-rw-r--r--Emby.Server.Implementations/Library/UserViewManager.cs30
-rw-r--r--Emby.Server.Implementations/Library/Validators/ArtistsPostScanTask.cs4
-rw-r--r--Emby.Server.Implementations/Library/Validators/ArtistsValidator.cs4
-rw-r--r--Emby.Server.Implementations/Library/Validators/GenresPostScanTask.cs4
-rw-r--r--Emby.Server.Implementations/Library/Validators/GenresValidator.cs4
-rw-r--r--Emby.Server.Implementations/Library/Validators/MusicGenresPostScanTask.cs4
-rw-r--r--Emby.Server.Implementations/Library/Validators/MusicGenresValidator.cs4
-rw-r--r--Emby.Server.Implementations/Library/Validators/StudiosPostScanTask.cs4
-rw-r--r--Emby.Server.Implementations/Library/Validators/StudiosValidator.cs4
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs31
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs4
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs4
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs4
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvManager.cs106
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs4
-rw-r--r--Emby.Server.Implementations/Localization/Core/bn.json9
-rw-r--r--Emby.Server.Implementations/Localization/Core/ca.json24
-rw-r--r--Emby.Server.Implementations/Localization/Core/es_419.json117
-rw-r--r--Emby.Server.Implementations/Localization/Core/fr.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/nb.json15
-rw-r--r--Emby.Server.Implementations/Localization/Core/sl-SI.json3
-rw-r--r--Emby.Server.Implementations/Localization/Core/ta.json99
-rw-r--r--Emby.Server.Implementations/Localization/LocalizationManager.cs2
-rw-r--r--Emby.Server.Implementations/MediaEncoder/EncodingManager.cs4
-rw-r--r--Emby.Server.Implementations/Net/SocketFactory.cs2
-rw-r--r--Emby.Server.Implementations/Net/UdpSocket.cs2
-rw-r--r--Emby.Server.Implementations/Networking/NetworkManager.cs4
-rw-r--r--Emby.Server.Implementations/Playlists/ManualPlaylistsFolder.cs5
-rw-r--r--Emby.Server.Implementations/Playlists/PlaylistManager.cs12
-rw-r--r--Emby.Server.Implementations/ResourceFileManager.cs4
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs22
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/TaskManager.cs17
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs11
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs9
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs9
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs9
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs10
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs8
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs17
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Triggers/DailyTrigger.cs7
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Triggers/IntervalTrigger.cs2
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Triggers/StartupTrigger.cs6
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Triggers/WeeklyTrigger.cs2
-rw-r--r--Emby.Server.Implementations/Security/AuthenticationRepository.cs2
-rw-r--r--Emby.Server.Implementations/Serialization/JsonSerializer.cs5
-rw-r--r--Emby.Server.Implementations/Services/HttpResult.cs2
-rw-r--r--Emby.Server.Implementations/Services/RequestHelper.cs7
-rw-r--r--Emby.Server.Implementations/Services/ResponseHelper.cs5
-rw-r--r--Emby.Server.Implementations/Services/ServiceController.cs4
-rw-r--r--Emby.Server.Implementations/Services/ServiceExec.cs2
-rw-r--r--Emby.Server.Implementations/Services/ServiceHandler.cs2
-rw-r--r--Emby.Server.Implementations/Services/ServiceMethod.cs2
-rw-r--r--Emby.Server.Implementations/Services/ServicePath.cs2
-rw-r--r--Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs2
-rw-r--r--Emby.Server.Implementations/Services/SwaggerService.cs2
-rw-r--r--Emby.Server.Implementations/Services/UrlExtensions.cs2
-rw-r--r--Emby.Server.Implementations/Session/SessionManager.cs103
-rw-r--r--Emby.Server.Implementations/Session/SessionWebSocketListener.cs247
-rw-r--r--Emby.Server.Implementations/Session/WebSocketController.cs2
-rw-r--r--Emby.Server.Implementations/SocketSharp/HttpFile.cs18
-rw-r--r--Emby.Server.Implementations/SocketSharp/HttpPostedFile.cs198
-rw-r--r--Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs6
-rw-r--r--Emby.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs2
-rw-r--r--Emby.Server.Implementations/Sorting/CommunityRatingComparer.cs18
-rw-r--r--Emby.Server.Implementations/Sorting/DateLastMediaAddedComparer.cs19
-rw-r--r--Emby.Server.Implementations/Sorting/DatePlayedComparer.cs1
-rw-r--r--Emby.Server.Implementations/Sorting/IsFavoriteOrLikeComparer.cs39
-rw-r--r--Emby.Server.Implementations/Sorting/IsFolderComparer.cs14
-rw-r--r--Emby.Server.Implementations/Sorting/IsPlayedComparer.cs39
-rw-r--r--Emby.Server.Implementations/Sorting/IsUnplayedComparer.cs39
-rw-r--r--Emby.Server.Implementations/Sorting/OfficialRatingComparer.cs14
-rw-r--r--Emby.Server.Implementations/Sorting/PlayCountComparer.cs1
-rw-r--r--Emby.Server.Implementations/Sorting/SeriesSortNameComparer.cs14
-rw-r--r--Emby.Server.Implementations/Sorting/StartDateComparer.cs19
-rw-r--r--Emby.Server.Implementations/Sorting/StudioComparer.cs2
-rw-r--r--Emby.Server.Implementations/SyncPlay/SyncPlayController.cs522
-rw-r--r--Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs380
-rw-r--r--Emby.Server.Implementations/TV/TVSeriesManager.cs19
-rw-r--r--Emby.Server.Implementations/Udp/UdpServer.cs16
-rw-r--r--Emby.Server.Implementations/Updates/InstallationManager.cs139
-rw-r--r--Jellyfin.Api/Auth/CustomAuthenticationHandler.cs5
-rw-r--r--Jellyfin.Api/Controllers/StartupController.cs8
-rw-r--r--Jellyfin.Api/Jellyfin.Api.csproj3
-rw-r--r--Jellyfin.Api/Models/StartupDtos/StartupConfigurationDto.cs2
-rw-r--r--Jellyfin.Api/Models/StartupDtos/StartupUserDto.cs2
-rw-r--r--Jellyfin.Data/DayOfWeekHelper.cs65
-rw-r--r--Jellyfin.Data/Entities/AccessSchedule.cs91
-rw-r--r--Jellyfin.Data/Entities/Group.cs103
-rw-r--r--Jellyfin.Data/Entities/ImageInfo.cs30
-rw-r--r--Jellyfin.Data/Entities/Permission.cs137
-rw-r--r--Jellyfin.Data/Entities/Preference.cs100
-rw-r--r--Jellyfin.Data/Entities/ProviderMapping.cs6
-rw-r--r--Jellyfin.Data/Entities/Series.cs20
-rw-r--r--Jellyfin.Data/Entities/User.cs500
-rw-r--r--Jellyfin.Data/Enums/DynamicDayOfWeek.cs (renamed from MediaBrowser.Model/Configuration/DynamicDayOfWeek.cs)2
-rw-r--r--Jellyfin.Data/Enums/PermissionKind.cs127
-rw-r--r--Jellyfin.Data/Enums/PreferenceKind.cs69
-rw-r--r--Jellyfin.Data/Enums/SubtitlePlaybackMode.cs (renamed from MediaBrowser.Model/Configuration/SubtitlePlaybackMode.cs)4
-rw-r--r--Jellyfin.Data/Enums/SyncPlayAccess.cs23
-rw-r--r--Jellyfin.Data/Enums/UnratedItem.cs (renamed from MediaBrowser.Model/Configuration/UnratedItem.cs)2
-rw-r--r--Jellyfin.Data/Enums/Weekday.cs13
-rw-r--r--Jellyfin.Data/IHasPermissions.cs31
-rw-r--r--Jellyfin.Data/Jellyfin.Data.csproj5
-rw-r--r--Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj6
-rw-r--r--Jellyfin.Drawing.Skia/SkiaEncoder.cs27
-rw-r--r--Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj6
-rw-r--r--Jellyfin.Server.Implementations/JellyfinDb.cs70
-rw-r--r--Jellyfin.Server.Implementations/JellyfinDbProvider.cs2
-rw-r--r--Jellyfin.Server.Implementations/Migrations/20200613202153_AddUsers.Designer.cs312
-rw-r--r--Jellyfin.Server.Implementations/Migrations/20200613202153_AddUsers.cs197
-rw-r--r--Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs244
-rw-r--r--Jellyfin.Server.Implementations/Users/DefaultAuthenticationProvider.cs (renamed from Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs)54
-rw-r--r--Jellyfin.Server.Implementations/Users/DefaultPasswordResetProvider.cs (renamed from Emby.Server.Implementations/Library/DefaultPasswordResetProvider.cs)59
-rw-r--r--Jellyfin.Server.Implementations/Users/DeviceAccessEntryPoint.cs67
-rw-r--r--Jellyfin.Server.Implementations/Users/InvalidAuthProvider.cs (renamed from Emby.Server.Implementations/Library/InvalidAuthProvider.cs)12
-rw-r--r--Jellyfin.Server.Implementations/Users/UserManager.cs841
-rw-r--r--Jellyfin.Server/CoreAppHost.cs13
-rw-r--r--Jellyfin.Server/Jellyfin.Server.csproj6
-rw-r--r--Jellyfin.Server/Migrations/IMigrationRoutine.cs1
-rw-r--r--Jellyfin.Server/Migrations/MigrationRunner.cs3
-rw-r--r--Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs1
-rw-r--r--Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs2
-rw-r--r--Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs36
-rw-r--r--Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs208
-rw-r--r--Jellyfin.Server/Migrations/Routines/RemoveDuplicateExtras.cs2
-rw-r--r--Jellyfin.Server/Program.cs4
-rw-r--r--Jellyfin.Server/StartupOptions.cs12
-rw-r--r--MediaBrowser.Api/ApiEntryPoint.cs6
-rw-r--r--MediaBrowser.Api/BaseApiService.cs5
-rw-r--r--MediaBrowser.Api/Devices/DeviceService.cs28
-rw-r--r--MediaBrowser.Api/EnvironmentService.cs15
-rw-r--r--MediaBrowser.Api/FilterService.cs1
-rw-r--r--MediaBrowser.Api/Images/ImageService.cs182
-rw-r--r--MediaBrowser.Api/Images/RemoteImageService.cs3
-rw-r--r--MediaBrowser.Api/Library/LibraryService.cs18
-rw-r--r--MediaBrowser.Api/LiveTv/LiveTvService.cs3
-rw-r--r--MediaBrowser.Api/Movies/MoviesService.cs15
-rw-r--r--MediaBrowser.Api/Music/AlbumsService.cs10
-rw-r--r--MediaBrowser.Api/Music/InstantMixService.cs1
-rw-r--r--MediaBrowser.Api/Playback/BaseStreamingService.cs13
-rw-r--r--MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs26
-rw-r--r--MediaBrowser.Api/Playback/Hls/VideoHlsService.cs2
-rw-r--r--MediaBrowser.Api/Playback/MediaInfoService.cs36
-rw-r--r--MediaBrowser.Api/Playback/StreamState.cs2
-rw-r--r--MediaBrowser.Api/Sessions/SessionInfoWebSocketListener.cs28
-rw-r--r--MediaBrowser.Api/Sessions/SessionService.cs5
-rw-r--r--MediaBrowser.Api/SimilarItemsHelper.cs2
-rw-r--r--MediaBrowser.Api/SuggestionsService.cs1
-rw-r--r--MediaBrowser.Api/SyncPlay/SyncPlayService.cs302
-rw-r--r--MediaBrowser.Api/SyncPlay/TimeSyncService.cs52
-rw-r--r--MediaBrowser.Api/TvShowsService.cs8
-rw-r--r--MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs1
-rw-r--r--MediaBrowser.Api/UserLibrary/ItemsService.cs19
-rw-r--r--MediaBrowser.Api/UserLibrary/PlaystateService.cs2
-rw-r--r--MediaBrowser.Api/UserLibrary/UserLibraryService.cs2
-rw-r--r--MediaBrowser.Api/UserService.cs75
-rw-r--r--MediaBrowser.Api/VideosService.cs1
-rw-r--r--MediaBrowser.Common/Configuration/ConfigurationStore.cs20
-rw-r--r--MediaBrowser.Common/Configuration/IApplicationPaths.cs22
-rw-r--r--MediaBrowser.Common/Configuration/IConfigurationFactory.cs30
-rw-r--r--MediaBrowser.Common/Configuration/IConfigurationManager.cs2
-rw-r--r--MediaBrowser.Common/Configuration/IValidatingConfiguration.cs15
-rw-r--r--MediaBrowser.Common/MediaBrowser.Common.csproj6
-rw-r--r--MediaBrowser.Common/Net/CacheMode.cs11
-rw-r--r--MediaBrowser.Common/Net/CompressionMethods.cs15
-rw-r--r--MediaBrowser.Common/Net/HttpRequestOptions.cs14
-rw-r--r--MediaBrowser.Common/Progress/ActionableProgress.cs17
-rw-r--r--MediaBrowser.Common/Progress/SimpleProgress.cs16
-rw-r--r--MediaBrowser.Common/Updates/IInstallationManager.cs28
-rw-r--r--MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs2
-rw-r--r--MediaBrowser.Controller/Authentication/IPasswordResetProvider.cs2
-rw-r--r--MediaBrowser.Controller/Channels/Channel.cs10
-rw-r--r--MediaBrowser.Controller/Collections/ICollectionManager.cs1
-rw-r--r--MediaBrowser.Controller/Devices/CameraImageUploadInfo.cs10
-rw-r--r--MediaBrowser.Controller/Devices/IDeviceManager.cs2
-rw-r--r--MediaBrowser.Controller/Drawing/IImageEncoder.cs9
-rw-r--r--MediaBrowser.Controller/Drawing/IImageProcessor.cs11
-rw-r--r--MediaBrowser.Controller/Drawing/ImageHelper.cs1
-rw-r--r--MediaBrowser.Controller/Dto/IDtoService.cs1
-rw-r--r--MediaBrowser.Controller/Entities/Audio/Audio.cs2
-rw-r--r--MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs13
-rw-r--r--MediaBrowser.Controller/Entities/Audio/MusicArtist.cs18
-rw-r--r--MediaBrowser.Controller/Entities/AudioBook.cs2
-rw-r--r--MediaBrowser.Controller/Entities/BaseItem.cs97
-rw-r--r--MediaBrowser.Controller/Entities/Book.cs11
-rw-r--r--MediaBrowser.Controller/Entities/DayOfWeekHelper.cs71
-rw-r--r--MediaBrowser.Controller/Entities/Folder.cs33
-rw-r--r--MediaBrowser.Controller/Entities/InternalItemsQuery.cs14
-rw-r--r--MediaBrowser.Controller/Entities/ItemImageInfo.cs6
-rw-r--r--MediaBrowser.Controller/Entities/Movies/BoxSet.cs8
-rw-r--r--MediaBrowser.Controller/Entities/Movies/Movie.cs4
-rw-r--r--MediaBrowser.Controller/Entities/MusicVideo.cs2
-rw-r--r--MediaBrowser.Controller/Entities/TV/Episode.cs2
-rw-r--r--MediaBrowser.Controller/Entities/TV/Season.cs10
-rw-r--r--MediaBrowser.Controller/Entities/TV/Series.cs35
-rw-r--r--MediaBrowser.Controller/Entities/Trailer.cs4
-rw-r--r--MediaBrowser.Controller/Entities/User.cs262
-rw-r--r--MediaBrowser.Controller/Entities/UserRootFolder.cs1
-rw-r--r--MediaBrowser.Controller/Entities/UserView.cs22
-rw-r--r--MediaBrowser.Controller/Entities/UserViewBuilder.cs63
-rw-r--r--MediaBrowser.Controller/Entities/Video.cs8
-rw-r--r--MediaBrowser.Controller/IO/FileData.cs3
-rw-r--r--MediaBrowser.Controller/Library/IIntroProvider.cs2
-rw-r--r--MediaBrowser.Controller/Library/ILibraryManager.cs24
-rw-r--r--MediaBrowser.Controller/Library/IMediaSourceManager.cs1
-rw-r--r--MediaBrowser.Controller/Library/IMusicManager.cs1
-rw-r--r--MediaBrowser.Controller/Library/IUserDataManager.cs3
-rw-r--r--MediaBrowser.Controller/Library/IUserManager.cs138
-rw-r--r--MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs1
-rw-r--r--MediaBrowser.Controller/Library/Profiler.cs2
-rw-r--r--MediaBrowser.Controller/LiveTv/ILiveTvManager.cs1
-rw-r--r--MediaBrowser.Controller/LiveTv/LiveTvChannel.cs2
-rw-r--r--MediaBrowser.Controller/LiveTv/LiveTvProgram.cs8
-rw-r--r--MediaBrowser.Controller/LiveTv/TimerEventInfo.cs13
-rw-r--r--MediaBrowser.Controller/MediaBrowser.Controller.csproj4
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs25
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs47
-rw-r--r--MediaBrowser.Controller/Net/AuthorizationInfo.cs10
-rw-r--r--MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs29
-rw-r--r--MediaBrowser.Controller/Net/IAuthService.cs3
-rw-r--r--MediaBrowser.Controller/Net/ISessionContext.cs2
-rw-r--r--MediaBrowser.Controller/Net/IWebSocketConnection.cs6
-rw-r--r--MediaBrowser.Controller/Notifications/INotificationService.cs2
-rw-r--r--MediaBrowser.Controller/Notifications/UserNotification.cs2
-rw-r--r--MediaBrowser.Controller/Persistence/IUserRepository.cs27
-rw-r--r--MediaBrowser.Controller/Playlists/Playlist.cs11
-rw-r--r--MediaBrowser.Controller/Providers/IProviderManager.cs3
-rw-r--r--MediaBrowser.Controller/Session/ISessionManager.cs22
-rw-r--r--MediaBrowser.Controller/Sorting/IUserBaseItemComparer.cs3
-rw-r--r--MediaBrowser.Controller/SyncPlay/GroupInfo.cs169
-rw-r--r--MediaBrowser.Controller/SyncPlay/GroupMember.cs28
-rw-r--r--MediaBrowser.Controller/SyncPlay/ISyncPlayController.cs67
-rw-r--r--MediaBrowser.Controller/SyncPlay/ISyncPlayManager.cs69
-rw-r--r--MediaBrowser.LocalMetadata/Images/InternalMetadataFolderImageProvider.cs2
-rw-r--r--MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs10
-rw-r--r--MediaBrowser.LocalMetadata/Parsers/BoxSetXmlParser.cs2
-rw-r--r--MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs2
-rw-r--r--MediaBrowser.LocalMetadata/Providers/BoxSetXmlProvider.cs4
-rw-r--r--MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs4
-rw-r--r--MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs4
-rw-r--r--MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs2
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs2
-rw-r--r--MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj2
-rw-r--r--MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs10
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/AssParser.cs6
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs2
-rw-r--r--MediaBrowser.Model/Activity/ActivityLogEntry.cs1
-rw-r--r--MediaBrowser.Model/ApiClient/ServerDiscoveryInfo.cs1
-rw-r--r--MediaBrowser.Model/Branding/BrandingOptions.cs1
-rw-r--r--MediaBrowser.Model/Channels/ChannelFeatures.cs1
-rw-r--r--MediaBrowser.Model/Channels/ChannelInfo.cs1
-rw-r--r--MediaBrowser.Model/Channels/ChannelQuery.cs6
-rw-r--r--MediaBrowser.Model/Configuration/AccessSchedule.cs2
-rw-r--r--MediaBrowser.Model/Configuration/BaseApplicationConfiguration.cs1
-rw-r--r--MediaBrowser.Model/Configuration/EncodingOptions.cs13
-rw-r--r--MediaBrowser.Model/Configuration/LibraryOptions.cs1
-rw-r--r--MediaBrowser.Model/Configuration/MetadataOptions.cs4
-rw-r--r--MediaBrowser.Model/Configuration/MetadataPlugin.cs1
-rw-r--r--MediaBrowser.Model/Configuration/MetadataPluginSummary.cs1
-rw-r--r--MediaBrowser.Model/Configuration/ServerConfiguration.cs22
-rw-r--r--MediaBrowser.Model/Configuration/UserConfiguration.cs2
-rw-r--r--MediaBrowser.Model/Configuration/XbmcMetadataOptions.cs1
-rw-r--r--MediaBrowser.Model/Cryptography/ICryptoProvider.cs2
-rw-r--r--MediaBrowser.Model/Devices/ContentUploadHistory.cs15
-rw-r--r--MediaBrowser.Model/Devices/DeviceInfo.cs1
-rw-r--r--MediaBrowser.Model/Devices/DeviceOptions.cs2
-rw-r--r--MediaBrowser.Model/Devices/LocalFileInfo.cs12
-rw-r--r--MediaBrowser.Model/Dlna/AudioOptions.cs6
-rw-r--r--MediaBrowser.Model/Dlna/CodecProfile.cs1
-rw-r--r--MediaBrowser.Model/Dlna/ConditionProcessor.cs36
-rw-r--r--MediaBrowser.Model/Dlna/ContainerProfile.cs2
-rw-r--r--MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs6
-rw-r--r--MediaBrowser.Model/Dlna/DeviceIdentification.cs1
-rw-r--r--MediaBrowser.Model/Dlna/DeviceProfile.cs1
-rw-r--r--MediaBrowser.Model/Dlna/DeviceProfileInfo.cs1
-rw-r--r--MediaBrowser.Model/Dlna/DirectPlayProfile.cs1
-rw-r--r--MediaBrowser.Model/Dlna/HttpHeaderInfo.cs1
-rw-r--r--MediaBrowser.Model/Dlna/ITranscoderSupport.cs5
-rw-r--r--MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs24
-rw-r--r--MediaBrowser.Model/Dlna/ProfileCondition.cs25
-rw-r--r--MediaBrowser.Model/Dlna/ResolutionConfiguration.cs1
-rw-r--r--MediaBrowser.Model/Dlna/ResolutionNormalizer.cs5
-rw-r--r--MediaBrowser.Model/Dlna/ResolutionOptions.cs1
-rw-r--r--MediaBrowser.Model/Dlna/ResponseProfile.cs4
-rw-r--r--MediaBrowser.Model/Dlna/SearchCriteria.cs9
-rw-r--r--MediaBrowser.Model/Dlna/StreamBuilder.cs2
-rw-r--r--MediaBrowser.Model/Dlna/StreamInfo.cs1
-rw-r--r--MediaBrowser.Model/Dlna/SubtitleProfile.cs1
-rw-r--r--MediaBrowser.Model/Dlna/SubtitleStreamInfo.cs9
-rw-r--r--MediaBrowser.Model/Dlna/TranscodingProfile.cs1
-rw-r--r--MediaBrowser.Model/Dlna/UpnpDeviceInfo.cs4
-rw-r--r--MediaBrowser.Model/Dlna/VideoOptions.cs1
-rw-r--r--MediaBrowser.Model/Dlna/XmlAttribute.cs1
-rw-r--r--MediaBrowser.Model/Drawing/DrawingUtils.cs5
-rw-r--r--MediaBrowser.Model/Dto/BaseItemDto.cs10
-rw-r--r--MediaBrowser.Model/Dto/BaseItemPerson.cs1
-rw-r--r--MediaBrowser.Model/Dto/IHasServerId.cs1
-rw-r--r--MediaBrowser.Model/Dto/ImageByNameInfo.cs1
-rw-r--r--MediaBrowser.Model/Dto/ImageInfo.cs11
-rw-r--r--MediaBrowser.Model/Dto/ImageOptions.cs17
-rw-r--r--MediaBrowser.Model/Dto/ItemIndex.cs20
-rw-r--r--MediaBrowser.Model/Dto/MediaSourceInfo.cs1
-rw-r--r--MediaBrowser.Model/Dto/MetadataEditorInfo.cs1
-rw-r--r--MediaBrowser.Model/Dto/NameIdPair.cs1
-rw-r--r--MediaBrowser.Model/Dto/NameValuePair.cs2
-rw-r--r--MediaBrowser.Model/Dto/PublicUserDto.cs48
-rw-r--r--MediaBrowser.Model/Dto/RecommendationDto.cs1
-rw-r--r--MediaBrowser.Model/Dto/UserDto.cs1
-rw-r--r--MediaBrowser.Model/Dto/UserItemDataDto.cs1
-rw-r--r--MediaBrowser.Model/Entities/ChapterInfo.cs1
-rw-r--r--MediaBrowser.Model/Entities/DisplayPreferences.cs1
-rw-r--r--MediaBrowser.Model/Entities/IHasProviderIds.cs2
-rw-r--r--MediaBrowser.Model/Entities/ImageType.cs7
-rw-r--r--MediaBrowser.Model/Entities/LibraryUpdateInfo.cs29
-rw-r--r--MediaBrowser.Model/Entities/MediaAttachment.cs1
-rw-r--r--MediaBrowser.Model/Entities/MediaStream.cs1
-rw-r--r--MediaBrowser.Model/Entities/MediaUrl.cs1
-rw-r--r--MediaBrowser.Model/Entities/MetadataFields.cs2
-rw-r--r--MediaBrowser.Model/Entities/MetadataProvider.cs (renamed from MediaBrowser.Model/Entities/MetadataProviders.cs)2
-rw-r--r--MediaBrowser.Model/Entities/PackageReviewInfo.cs13
-rw-r--r--MediaBrowser.Model/Entities/ParentalRating.cs24
-rw-r--r--MediaBrowser.Model/Entities/ProviderIdsExtensions.cs8
-rw-r--r--MediaBrowser.Model/Entities/VirtualFolderInfo.cs1
-rw-r--r--MediaBrowser.Model/Events/GenericEventArgs.cs7
-rw-r--r--MediaBrowser.Model/Extensions/ListHelper.cs29
-rw-r--r--MediaBrowser.Model/Extensions/StringHelper.cs4
-rw-r--r--MediaBrowser.Model/Globalization/CountryInfo.cs1
-rw-r--r--MediaBrowser.Model/Globalization/CultureDto.cs1
-rw-r--r--MediaBrowser.Model/Globalization/ILocalizationManager.cs1
-rw-r--r--MediaBrowser.Model/Globalization/LocalizationOption.cs2
-rw-r--r--MediaBrowser.Model/IO/FileSystemEntryInfo.cs27
-rw-r--r--MediaBrowser.Model/IO/FileSystemMetadata.cs1
-rw-r--r--MediaBrowser.Model/IO/IFileSystem.cs1
-rw-r--r--MediaBrowser.Model/IO/IIsoManager.cs1
-rw-r--r--MediaBrowser.Model/IO/IIsoMount.cs2
-rw-r--r--MediaBrowser.Model/IO/IIsoMounter.cs12
-rw-r--r--MediaBrowser.Model/IO/IStreamHelper.cs1
-rw-r--r--MediaBrowser.Model/Library/UserViewQuery.cs12
-rw-r--r--MediaBrowser.Model/LiveTv/BaseTimerInfoDto.cs1
-rw-r--r--MediaBrowser.Model/LiveTv/GuideInfo.cs1
-rw-r--r--MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs1
-rw-r--r--MediaBrowser.Model/LiveTv/LiveTvInfo.cs12
-rw-r--r--MediaBrowser.Model/LiveTv/LiveTvOptions.cs1
-rw-r--r--MediaBrowser.Model/LiveTv/LiveTvServiceInfo.cs1
-rw-r--r--MediaBrowser.Model/LiveTv/RecordingQuery.cs1
-rw-r--r--MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs1
-rw-r--r--MediaBrowser.Model/LiveTv/SeriesTimerQuery.cs2
-rw-r--r--MediaBrowser.Model/LiveTv/TimerInfoDto.cs1
-rw-r--r--MediaBrowser.Model/LiveTv/TimerQuery.cs1
-rw-r--r--MediaBrowser.Model/MediaBrowser.Model.csproj4
-rw-r--r--MediaBrowser.Model/MediaInfo/AudioCodec.cs4
-rw-r--r--MediaBrowser.Model/MediaInfo/BlurayDiscInfo.cs1
-rw-r--r--MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs34
-rw-r--r--MediaBrowser.Model/MediaInfo/LiveStreamResponse.cs7
-rw-r--r--MediaBrowser.Model/MediaInfo/MediaInfo.cs1
-rw-r--r--MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs1
-rw-r--r--MediaBrowser.Model/MediaInfo/PlaybackInfoResponse.cs2
-rw-r--r--MediaBrowser.Model/MediaInfo/SubtitleTrackEvent.cs4
-rw-r--r--MediaBrowser.Model/Net/EndPointInfo.cs1
-rw-r--r--MediaBrowser.Model/Net/ISocket.cs1
-rw-r--r--MediaBrowser.Model/Net/MimeTypes.cs18
-rw-r--r--MediaBrowser.Model/Net/NetworkShare.cs1
-rw-r--r--MediaBrowser.Model/Net/SocketReceiveResult.cs10
-rw-r--r--MediaBrowser.Model/Net/WebSocketMessage.cs2
-rw-r--r--MediaBrowser.Model/Notifications/NotificationOption.cs16
-rw-r--r--MediaBrowser.Model/Notifications/NotificationOptions.cs38
-rw-r--r--MediaBrowser.Model/Notifications/NotificationRequest.cs1
-rw-r--r--MediaBrowser.Model/Notifications/NotificationTypeInfo.cs1
-rw-r--r--MediaBrowser.Model/Playlists/PlaylistCreationRequest.cs1
-rw-r--r--MediaBrowser.Model/Playlists/PlaylistCreationResult.cs7
-rw-r--r--MediaBrowser.Model/Playlists/PlaylistItemQuery.cs39
-rw-r--r--MediaBrowser.Model/Plugins/PluginInfo.cs1
-rw-r--r--MediaBrowser.Model/Plugins/PluginPageInfo.cs1
-rw-r--r--MediaBrowser.Model/Providers/ExternalIdInfo.cs1
-rw-r--r--MediaBrowser.Model/Providers/ExternalUrl.cs1
-rw-r--r--MediaBrowser.Model/Providers/ImageProviderInfo.cs28
-rw-r--r--MediaBrowser.Model/Providers/RemoteImageInfo.cs1
-rw-r--r--MediaBrowser.Model/Providers/RemoteImageQuery.cs7
-rw-r--r--MediaBrowser.Model/Providers/RemoteImageResult.cs1
-rw-r--r--MediaBrowser.Model/Providers/RemoteSearchResult.cs20
-rw-r--r--MediaBrowser.Model/Providers/RemoteSubtitleInfo.cs1
-rw-r--r--MediaBrowser.Model/Providers/SubtitleOptions.cs1
-rw-r--r--MediaBrowser.Model/Providers/SubtitleProviderInfo.cs1
-rw-r--r--MediaBrowser.Model/Querying/AllThemeMediaResult.cs13
-rw-r--r--MediaBrowser.Model/Querying/EpisodeQuery.cs1
-rw-r--r--MediaBrowser.Model/Querying/ItemCountsQuery.cs20
-rw-r--r--MediaBrowser.Model/Querying/ItemSortBy.cs54
-rw-r--r--MediaBrowser.Model/Querying/LatestItemsQuery.cs20
-rw-r--r--MediaBrowser.Model/Querying/MovieRecommendationQuery.cs1
-rw-r--r--MediaBrowser.Model/Querying/NextUpQuery.cs1
-rw-r--r--MediaBrowser.Model/Querying/QueryFilters.cs1
-rw-r--r--MediaBrowser.Model/Querying/QueryResult.cs1
-rw-r--r--MediaBrowser.Model/Querying/ThemeMediaResult.cs2
-rw-r--r--MediaBrowser.Model/Querying/UpcomingEpisodesQuery.cs1
-rw-r--r--MediaBrowser.Model/Search/SearchHint.cs1
-rw-r--r--MediaBrowser.Model/Search/SearchHintResult.cs1
-rw-r--r--MediaBrowser.Model/Search/SearchQuery.cs1
-rw-r--r--MediaBrowser.Model/Serialization/IJsonSerializer.cs1
-rw-r--r--MediaBrowser.Model/Serialization/IXmlSerializer.cs1
-rw-r--r--MediaBrowser.Model/Services/ApiMemberAttribute.cs1
-rw-r--r--MediaBrowser.Model/Services/IHasRequestFilter.cs2
-rw-r--r--MediaBrowser.Model/Services/IHttpRequest.cs4
-rw-r--r--MediaBrowser.Model/Services/IHttpResult.cs11
-rw-r--r--MediaBrowser.Model/Services/IRequest.cs1
-rw-r--r--MediaBrowser.Model/Services/QueryParamCollection.cs6
-rw-r--r--MediaBrowser.Model/Services/RouteAttribute.cs1
-rw-r--r--MediaBrowser.Model/Session/BrowseRequest.cs1
-rw-r--r--MediaBrowser.Model/Session/ClientCapabilities.cs1
-rw-r--r--MediaBrowser.Model/Session/GeneralCommand.cs1
-rw-r--r--MediaBrowser.Model/Session/MessageCommand.cs1
-rw-r--r--MediaBrowser.Model/Session/PlayRequest.cs1
-rw-r--r--MediaBrowser.Model/Session/PlaybackProgressInfo.cs1
-rw-r--r--MediaBrowser.Model/Session/PlaybackStopInfo.cs1
-rw-r--r--MediaBrowser.Model/Session/PlayerStateInfo.cs1
-rw-r--r--MediaBrowser.Model/Session/PlaystateRequest.cs2
-rw-r--r--MediaBrowser.Model/Session/SessionUserInfo.cs2
-rw-r--r--MediaBrowser.Model/Session/TranscodingInfo.cs5
-rw-r--r--MediaBrowser.Model/Session/UserDataChangeInfo.cs1
-rw-r--r--MediaBrowser.Model/Sync/SyncJob.cs1
-rw-r--r--MediaBrowser.Model/Sync/SyncTarget.cs1
-rw-r--r--MediaBrowser.Model/SyncPlay/GroupInfoView.cs42
-rw-r--r--MediaBrowser.Model/SyncPlay/GroupUpdate.cs28
-rw-r--r--MediaBrowser.Model/SyncPlay/GroupUpdateType.cs53
-rw-r--r--MediaBrowser.Model/SyncPlay/JoinGroupRequest.cs22
-rw-r--r--MediaBrowser.Model/SyncPlay/PlaybackRequest.cs34
-rw-r--r--MediaBrowser.Model/SyncPlay/PlaybackRequestType.cs33
-rw-r--r--MediaBrowser.Model/SyncPlay/SendCommand.cs40
-rw-r--r--MediaBrowser.Model/SyncPlay/SendCommandType.cs21
-rw-r--r--MediaBrowser.Model/SyncPlay/UtcTimeResponse.cs22
-rw-r--r--MediaBrowser.Model/System/LogFile.cs1
-rw-r--r--MediaBrowser.Model/System/PublicSystemInfo.cs1
-rw-r--r--MediaBrowser.Model/System/SystemInfo.cs2
-rw-r--r--MediaBrowser.Model/System/WakeOnLanInfo.cs31
-rw-r--r--MediaBrowser.Model/Tasks/IScheduledTask.cs11
-rw-r--r--MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs1
-rw-r--r--MediaBrowser.Model/Tasks/ScheduledTaskHelpers.cs4
-rw-r--r--MediaBrowser.Model/Tasks/TaskCompletionEventArgs.cs10
-rw-r--r--MediaBrowser.Model/Tasks/TaskInfo.cs1
-rw-r--r--MediaBrowser.Model/Tasks/TaskResult.cs1
-rw-r--r--MediaBrowser.Model/Tasks/TaskTriggerInfo.cs1
-rw-r--r--MediaBrowser.Model/Updates/InstallationInfo.cs23
-rw-r--r--MediaBrowser.Model/Updates/PackageInfo.cs1
-rw-r--r--MediaBrowser.Model/Updates/VersionInfo.cs22
-rw-r--r--MediaBrowser.Model/Users/ForgotPasswordResult.cs1
-rw-r--r--MediaBrowser.Model/Users/PinRedeemResult.cs1
-rw-r--r--MediaBrowser.Model/Users/UserAction.cs1
-rw-r--r--MediaBrowser.Model/Users/UserPolicy.cs14
-rw-r--r--MediaBrowser.Providers/Books/AudioBookMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Books/BookMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Channels/ChannelMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Folders/FolderMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Folders/UserViewMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Genres/GenreMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/LiveTv/ProgramMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Manager/ImageSaver.cs24
-rw-r--r--MediaBrowser.Providers/Manager/ItemImageProvider.cs24
-rw-r--r--MediaBrowser.Providers/Manager/MetadataService.cs18
-rw-r--r--MediaBrowser.Providers/Manager/ProviderManager.cs22
-rw-r--r--MediaBrowser.Providers/Manager/ProviderUtils.cs20
-rw-r--r--MediaBrowser.Providers/MediaBrowser.Providers.csproj12
-rw-r--r--MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs16
-rw-r--r--MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs2
-rw-r--r--MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs12
-rw-r--r--MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs3
-rw-r--r--MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs2
-rw-r--r--MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs2
-rw-r--r--MediaBrowser.Providers/Movies/MovieExternalIds.cs4
-rw-r--r--MediaBrowser.Providers/Movies/MovieMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Movies/TrailerMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Music/AlbumMetadataService.cs4
-rw-r--r--MediaBrowser.Providers/Music/ArtistMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Music/AudioMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Music/Extensions.cs18
-rw-r--r--MediaBrowser.Providers/Music/MusicVideoMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/MusicGenres/MusicGenreMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/People/PersonMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Photos/PhotoAlbumMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Photos/PhotoMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Playlists/PlaylistItemsProvider.cs2
-rw-r--r--MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs2
-rw-r--r--MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs8
-rw-r--r--MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs2
-rw-r--r--MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs4
-rw-r--r--MediaBrowser.Providers/Plugins/AudioDb/Configuration/config.html8
-rw-r--r--MediaBrowser.Providers/Plugins/AudioDb/ExternalIds.cs8
-rw-r--r--MediaBrowser.Providers/Plugins/MusicBrainz/AlbumProvider.cs12
-rw-r--r--MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs6
-rw-r--r--MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/config.html8
-rw-r--r--MediaBrowser.Providers/Plugins/MusicBrainz/ExternalIds.cs12
-rw-r--r--MediaBrowser.Providers/Plugins/Omdb/Configuration/PluginConfiguration.cs9
-rw-r--r--MediaBrowser.Providers/Plugins/Omdb/Configuration/config.html49
-rw-r--r--MediaBrowser.Providers/Plugins/Omdb/OmdbEpisodeProvider.cs4
-rw-r--r--MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs3
-rw-r--r--MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs20
-rw-r--r--MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs145
-rw-r--r--MediaBrowser.Providers/Plugins/Omdb/Plugin.cs35
-rw-r--r--MediaBrowser.Providers/Plugins/TheTvdb/Configuration/PluginConfiguration.cs8
-rw-r--r--MediaBrowser.Providers/Plugins/TheTvdb/Plugin.cs24
-rw-r--r--MediaBrowser.Providers/Plugins/TheTvdb/TvdbClientManager.cs5
-rw-r--r--MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeImageProvider.cs6
-rw-r--r--MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeProvider.cs12
-rw-r--r--MediaBrowser.Providers/Plugins/TheTvdb/TvdbPersonImageProvider.cs5
-rw-r--r--MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeasonImageProvider.cs11
-rw-r--r--MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesImageProvider.cs7
-rw-r--r--MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesProvider.cs88
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetExternalId.cs (renamed from MediaBrowser.Providers/Tmdb/BoxSets/TmdbBoxSetExternalId.cs)4
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs (renamed from MediaBrowser.Providers/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs)13
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs (renamed from MediaBrowser.Providers/Tmdb/BoxSets/TmdbBoxSetProvider.cs)25
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/Collections/CollectionImages.cs (renamed from MediaBrowser.Providers/Tmdb/Models/Collections/CollectionImages.cs)4
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/Collections/CollectionResult.cs (renamed from MediaBrowser.Providers/Tmdb/Models/Collections/CollectionResult.cs)2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/Collections/Part.cs (renamed from MediaBrowser.Providers/Tmdb/Models/Collections/Part.cs)2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/General/Backdrop.cs (renamed from MediaBrowser.Providers/Tmdb/Models/General/Backdrop.cs)2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/General/Crew.cs (renamed from MediaBrowser.Providers/Tmdb/Models/General/Crew.cs)2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/General/ExternalIds.cs (renamed from MediaBrowser.Providers/Tmdb/Models/General/ExternalIds.cs)2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/General/Genre.cs (renamed from MediaBrowser.Providers/Tmdb/Models/General/Genre.cs)2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/General/Images.cs (renamed from MediaBrowser.Providers/Tmdb/Models/General/Images.cs)2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/General/Keyword.cs (renamed from MediaBrowser.Providers/Tmdb/Models/General/Keyword.cs)2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/General/Keywords.cs (renamed from MediaBrowser.Providers/Tmdb/Models/General/Keywords.cs)2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/General/Poster.cs (renamed from MediaBrowser.Providers/Tmdb/Models/General/Poster.cs)2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/General/Profile.cs (renamed from MediaBrowser.Providers/Tmdb/Models/General/Profile.cs)2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/General/Still.cs (renamed from MediaBrowser.Providers/Tmdb/Models/General/Still.cs)2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/General/StillImages.cs (renamed from MediaBrowser.Providers/Tmdb/Models/General/StillImages.cs)2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/General/Video.cs (renamed from MediaBrowser.Providers/Tmdb/Models/General/Video.cs)2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/General/Videos.cs (renamed from MediaBrowser.Providers/Tmdb/Models/General/Videos.cs)2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/BelongsToCollection.cs (renamed from MediaBrowser.Providers/Tmdb/Models/Movies/BelongsToCollection.cs)2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/Cast.cs (renamed from MediaBrowser.Providers/Tmdb/Models/Movies/Cast.cs)2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/Casts.cs (renamed from MediaBrowser.Providers/Tmdb/Models/Movies/Casts.cs)4
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/Country.cs (renamed from MediaBrowser.Providers/Tmdb/Models/Movies/Country.cs)2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/MovieResult.cs (renamed from MediaBrowser.Providers/Tmdb/Models/Movies/MovieResult.cs)4
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/ProductionCompany.cs (renamed from MediaBrowser.Providers/Tmdb/Models/Movies/ProductionCompany.cs)2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/ProductionCountry.cs (renamed from MediaBrowser.Providers/Tmdb/Models/Movies/ProductionCountry.cs)2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/Releases.cs (renamed from MediaBrowser.Providers/Tmdb/Models/Movies/Releases.cs)2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/SpokenLanguage.cs (renamed from MediaBrowser.Providers/Tmdb/Models/Movies/SpokenLanguage.cs)2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/Trailers.cs (renamed from MediaBrowser.Providers/Tmdb/Models/Movies/Trailers.cs)2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/Youtube.cs (renamed from MediaBrowser.Providers/Tmdb/Models/Movies/Youtube.cs)2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/People/PersonImages.cs (renamed from MediaBrowser.Providers/Tmdb/Models/People/PersonImages.cs)4
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/People/PersonResult.cs (renamed from MediaBrowser.Providers/Tmdb/Models/People/PersonResult.cs)4
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/Search/ExternalIdLookupResult.cs (renamed from MediaBrowser.Providers/Tmdb/Models/Search/ExternalIdLookupResult.cs)2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/Search/MovieResult.cs (renamed from MediaBrowser.Providers/Tmdb/Models/Search/MovieResult.cs)2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/Search/PersonSearchResult.cs (renamed from MediaBrowser.Providers/Tmdb/Models/Search/PersonSearchResult.cs)2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/Search/TmdbSearchResult.cs (renamed from MediaBrowser.Providers/Tmdb/Models/Search/TmdbSearchResult.cs)2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/Search/TvResult.cs (renamed from MediaBrowser.Providers/Tmdb/Models/Search/TvResult.cs)2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/TV/Cast.cs (renamed from MediaBrowser.Providers/Tmdb/Models/TV/Cast.cs)2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/TV/ContentRating.cs (renamed from MediaBrowser.Providers/Tmdb/Models/TV/ContentRating.cs)2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/TV/ContentRatings.cs (renamed from MediaBrowser.Providers/Tmdb/Models/TV/ContentRatings.cs)2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/TV/CreatedBy.cs (renamed from MediaBrowser.Providers/Tmdb/Models/TV/CreatedBy.cs)2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/TV/Credits.cs (renamed from MediaBrowser.Providers/Tmdb/Models/TV/Credits.cs)4
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/TV/Episode.cs (renamed from MediaBrowser.Providers/Tmdb/Models/TV/Episode.cs)2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/TV/EpisodeCredits.cs (renamed from MediaBrowser.Providers/Tmdb/Models/TV/EpisodeCredits.cs)4
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/TV/EpisodeResult.cs (renamed from MediaBrowser.Providers/Tmdb/Models/TV/EpisodeResult.cs)4
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/TV/GuestStar.cs (renamed from MediaBrowser.Providers/Tmdb/Models/TV/GuestStar.cs)2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/TV/Network.cs (renamed from MediaBrowser.Providers/Tmdb/Models/TV/Network.cs)2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/TV/Season.cs (renamed from MediaBrowser.Providers/Tmdb/Models/TV/Season.cs)2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/TV/SeasonImages.cs (renamed from MediaBrowser.Providers/Tmdb/Models/TV/SeasonImages.cs)4
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/TV/SeasonResult.cs (renamed from MediaBrowser.Providers/Tmdb/Models/TV/SeasonResult.cs)4
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Models/TV/SeriesResult.cs (renamed from MediaBrowser.Providers/Tmdb/Models/TV/SeriesResult.cs)4
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Movies/GenericTmdbMovieInfo.cs (renamed from MediaBrowser.Providers/Tmdb/Movies/GenericTmdbMovieInfo.cs)20
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbImageProvider.cs (renamed from MediaBrowser.Providers/Tmdb/Movies/TmdbImageProvider.cs)10
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieExternalId.cs (renamed from MediaBrowser.Providers/Tmdb/Movies/TmdbMovieExternalId.cs)4
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs (renamed from MediaBrowser.Providers/Tmdb/Movies/TmdbMovieProvider.cs)13
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbSearch.cs (renamed from MediaBrowser.Providers/Tmdb/Movies/TmdbSearch.cs)94
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbSettings.cs (renamed from MediaBrowser.Providers/Tmdb/Movies/TmdbSettings.cs)2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Music/TmdbMusicVideoProvider.cs (renamed from MediaBrowser.Providers/Tmdb/Music/TmdbMusicVideoProvider.cs)4
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonExternalId.cs (renamed from MediaBrowser.Providers/Tmdb/People/TmdbPersonExternalId.cs)4
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs (renamed from MediaBrowser.Providers/Tmdb/People/TmdbPersonImageProvider.cs)10
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs (renamed from MediaBrowser.Providers/Tmdb/People/TmdbPersonProvider.cs)28
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs (renamed from MediaBrowser.Providers/Tmdb/TV/TmdbEpisodeImageProvider.cs)14
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs (renamed from MediaBrowser.Providers/Tmdb/TV/TmdbEpisodeProvider.cs)7
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProviderBase.cs (renamed from MediaBrowser.Providers/Tmdb/TV/TmdbEpisodeProviderBase.cs)12
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs (renamed from MediaBrowser.Providers/Tmdb/TV/TmdbSeasonImageProvider.cs)8
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs (renamed from MediaBrowser.Providers/Tmdb/TV/TmdbSeasonProvider.cs)19
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesExternalId.cs (renamed from MediaBrowser.Providers/Tmdb/TV/TmdbSeriesExternalId.cs)4
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs (renamed from MediaBrowser.Providers/Tmdb/TV/TmdbSeriesImageProvider.cs)14
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs (renamed from MediaBrowser.Providers/Tmdb/TV/TmdbSeriesProvider.cs)51
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs (renamed from MediaBrowser.Providers/Tmdb/TmdbUtils.cs)6
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Trailers/TmdbTrailerProvider.cs (renamed from MediaBrowser.Providers/Tmdb/Trailers/TmdbTrailerProvider.cs)4
-rw-r--r--MediaBrowser.Providers/Studios/StudioMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Subtitles/SubtitleManager.cs2
-rw-r--r--MediaBrowser.Providers/TV/EpisodeMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/TV/MissingEpisodeProvider.cs2
-rw-r--r--MediaBrowser.Providers/TV/SeasonMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/TV/SeriesMetadataService.cs5
-rw-r--r--MediaBrowser.Providers/TV/TvExternalIds.cs8
-rw-r--r--MediaBrowser.Providers/Users/UserMetadataService.cs30
-rw-r--r--MediaBrowser.Providers/Videos/VideoMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Years/YearMetadataService.cs2
-rw-r--r--MediaBrowser.WebDashboard/Api/DashboardService.cs157
-rw-r--r--MediaBrowser.WebDashboard/Api/PackageCreator.cs161
-rw-r--r--MediaBrowser.XbmcMetadata/EntryPoint.cs2
-rw-r--r--MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs10
-rw-r--r--MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs6
-rw-r--r--MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs6
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs2
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs2
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs4
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs2
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/SeasonNfoProvider.cs2
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/SeriesNfoProvider.cs2
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs54
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs2
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs2
-rw-r--r--RSSDP/RSSDP.csproj1
-rw-r--r--tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs8
-rw-r--r--tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj6
-rw-r--r--tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj2
-rw-r--r--tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj2
-rw-r--r--tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj2
-rw-r--r--tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj2
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj2
-rw-r--r--tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.csproj4
699 files changed, 9146 insertions, 5535 deletions
diff --git a/.ci/azure-pipelines-compat.yml b/.ci/azure-pipelines-abi.yml
index 1ffaaf2b9..635aa759c 100644
--- a/.ci/azure-pipelines-compat.yml
+++ b/.ci/azure-pipelines-abi.yml
@@ -33,6 +33,13 @@ jobs:
packageType: sdk
version: ${{ parameters.DotNetSdkVersion }}
+ - task: DotNetCoreCLI@2
+ displayName: 'Install ABI CompatibilityChecker tool'
+ inputs:
+ command: custom
+ custom: tool
+ arguments: 'update compatibilitychecker -g'
+
- task: DownloadPipelineArtifact@2
displayName: "Download New Assembly Build Artifact"
inputs:
@@ -72,25 +79,11 @@ jobs:
overWrite: true
flattenFolders: true
- - task: DownloadGitHubRelease@0
- displayName: "Download ABI Compatibility Check Tool"
- inputs:
- connection: Jellyfin Release Download
- userRepository: EraYaN/dotnet-compatibility
- defaultVersionType: "latest"
- itemPattern: "**-ci.zip"
- downloadPath: "$(System.ArtifactsDirectory)"
-
- - task: ExtractFiles@1
- displayName: "Extract ABI Compatibility Check Tool"
- inputs:
- archiveFilePatterns: "$(System.ArtifactsDirectory)/*-ci.zip"
- destinationFolder: $(System.ArtifactsDirectory)/tools
- cleanDestinationFolder: true
-
# The `--warnings-only` switch will swallow the return code and not emit any errors.
- - task: CmdLine@2
- displayName: "Execute ABI Compatibility Check Tool"
+ - task: DotNetCoreCLI@2
+ displayName: 'Execute ABI Compatibility Check Tool'
inputs:
- script: "dotnet tools/CompatibilityCheckerCLI.dll current-release/$(AssemblyFileName) new-release/$(AssemblyFileName) --azure-pipelines --warnings-only"
+ command: custom
+ custom: compat
+ arguments: 'current-release/$(AssemblyFileName) new-release/$(AssemblyFileName) --azure-pipelines --warnings-only'
workingDirectory: $(System.ArtifactsDirectory)
diff --git a/.ci/azure-pipelines-main.yml b/.ci/azure-pipelines-main.yml
index 456be7108..2a1c0e6f2 100644
--- a/.ci/azure-pipelines-main.yml
+++ b/.ci/azure-pipelines-main.yml
@@ -1,6 +1,6 @@
parameters:
- LinuxImage: "ubuntu-latest"
- RestoreBuildProjects: "Jellyfin.Server/Jellyfin.Server.csproj"
+ LinuxImage: 'ubuntu-latest'
+ RestoreBuildProjects: 'Jellyfin.Server/Jellyfin.Server.csproj'
DotNetSdkVersion: 3.1.100
jobs:
@@ -13,7 +13,7 @@ jobs:
Debug:
BuildConfiguration: Debug
pool:
- vmImage: "${{ parameters.LinuxImage }}"
+ vmImage: '${{ parameters.LinuxImage }}'
steps:
- checkout: self
clean: true
@@ -21,7 +21,7 @@ jobs:
persistCredentials: true
- task: DownloadPipelineArtifact@2
- displayName: "Download Web Branch"
+ displayName: 'Download Web Branch'
condition: in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI', 'BuildCompletion')
inputs:
path: '$(Agent.TempDirectory)'
@@ -32,7 +32,7 @@ jobs:
runBranch: variables['Build.SourceBranch']
- task: DownloadPipelineArtifact@2
- displayName: "Download Web Target"
+ displayName: 'Download Web Target'
condition: eq(variables['Build.Reason'], 'PullRequest')
inputs:
path: '$(Agent.TempDirectory)'
@@ -43,51 +43,51 @@ jobs:
runBranch: variables['System.PullRequest.TargetBranch']
- task: ExtractFiles@1
- displayName: "Extract Web Client"
+ displayName: 'Extract Web Client'
inputs:
archiveFilePatterns: '$(Agent.TempDirectory)/*.zip'
destinationFolder: '$(Build.SourcesDirectory)/MediaBrowser.WebDashboard'
cleanDestinationFolder: false
- task: UseDotNet@2
- displayName: "Update DotNet"
+ displayName: 'Update DotNet'
inputs:
packageType: sdk
version: ${{ parameters.DotNetSdkVersion }}
- task: DotNetCoreCLI@2
- displayName: "Publish Server"
+ displayName: 'Publish Server'
inputs:
command: publish
publishWebProjects: false
- projects: "${{ parameters.RestoreBuildProjects }}"
- arguments: "--configuration $(BuildConfiguration) --output $(Build.ArtifactStagingDirectory)"
+ projects: '${{ parameters.RestoreBuildProjects }}'
+ arguments: '--configuration $(BuildConfiguration) --output $(Build.ArtifactStagingDirectory)'
zipAfterPublish: false
- task: PublishPipelineArtifact@0
- displayName: "Publish Artifact Naming"
+ displayName: 'Publish Artifact Naming'
condition: and(succeeded(), eq(variables['BuildConfiguration'], 'Release'))
inputs:
- targetPath: "$(build.ArtifactStagingDirectory)/Jellyfin.Server/Emby.Naming.dll"
- artifactName: "Jellyfin.Naming"
+ targetPath: '$(build.ArtifactStagingDirectory)/Jellyfin.Server/Emby.Naming.dll'
+ artifactName: 'Jellyfin.Naming'
- task: PublishPipelineArtifact@0
- displayName: "Publish Artifact Controller"
+ displayName: 'Publish Artifact Controller'
condition: and(succeeded(), eq(variables['BuildConfiguration'], 'Release'))
inputs:
- targetPath: "$(build.ArtifactStagingDirectory)/Jellyfin.Server/MediaBrowser.Controller.dll"
- artifactName: "Jellyfin.Controller"
+ targetPath: '$(build.ArtifactStagingDirectory)/Jellyfin.Server/MediaBrowser.Controller.dll'
+ artifactName: 'Jellyfin.Controller'
- task: PublishPipelineArtifact@0
- displayName: "Publish Artifact Model"
+ displayName: 'Publish Artifact Model'
condition: and(succeeded(), eq(variables['BuildConfiguration'], 'Release'))
inputs:
- targetPath: "$(build.ArtifactStagingDirectory)/Jellyfin.Server/MediaBrowser.Model.dll"
- artifactName: "Jellyfin.Model"
+ targetPath: '$(build.ArtifactStagingDirectory)/Jellyfin.Server/MediaBrowser.Model.dll'
+ artifactName: 'Jellyfin.Model'
- task: PublishPipelineArtifact@0
- displayName: "Publish Artifact Common"
+ displayName: 'Publish Artifact Common'
condition: and(succeeded(), eq(variables['BuildConfiguration'], 'Release'))
inputs:
- targetPath: "$(build.ArtifactStagingDirectory)/Jellyfin.Server/MediaBrowser.Common.dll"
- artifactName: "Jellyfin.Common"
+ targetPath: '$(build.ArtifactStagingDirectory)/Jellyfin.Server/MediaBrowser.Common.dll'
+ artifactName: 'Jellyfin.Common'
diff --git a/.ci/azure-pipelines-test.yml b/.ci/azure-pipelines-test.yml
index cb5338ac8..a3c7f8526 100644
--- a/.ci/azure-pipelines-test.yml
+++ b/.ci/azure-pipelines-test.yml
@@ -45,6 +45,7 @@ jobs:
- task: SonarCloudPrepare@1
displayName: 'Prepare analysis on SonarCloud'
condition: eq(variables['ImageName'], 'ubuntu-latest')
+ enabled: false
inputs:
SonarCloud: 'Sonarcloud for Jellyfin'
organization: 'jellyfin'
@@ -63,14 +64,17 @@ jobs:
- task: SonarCloudAnalyze@1
displayName: 'Run Code Analysis'
condition: eq(variables['ImageName'], 'ubuntu-latest')
+ enabled: false
- task: SonarCloudPublish@1
displayName: 'Publish Quality Gate Result'
condition: eq(variables['ImageName'], 'ubuntu-latest')
+ enabled: false
- task: Palmmedia.reportgenerator.reportgenerator-build-release-task.reportgenerator@4
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) # !! THIS is for V1 only V2 will/should support merging
displayName: 'Run ReportGenerator'
+ enabled: false
inputs:
reports: "$(Agent.TempDirectory)/**/coverage.cobertura.xml"
targetdir: "$(Agent.TempDirectory)/merged/"
@@ -80,10 +84,10 @@ jobs:
- task: PublishCodeCoverageResults@1
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) # !! THIS is for V1 only V2 will/should support merging
displayName: 'Publish Code Coverage'
+ enabled: false
inputs:
codeCoverageTool: "cobertura"
#summaryFileLocation: '$(Agent.TempDirectory)/**/coverage.cobertura.xml' # !!THIS IS FOR V2
summaryFileLocation: "$(Agent.TempDirectory)/merged/**.xml"
pathToSources: $(Build.SourcesDirectory)
failIfCoverageEmpty: true
-
diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml
index 1a439c718..3283121e2 100644
--- a/.ci/azure-pipelines.yml
+++ b/.ci/azure-pipelines.yml
@@ -2,9 +2,9 @@ name: $(Date:yyyyMMdd)$(Rev:.r)
variables:
- name: TestProjects
- value: "tests/**/*Tests.csproj"
+ value: 'tests/**/*Tests.csproj'
- name: RestoreBuildProjects
- value: "Jellyfin.Server/Jellyfin.Server.csproj"
+ value: 'Jellyfin.Server/Jellyfin.Server.csproj'
- name: DotNetSdkVersion
value: 3.1.100
@@ -17,17 +17,17 @@ trigger:
jobs:
- template: azure-pipelines-main.yml
parameters:
- LinuxImage: "ubuntu-latest"
+ LinuxImage: 'ubuntu-latest'
RestoreBuildProjects: $(RestoreBuildProjects)
- template: azure-pipelines-test.yml
parameters:
ImageNames:
- Linux: "ubuntu-latest"
- Windows: "windows-latest"
- macOS: "macos-latest"
+ Linux: 'ubuntu-latest'
+ Windows: 'windows-latest'
+ macOS: 'macos-latest'
- - template: azure-pipelines-compat.yml
+ - template: azure-pipelines-abi.yml
parameters:
Packages:
Naming:
@@ -42,4 +42,4 @@ jobs:
Common:
NugetPackageName: Jellyfin.Common
AssemblyFileName: MediaBrowser.Common.dll
- LinuxImage: "ubuntu-latest"
+ LinuxImage: 'ubuntu-latest'
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 000000000..0874cae2e
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,9 @@
+version: 2
+updates:
+- package-ecosystem: nuget
+ directory: "/"
+ schedule:
+ interval: weekly
+ time: '12:00'
+ open-pull-requests-limit: 10
+
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index ac517e10c..2289fd991 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -10,6 +10,16 @@
"${workspaceFolder}/Jellyfin.Server/Jellyfin.Server.csproj"
],
"problemMatcher": "$msCompile"
+ },
+ {
+ "label": "api tests",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "test",
+ "${workspaceFolder}/tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.csproj"
+ ],
+ "problemMatcher": "$msCompile"
}
]
-} \ No newline at end of file
+}
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index ce956176e..1bba7a51d 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -7,6 +7,7 @@
- [anthonylavado](https://github.com/anthonylavado)
- [Artiume](https://github.com/Artiume)
- [AThomsen](https://github.com/AThomsen)
+ - [barronpm](https://github.com/barronpm)
- [bilde2910](https://github.com/bilde2910)
- [bfayers](https://github.com/bfayers)
- [BnMcG](https://github.com/BnMcG)
diff --git a/Dockerfile.arm b/Dockerfile.arm
index 39beaa479..59b8a8c98 100644
--- a/Dockerfile.arm
+++ b/Dockerfile.arm
@@ -38,7 +38,7 @@ COPY --from=qemu /usr/bin/qemu-arm-static /usr/bin
RUN apt-get update \
&& apt-get install --no-install-recommends --no-install-suggests -y ca-certificates gnupg curl && \
curl -ks https://repo.jellyfin.org/debian/jellyfin_team.gpg.key | apt-key add - && \
- curl -s https://keyserver.ubuntu.com/pks/lookup?op=get\&search=0x6587ffd6536b8826e88a62547876ae518cbcf2f2 | apt-key add - && \
+ curl -ks https://keyserver.ubuntu.com/pks/lookup?op=get\&search=0x6587ffd6536b8826e88a62547876ae518cbcf2f2 | apt-key add - && \
echo 'deb [arch=armhf] https://repo.jellyfin.org/debian buster main' > /etc/apt/sources.list.d/jellyfin.list && \
echo "deb http://ppa.launchpad.net/ubuntu-raspi2/ppa/ubuntu bionic main">> /etc/apt/sources.list.d/raspbins.list && \
apt-get update && \
diff --git a/DvdLib/Ifo/Program.cs b/DvdLib/Ifo/Program.cs
index 9f6251270..3d94fa7dc 100644
--- a/DvdLib/Ifo/Program.cs
+++ b/DvdLib/Ifo/Program.cs
@@ -6,7 +6,7 @@ namespace DvdLib.Ifo
{
public class Program
{
- public readonly List<Cell> Cells;
+ public IReadOnlyList<Cell> Cells { get; }
public Program(List<Cell> cells)
{
diff --git a/Emby.Dlna/ContentDirectory/ContentDirectory.cs b/Emby.Dlna/ContentDirectory/ContentDirectory.cs
index 64cd308a2..b1ce7e8ec 100644
--- a/Emby.Dlna/ContentDirectory/ContentDirectory.cs
+++ b/Emby.Dlna/ContentDirectory/ContentDirectory.cs
@@ -1,13 +1,15 @@
#pragma warning disable CS1591
using System;
+using System.Linq;
using System.Threading.Tasks;
using Emby.Dlna.Service;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Enums;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing;
-using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.TV;
@@ -31,7 +33,8 @@ namespace Emby.Dlna.ContentDirectory
private readonly IMediaEncoder _mediaEncoder;
private readonly ITVSeriesManager _tvSeriesManager;
- public ContentDirectory(IDlnaManager dlna,
+ public ContentDirectory(
+ IDlnaManager dlna,
IUserDataManager userDataManager,
IImageProcessor imageProcessor,
ILibraryManager libraryManager,
@@ -130,18 +133,13 @@ namespace Emby.Dlna.ContentDirectory
foreach (var user in _userManager.Users)
{
- if (user.Policy.IsAdministrator)
+ if (user.HasPermission(PermissionKind.IsAdministrator))
{
return user;
}
}
- foreach (var user in _userManager.Users)
- {
- return user;
- }
-
- return null;
+ return _userManager.Users.FirstOrDefault();
}
}
}
diff --git a/Emby.Dlna/ContentDirectory/ControlHandler.cs b/Emby.Dlna/ContentDirectory/ControlHandler.cs
index 28888f031..27585eafa 100644
--- a/Emby.Dlna/ContentDirectory/ControlHandler.cs
+++ b/Emby.Dlna/ContentDirectory/ControlHandler.cs
@@ -10,6 +10,7 @@ using System.Threading;
using System.Xml;
using Emby.Dlna.Didl;
using Emby.Dlna.Service;
+using Jellyfin.Data.Entities;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Drawing;
@@ -17,7 +18,6 @@ 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.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.MediaEncoding;
@@ -28,6 +28,12 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Querying;
using Microsoft.Extensions.Logging;
+using Book = MediaBrowser.Controller.Entities.Book;
+using Episode = MediaBrowser.Controller.Entities.TV.Episode;
+using Genre = MediaBrowser.Controller.Entities.Genre;
+using Movie = MediaBrowser.Controller.Entities.Movies.Movie;
+using MusicAlbum = MediaBrowser.Controller.Entities.Audio.MusicAlbum;
+using Series = MediaBrowser.Controller.Entities.TV.Series;
namespace Emby.Dlna.ContentDirectory
{
@@ -731,7 +737,7 @@ namespace Emby.Dlna.ContentDirectory
return GetGenres(item, user, query);
}
- var array = new ServerItem[]
+ var array = new[]
{
new ServerItem(item)
{
@@ -1115,7 +1121,7 @@ namespace Emby.Dlna.ContentDirectory
private QueryResult<ServerItem> GetMusicPlaylists(User user, InternalItemsQuery query)
{
query.Parent = null;
- query.IncludeItemTypes = new[] { typeof(Playlist).Name };
+ query.IncludeItemTypes = new[] { nameof(Playlist) };
query.SetUser(user);
query.Recursive = true;
@@ -1132,10 +1138,9 @@ namespace Emby.Dlna.ContentDirectory
{
UserId = user.Id,
Limit = 50,
- IncludeItemTypes = new[] { typeof(Audio).Name },
- ParentId = parent == null ? Guid.Empty : parent.Id,
+ IncludeItemTypes = new[] { nameof(Audio) },
+ ParentId = parent?.Id ?? Guid.Empty,
GroupItems = true
-
}, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray();
return ToResult(items);
@@ -1150,7 +1155,6 @@ namespace Emby.Dlna.ContentDirectory
Limit = query.Limit,
StartIndex = query.StartIndex,
UserId = query.User.Id
-
}, new[] { parent }, query.DtoOptions);
return ToResult(result);
@@ -1167,7 +1171,6 @@ namespace Emby.Dlna.ContentDirectory
IncludeItemTypes = new[] { typeof(Episode).Name },
ParentId = parent == null ? Guid.Empty : parent.Id,
GroupItems = false
-
}, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray();
return ToResult(items);
@@ -1177,14 +1180,14 @@ namespace Emby.Dlna.ContentDirectory
{
query.OrderBy = Array.Empty<(string, SortOrder)>();
- var items = _userViewManager.GetLatestItems(new LatestItemsQuery
+ var items = _userViewManager.GetLatestItems(
+ new LatestItemsQuery
{
UserId = user.Id,
Limit = 50,
- IncludeItemTypes = new[] { typeof(Movie).Name },
- ParentId = parent == null ? Guid.Empty : parent.Id,
+ IncludeItemTypes = new[] { nameof(Movie) },
+ ParentId = parent?.Id ?? Guid.Empty,
GroupItems = true
-
}, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray();
return ToResult(items);
@@ -1217,7 +1220,11 @@ namespace Emby.Dlna.ContentDirectory
Recursive = true,
ParentId = parentId,
GenreIds = new[] { item.Id },
- IncludeItemTypes = new[] { typeof(Movie).Name, typeof(Series).Name },
+ IncludeItemTypes = new[]
+ {
+ nameof(Movie),
+ nameof(Series)
+ },
Limit = limit,
StartIndex = startIndex,
DtoOptions = GetDtoOptions()
diff --git a/Emby.Dlna/Didl/DidlBuilder.cs b/Emby.Dlna/Didl/DidlBuilder.cs
index f7d840c62..6cedb3ef0 100644
--- a/Emby.Dlna/Didl/DidlBuilder.cs
+++ b/Emby.Dlna/Didl/DidlBuilder.cs
@@ -6,14 +6,13 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
-using Emby.Dlna.Configuration;
using Emby.Dlna.ContentDirectory;
+using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies;
-using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Playlists;
@@ -23,6 +22,13 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Net;
using Microsoft.Extensions.Logging;
+using Episode = MediaBrowser.Controller.Entities.TV.Episode;
+using Genre = MediaBrowser.Controller.Entities.Genre;
+using Movie = MediaBrowser.Controller.Entities.Movies.Movie;
+using MusicAlbum = MediaBrowser.Controller.Entities.Audio.MusicAlbum;
+using Season = MediaBrowser.Controller.Entities.TV.Season;
+using Series = MediaBrowser.Controller.Entities.TV.Series;
+using XmlAttribute = MediaBrowser.Model.Dlna.XmlAttribute;
namespace Emby.Dlna.Didl
{
@@ -421,7 +427,6 @@ namespace Emby.Dlna.Didl
case StubType.FavoriteSeries: return _localization.GetLocalizedString("HeaderFavoriteShows");
case StubType.FavoriteEpisodes: return _localization.GetLocalizedString("HeaderFavoriteEpisodes");
case StubType.Series: return _localization.GetLocalizedString("Shows");
- default: break;
}
}
@@ -670,7 +675,7 @@ namespace Emby.Dlna.Didl
return;
}
- MediaBrowser.Model.Dlna.XmlAttribute secAttribute = null;
+ XmlAttribute secAttribute = null;
foreach (var attribute in _profile.XmlRootAttributes)
{
if (string.Equals(attribute.Name, "xmlns:sec", StringComparison.OrdinalIgnoreCase))
@@ -995,7 +1000,6 @@ namespace Emby.Dlna.Didl
}
AddImageResElement(item, writer, 160, 160, "jpg", "JPEG_TN");
-
}
private void AddImageResElement(
diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs
index 10f881fe7..a85e5c35e 100644
--- a/Emby.Dlna/DlnaManager.cs
+++ b/Emby.Dlna/DlnaManager.cs
@@ -31,7 +31,7 @@ namespace Emby.Dlna
private readonly IApplicationPaths _appPaths;
private readonly IXmlSerializer _xmlSerializer;
private readonly IFileSystem _fileSystem;
- private readonly ILogger _logger;
+ private readonly ILogger<DlnaManager> _logger;
private readonly IJsonSerializer _jsonSerializer;
private readonly IServerApplicationHost _appHost;
private static readonly Assembly _assembly = typeof(DlnaManager).Assembly;
@@ -49,7 +49,7 @@ namespace Emby.Dlna
_xmlSerializer = xmlSerializer;
_fileSystem = fileSystem;
_appPaths = appPaths;
- _logger = loggerFactory.CreateLogger("Dlna");
+ _logger = loggerFactory.CreateLogger<DlnaManager>();
_jsonSerializer = jsonSerializer;
_appHost = appHost;
}
diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs
index c5d60b2a0..47b235e59 100644
--- a/Emby.Dlna/Main/DlnaEntryPoint.cs
+++ b/Emby.Dlna/Main/DlnaEntryPoint.cs
@@ -33,7 +33,7 @@ namespace Emby.Dlna.Main
public class DlnaEntryPoint : IServerEntryPoint, IRunBeforeStartup
{
private readonly IServerConfigurationManager _config;
- private readonly ILogger _logger;
+ private readonly ILogger<DlnaEntryPoint> _logger;
private readonly IServerApplicationHost _appHost;
private PlayToManager _manager;
@@ -65,7 +65,8 @@ namespace Emby.Dlna.Main
public static DlnaEntryPoint Current;
- public DlnaEntryPoint(IServerConfigurationManager config,
+ public DlnaEntryPoint(
+ IServerConfigurationManager config,
ILoggerFactory loggerFactory,
IServerApplicationHost appHost,
ISessionManager sessionManager,
@@ -99,7 +100,7 @@ namespace Emby.Dlna.Main
_mediaEncoder = mediaEncoder;
_socketFactory = socketFactory;
_networkManager = networkManager;
- _logger = loggerFactory.CreateLogger("Dlna");
+ _logger = loggerFactory.CreateLogger<DlnaEntryPoint>();
ContentDirectory = new ContentDirectory.ContentDirectory(
dlnaManager,
@@ -133,20 +134,20 @@ namespace Emby.Dlna.Main
{
await ((DlnaManager)_dlnaManager).InitProfilesAsync().ConfigureAwait(false);
- ReloadComponents();
+ await ReloadComponents().ConfigureAwait(false);
- _config.NamedConfigurationUpdated += _config_NamedConfigurationUpdated;
+ _config.NamedConfigurationUpdated += OnNamedConfigurationUpdated;
}
- void _config_NamedConfigurationUpdated(object sender, ConfigurationUpdateEventArgs e)
+ private async void OnNamedConfigurationUpdated(object sender, ConfigurationUpdateEventArgs e)
{
if (string.Equals(e.Key, "dlna", StringComparison.OrdinalIgnoreCase))
{
- ReloadComponents();
+ await ReloadComponents().ConfigureAwait(false);
}
}
- private async void ReloadComponents()
+ private async Task ReloadComponents()
{
var options = _config.GetDlnaConfiguration();
@@ -347,7 +348,8 @@ namespace Emby.Dlna.Main
try
{
- _manager = new PlayToManager(_logger,
+ _manager = new PlayToManager(
+ _logger,
_sessionManager,
_libraryManager,
_userManager,
diff --git a/Emby.Dlna/PlayTo/Device.cs b/Emby.Dlna/PlayTo/Device.cs
index 6abc3a82c..c7431d143 100644
--- a/Emby.Dlna/PlayTo/Device.cs
+++ b/Emby.Dlna/PlayTo/Device.cs
@@ -34,7 +34,7 @@ namespace Emby.Dlna.PlayTo
{
get
{
- RefreshVolumeIfNeeded();
+ RefreshVolumeIfNeeded().GetAwaiter().GetResult();
return _volume;
}
set => _volume = value;
@@ -76,24 +76,24 @@ namespace Emby.Dlna.PlayTo
private DateTime _lastVolumeRefresh;
private bool _volumeRefreshActive;
- private void RefreshVolumeIfNeeded()
+ private Task RefreshVolumeIfNeeded()
{
- if (!_volumeRefreshActive)
- {
- return;
- }
-
- if (DateTime.UtcNow >= _lastVolumeRefresh.AddSeconds(5))
+ if (_volumeRefreshActive
+ && DateTime.UtcNow >= _lastVolumeRefresh.AddSeconds(5))
{
_lastVolumeRefresh = DateTime.UtcNow;
- RefreshVolume(CancellationToken.None);
+ return RefreshVolume();
}
+
+ return Task.CompletedTask;
}
- private async void RefreshVolume(CancellationToken cancellationToken)
+ private async Task RefreshVolume(CancellationToken cancellationToken = default)
{
if (_disposed)
+ {
return;
+ }
try
{
diff --git a/Emby.Dlna/PlayTo/PlayToController.cs b/Emby.Dlna/PlayTo/PlayToController.cs
index 9d7c0d365..f1c69196a 100644
--- a/Emby.Dlna/PlayTo/PlayToController.cs
+++ b/Emby.Dlna/PlayTo/PlayToController.cs
@@ -7,6 +7,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Emby.Dlna.Didl;
+using Jellyfin.Data.Entities;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing;
@@ -22,6 +23,7 @@ using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Session;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Logging;
+using Photo = MediaBrowser.Controller.Entities.Photo;
namespace Emby.Dlna.PlayTo
{
@@ -146,11 +148,14 @@ namespace Emby.Dlna.PlayTo
{
var positionTicks = GetProgressPositionTicks(streamInfo);
- ReportPlaybackStopped(streamInfo, positionTicks);
+ await ReportPlaybackStopped(streamInfo, positionTicks).ConfigureAwait(false);
}
streamInfo = StreamParams.ParseFromUrl(e.NewMediaInfo.Url, _libraryManager, _mediaSourceManager);
- if (streamInfo.Item == null) return;
+ if (streamInfo.Item == null)
+ {
+ return;
+ }
var newItemProgress = GetProgressInfo(streamInfo);
@@ -173,11 +178,14 @@ namespace Emby.Dlna.PlayTo
{
var streamInfo = StreamParams.ParseFromUrl(e.MediaInfo.Url, _libraryManager, _mediaSourceManager);
- if (streamInfo.Item == null) return;
+ if (streamInfo.Item == null)
+ {
+ return;
+ }
var positionTicks = GetProgressPositionTicks(streamInfo);
- ReportPlaybackStopped(streamInfo, positionTicks);
+ await ReportPlaybackStopped(streamInfo, positionTicks).ConfigureAwait(false);
var mediaSource = await streamInfo.GetMediaSource(CancellationToken.None).ConfigureAwait(false);
@@ -185,7 +193,7 @@ namespace Emby.Dlna.PlayTo
(_device.Duration == null ? (long?)null : _device.Duration.Value.Ticks) :
mediaSource.RunTimeTicks;
- var playedToCompletion = (positionTicks.HasValue && positionTicks.Value == 0);
+ var playedToCompletion = positionTicks.HasValue && positionTicks.Value == 0;
if (!playedToCompletion && duration.HasValue && positionTicks.HasValue)
{
@@ -210,7 +218,7 @@ namespace Emby.Dlna.PlayTo
}
}
- private async void ReportPlaybackStopped(StreamParams streamInfo, long? positionTicks)
+ private async Task ReportPlaybackStopped(StreamParams streamInfo, long? positionTicks)
{
try
{
@@ -220,7 +228,6 @@ namespace Emby.Dlna.PlayTo
SessionId = _session.Id,
PositionTicks = positionTicks,
MediaSourceId = streamInfo.MediaSourceId
-
}).ConfigureAwait(false);
}
catch (Exception ex)
@@ -441,7 +448,13 @@ namespace Emby.Dlna.PlayTo
}
}
- private PlaylistItem CreatePlaylistItem(BaseItem item, User user, long startPostionTicks, string mediaSourceId, int? audioStreamIndex, int? subtitleStreamIndex)
+ private PlaylistItem CreatePlaylistItem(
+ BaseItem item,
+ User user,
+ long startPostionTicks,
+ string mediaSourceId,
+ int? audioStreamIndex,
+ int? subtitleStreamIndex)
{
var deviceInfo = _device.Properties;
diff --git a/Emby.Dlna/PlayTo/PlayToManager.cs b/Emby.Dlna/PlayTo/PlayToManager.cs
index bbedd1485..9b0339e5d 100644
--- a/Emby.Dlna/PlayTo/PlayToManager.cs
+++ b/Emby.Dlna/PlayTo/PlayToManager.cs
@@ -184,7 +184,8 @@ namespace Emby.Dlna.PlayTo
serverAddress = _appHost.GetLocalApiUrl(info.LocalIpAddress);
}
- controller = new PlayToController(sessionInfo,
+ controller = new PlayToController(
+ sessionInfo,
_sessionManager,
_libraryManager,
_logger,
diff --git a/Emby.Dlna/Service/BaseService.cs b/Emby.Dlna/Service/BaseService.cs
index 3704bedcd..8794ec26a 100644
--- a/Emby.Dlna/Service/BaseService.cs
+++ b/Emby.Dlna/Service/BaseService.cs
@@ -17,7 +17,7 @@ namespace Emby.Dlna.Service
Logger = logger;
HttpClient = httpClient;
- EventManager = new EventManager(Logger, HttpClient);
+ EventManager = new EventManager(logger, HttpClient);
}
public EventSubscriptionResponse CancelEventSubscription(string subscriptionId)
diff --git a/Emby.Dlna/Ssdp/DeviceDiscovery.cs b/Emby.Dlna/Ssdp/DeviceDiscovery.cs
index f95b8ce7d..ab5e56ab0 100644
--- a/Emby.Dlna/Ssdp/DeviceDiscovery.cs
+++ b/Emby.Dlna/Ssdp/DeviceDiscovery.cs
@@ -100,15 +100,13 @@ namespace Emby.Dlna.Ssdp
var headers = headerDict.ToDictionary(i => i.Key, i => i.Value.Value.FirstOrDefault(), StringComparer.OrdinalIgnoreCase);
- var args = new GenericEventArgs<UpnpDeviceInfo>
- {
- Argument = new UpnpDeviceInfo
+ var args = new GenericEventArgs<UpnpDeviceInfo>(
+ new UpnpDeviceInfo
{
Location = e.DiscoveredDevice.DescriptionLocation,
Headers = headers,
LocalIpAddress = e.LocalIpAddress
- }
- };
+ });
DeviceDiscoveredInternal?.Invoke(this, args);
}
@@ -121,14 +119,12 @@ namespace Emby.Dlna.Ssdp
var headers = headerDict.ToDictionary(i => i.Key, i => i.Value.Value.FirstOrDefault(), StringComparer.OrdinalIgnoreCase);
- var args = new GenericEventArgs<UpnpDeviceInfo>
- {
- Argument = new UpnpDeviceInfo
+ var args = new GenericEventArgs<UpnpDeviceInfo>(
+ new UpnpDeviceInfo
{
Location = e.DiscoveredDevice.DescriptionLocation,
Headers = headers
- }
- };
+ });
DeviceLeft?.Invoke(this, args);
}
diff --git a/Emby.Dlna/Ssdp/Extensions.cs b/Emby.Dlna/Ssdp/Extensions.cs
index 10c1f321b..613d332b2 100644
--- a/Emby.Dlna/Ssdp/Extensions.cs
+++ b/Emby.Dlna/Ssdp/Extensions.cs
@@ -1,5 +1,6 @@
#pragma warning disable CS1591
+using System.Linq;
using System.Xml.Linq;
namespace Emby.Dlna.Ssdp
@@ -10,24 +11,17 @@ namespace Emby.Dlna.Ssdp
{
var node = container.Element(name);
- return node == null ? null : node.Value;
+ return node?.Value;
}
public static string GetAttributeValue(this XElement container, XName name)
{
var node = container.Attribute(name);
- return node == null ? null : node.Value;
+ return node?.Value;
}
public static string GetDescendantValue(this XElement container, XName name)
- {
- foreach (var node in container.Descendants(name))
- {
- return node.Value;
- }
-
- return null;
- }
+ => container.Descendants(name).FirstOrDefault()?.Value;
}
}
diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs
index 0b3bbe29e..8696cb280 100644
--- a/Emby.Drawing/ImageProcessor.cs
+++ b/Emby.Drawing/ImageProcessor.cs
@@ -4,6 +4,7 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Drawing;
@@ -14,6 +15,7 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Net;
using Microsoft.Extensions.Logging;
+using Photo = MediaBrowser.Controller.Entities.Photo;
namespace Emby.Drawing
{
@@ -28,7 +30,7 @@ namespace Emby.Drawing
private static readonly HashSet<string> _transparentImageTypes
= new HashSet<string>(StringComparer.OrdinalIgnoreCase) { ".png", ".webp", ".gif" };
- private readonly ILogger _logger;
+ private readonly ILogger<ImageProcessor> _logger;
private readonly IFileSystem _fileSystem;
private readonly IServerApplicationPaths _appPaths;
private readonly IImageEncoder _imageEncoder;
@@ -114,7 +116,7 @@ namespace Emby.Drawing
=> _transparentImageTypes.Contains(Path.GetExtension(path));
/// <inheritdoc />
- public async Task<(string path, string mimeType, DateTime dateModified)> ProcessImage(ImageProcessingOptions options)
+ public async Task<(string path, string? mimeType, DateTime dateModified)> ProcessImage(ImageProcessingOptions options)
{
ItemImageInfo originalImage = options.Image;
BaseItem item = options.Item;
@@ -230,7 +232,7 @@ namespace Emby.Drawing
return ImageFormat.Jpg;
}
- private string GetMimeType(ImageFormat format, string path)
+ private string? GetMimeType(ImageFormat format, string path)
=> format switch
{
ImageFormat.Bmp => MimeTypes.GetMimeType("i.bmp"),
@@ -300,7 +302,7 @@ namespace Emby.Drawing
}
string path = info.Path;
- _logger.LogInformation("Getting image size for item {ItemType} {Path}", item.GetType().Name, path);
+ _logger.LogDebug("Getting image size for item {ItemType} {Path}", item.GetType().Name, path);
ImageDimensions size = GetImageDimensions(path);
info.Width = size.Width;
@@ -314,6 +316,27 @@ namespace Emby.Drawing
=> _imageEncoder.GetImageSize(path);
/// <inheritdoc />
+ public string GetImageBlurHash(string path)
+ {
+ var size = GetImageDimensions(path);
+ if (size.Width <= 0 || size.Height <= 0)
+ {
+ return string.Empty;
+ }
+
+ // We want tiles to be as close to square as possible, and to *mostly* keep under 16 tiles for performance.
+ // One tile is (width / xComp) x (height / yComp) pixels, which means that ideally yComp = xComp * height / width.
+ // See more at https://github.com/woltapp/blurhash/#how-do-i-pick-the-number-of-x-and-y-components
+ float xCompF = MathF.Sqrt(16.0f * size.Width / size.Height);
+ float yCompF = xCompF * size.Height / size.Width;
+
+ int xComp = Math.Min((int)xCompF + 1, 9);
+ int yComp = Math.Min((int)yCompF + 1, 9);
+
+ return _imageEncoder.GetImageBlurHash(xComp, yComp, path);
+ }
+
+ /// <inheritdoc />
public string GetImageCacheTag(BaseItem item, ItemImageInfo image)
=> (item.Path + image.DateModified.Ticks).GetMD5().ToString("N", CultureInfo.InvariantCulture);
@@ -328,6 +351,13 @@ namespace Emby.Drawing
});
}
+ /// <inheritdoc />
+ public string GetImageCacheTag(User user)
+ {
+ return (user.ProfileImage.Path + user.ProfileImage.LastModified.Ticks).GetMD5()
+ .ToString("N", CultureInfo.InvariantCulture);
+ }
+
private async Task<(string path, DateTime dateModified)> GetSupportedImage(string originalImagePath, DateTime dateModified)
{
var inputFormat = Path.GetExtension(originalImagePath)
diff --git a/Emby.Drawing/NullImageEncoder.cs b/Emby.Drawing/NullImageEncoder.cs
index 5af7f1622..bbb5c1716 100644
--- a/Emby.Drawing/NullImageEncoder.cs
+++ b/Emby.Drawing/NullImageEncoder.cs
@@ -42,5 +42,11 @@ namespace Emby.Drawing
{
throw new NotImplementedException();
}
+
+ /// <inheritdoc />
+ public string GetImageBlurHash(int xComp, int yComp, string path)
+ {
+ throw new NotImplementedException();
+ }
}
}
diff --git a/Emby.Naming/Common/NamingOptions.cs b/Emby.Naming/Common/NamingOptions.cs
index a2d75d0b8..1b343790e 100644
--- a/Emby.Naming/Common/NamingOptions.cs
+++ b/Emby.Naming/Common/NamingOptions.cs
@@ -142,7 +142,7 @@ namespace Emby.Naming.Common
CleanStrings = new[]
{
- @"[ _\,\.\(\)\[\]\-](3d|sbs|tab|hsbs|htab|mvc|HDR|HDC|UHD|UltraHD|4k|ac3|dts|custom|dc|divx|divx5|dsr|dsrip|dutch|dvd|dvdrip|dvdscr|dvdscreener|screener|dvdivx|cam|fragment|fs|hdtv|hdrip|hdtvrip|internal|limited|multisubs|ntsc|ogg|ogm|pal|pdtv|proper|repack|rerip|retail|cd[1-9]|r3|r5|bd5|se|svcd|swedish|german|read.nfo|nfofix|unrated|ws|telesync|ts|telecine|tc|brrip|bdrip|480p|480i|576p|576i|720p|720i|1080p|1080i|2160p|hrhd|hrhdtv|hddvd|bluray|x264|h264|xvid|xvidvd|xxx|www.www|\[.*\])([ _\,\.\(\)\[\]\-]|$)",
+ @"[ _\,\.\(\)\[\]\-](3d|sbs|tab|hsbs|htab|mvc|HDR|HDC|UHD|UltraHD|4k|ac3|dts|custom|dc|divx|divx5|dsr|dsrip|dutch|dvd|dvdrip|dvdscr|dvdscreener|screener|dvdivx|cam|fragment|fs|hdtv|hdrip|hdtvrip|internal|limited|multisubs|ntsc|ogg|ogm|pal|pdtv|proper|repack|rerip|retail|cd[1-9]|r3|r5|bd5|bd|se|svcd|swedish|german|read.nfo|nfofix|unrated|ws|telesync|ts|telecine|tc|brrip|bdrip|480p|480i|576p|576i|720p|720i|1080p|1080i|2160p|hrhd|hrhdtv|hddvd|bluray|x264|h264|xvid|xvidvd|xxx|www.www|\[.*\])([ _\,\.\(\)\[\]\-]|$)",
@"(\[.*\])"
};
diff --git a/Emby.Notifications/Api/NotificationsService.cs b/Emby.Notifications/Api/NotificationsService.cs
index 788750796..1ff8a5026 100644
--- a/Emby.Notifications/Api/NotificationsService.cs
+++ b/Emby.Notifications/Api/NotificationsService.cs
@@ -8,6 +8,7 @@ using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Notifications;
@@ -149,9 +150,7 @@ namespace Emby.Notifications.Api
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
public object Get(GetNotificationsSummary request)
{
- return new NotificationsSummary
- {
- };
+ return new NotificationsSummary();
}
public Task Post(AddAdminNotification request)
@@ -164,7 +163,10 @@ namespace Emby.Notifications.Api
Level = request.Level,
Name = request.Name,
Url = request.Url,
- UserIds = _userManager.Users.Where(i => i.Policy.IsAdministrator).Select(i => i.Id).ToArray()
+ UserIds = _userManager.Users
+ .Where(user => user.HasPermission(PermissionKind.IsAdministrator))
+ .Select(user => user.Id)
+ .ToArray()
};
return _notificationManager.SendNotification(notification, CancellationToken.None);
diff --git a/Emby.Notifications/NotificationEntryPoint.cs b/Emby.Notifications/NotificationEntryPoint.cs
index 869b7407e..b923fd26c 100644
--- a/Emby.Notifications/NotificationEntryPoint.cs
+++ b/Emby.Notifications/NotificationEntryPoint.cs
@@ -25,7 +25,7 @@ namespace Emby.Notifications
/// </summary>
public class NotificationEntryPoint : IServerEntryPoint
{
- private readonly ILogger _logger;
+ private readonly ILogger<NotificationEntryPoint> _logger;
private readonly IActivityManager _activityManager;
private readonly ILocalizationManager _localization;
private readonly INotificationManager _notificationManager;
diff --git a/Emby.Notifications/NotificationManager.cs b/Emby.Notifications/NotificationManager.cs
index 639a5e1aa..8b281e487 100644
--- a/Emby.Notifications/NotificationManager.cs
+++ b/Emby.Notifications/NotificationManager.cs
@@ -4,6 +4,8 @@ using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Enums;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration;
@@ -21,7 +23,7 @@ namespace Emby.Notifications
/// </summary>
public class NotificationManager : INotificationManager
{
- private readonly ILogger _logger;
+ private readonly ILogger<NotificationManager> _logger;
private readonly IUserManager _userManager;
private readonly IServerConfigurationManager _config;
@@ -101,7 +103,7 @@ namespace Emby.Notifications
switch (request.SendToUserMode.Value)
{
case SendToUserType.Admins:
- return _userManager.Users.Where(i => i.Policy.IsAdministrator)
+ return _userManager.Users.Where(i => i.HasPermission(PermissionKind.IsAdministrator))
.Select(i => i.Id);
case SendToUserType.All:
return _userManager.UsersIds;
@@ -117,7 +119,7 @@ namespace Emby.Notifications
var config = GetConfiguration();
return _userManager.Users
- .Where(i => config.IsEnabledToSendToUser(request.NotificationType, i.Id.ToString("N", CultureInfo.InvariantCulture), i.Policy))
+ .Where(i => config.IsEnabledToSendToUser(request.NotificationType, i.Id.ToString("N", CultureInfo.InvariantCulture), i))
.Select(i => i.Id);
}
@@ -142,7 +144,7 @@ namespace Emby.Notifications
User = user
};
- _logger.LogDebug("Sending notification via {0} to user {1}", service.Name, user.Name);
+ _logger.LogDebug("Sending notification via {0} to user {1}", service.Name, user.Username);
try
{
diff --git a/Emby.Photos/PhotoProvider.cs b/Emby.Photos/PhotoProvider.cs
index 987cb7fb2..4071e4e54 100644
--- a/Emby.Photos/PhotoProvider.cs
+++ b/Emby.Photos/PhotoProvider.cs
@@ -22,7 +22,7 @@ namespace Emby.Photos
/// </summary>
public class PhotoProvider : ICustomMetadataProvider<Photo>, IForcedProvider, IHasItemChangeMonitor
{
- private readonly ILogger _logger;
+ private readonly ILogger<PhotoProvider> _logger;
private readonly IImageProcessor _imageProcessor;
// These are causing taglib to hang
@@ -104,7 +104,7 @@ namespace Emby.Photos
item.Overview = image.ImageTag.Comment;
if (!string.IsNullOrWhiteSpace(image.ImageTag.Title)
- && !item.LockedFields.Contains(MetadataFields.Name))
+ && !item.LockedFields.Contains(MetadataField.Name))
{
item.Name = image.ImageTag.Title;
}
diff --git a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
index 3983824a3..84bec9201 100644
--- a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
+++ b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
@@ -88,25 +88,26 @@ namespace Emby.Server.Implementations.Activity
_subManager.SubtitleDownloadFailure += OnSubtitleDownloadFailure;
- _userManager.UserCreated += OnUserCreated;
- _userManager.UserPasswordChanged += OnUserPasswordChanged;
- _userManager.UserDeleted += OnUserDeleted;
- _userManager.UserPolicyUpdated += OnUserPolicyUpdated;
- _userManager.UserLockedOut += OnUserLockedOut;
+ _userManager.OnUserCreated += OnUserCreated;
+ _userManager.OnUserPasswordChanged += OnUserPasswordChanged;
+ _userManager.OnUserDeleted += OnUserDeleted;
+ _userManager.OnUserLockedOut += OnUserLockedOut;
return Task.CompletedTask;
}
- private async void OnUserLockedOut(object sender, GenericEventArgs<MediaBrowser.Controller.Entities.User> e)
+ private async void OnUserLockedOut(object sender, GenericEventArgs<User> e)
{
await CreateLogEntry(new ActivityLog(
string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserLockedOutWithName"),
- e.Argument.Name),
+ e.Argument.Username),
NotificationType.UserLockedOut.ToString(),
- e.Argument.Id))
- .ConfigureAwait(false);
+ e.Argument.Id)
+ {
+ LogSeverity = LogLevel.Error
+ }).ConfigureAwait(false);
}
private async void OnSubtitleDownloadFailure(object sender, SubtitleDownloadFailureEventArgs e)
@@ -152,7 +153,7 @@ namespace Emby.Server.Implementations.Activity
string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserStoppedPlayingItemWithValues"),
- user.Name,
+ user.Username,
GetItemName(item),
e.DeviceName),
GetPlaybackStoppedNotificationType(item.MediaType),
@@ -187,7 +188,7 @@ namespace Emby.Server.Implementations.Activity
string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserStartedPlayingItemWithValues"),
- user.Name,
+ user.Username,
GetItemName(item),
e.DeviceName),
GetPlaybackNotificationType(item.MediaType),
@@ -304,49 +305,37 @@ namespace Emby.Server.Implementations.Activity
}).ConfigureAwait(false);
}
- private async void OnUserPolicyUpdated(object sender, GenericEventArgs<MediaBrowser.Controller.Entities.User> e)
- {
- await CreateLogEntry(new ActivityLog(
- string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("UserPolicyUpdatedWithName"),
- e.Argument.Name),
- "UserPolicyUpdated",
- e.Argument.Id))
- .ConfigureAwait(false);
- }
-
- private async void OnUserDeleted(object sender, GenericEventArgs<MediaBrowser.Controller.Entities.User> e)
+ private async void OnUserDeleted(object sender, GenericEventArgs<User> e)
{
await CreateLogEntry(new ActivityLog(
string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserDeletedWithName"),
- e.Argument.Name),
+ e.Argument.Username),
"UserDeleted",
Guid.Empty))
.ConfigureAwait(false);
}
- private async void OnUserPasswordChanged(object sender, GenericEventArgs<MediaBrowser.Controller.Entities.User> e)
+ private async void OnUserPasswordChanged(object sender, GenericEventArgs<User> e)
{
await CreateLogEntry(new ActivityLog(
string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserPasswordChangedWithName"),
- e.Argument.Name),
+ e.Argument.Username),
"UserPasswordChanged",
e.Argument.Id))
.ConfigureAwait(false);
}
- private async void OnUserCreated(object sender, GenericEventArgs<MediaBrowser.Controller.Entities.User> e)
+ private async void OnUserCreated(object sender, GenericEventArgs<User> e)
{
await CreateLogEntry(new ActivityLog(
string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserCreatedWithName"),
- e.Argument.Name),
+ e.Argument.Username),
"UserCreated",
e.Argument.Id))
.ConfigureAwait(false);
@@ -377,50 +366,50 @@ namespace Emby.Server.Implementations.Activity
}).ConfigureAwait(false);
}
- private async void OnPluginUpdated(object sender, GenericEventArgs<(IPlugin, VersionInfo)> e)
+ private async void OnPluginUpdated(object sender, InstallationInfo e)
{
await CreateLogEntry(new ActivityLog(
string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("PluginUpdatedWithName"),
- e.Argument.Item1.Name),
+ e.Name),
NotificationType.PluginUpdateInstalled.ToString(),
Guid.Empty)
{
ShortOverview = string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("VersionNumber"),
- e.Argument.Item2.version),
- Overview = e.Argument.Item2.changelog
+ e.Version),
+ Overview = e.Changelog
}).ConfigureAwait(false);
}
- private async void OnPluginUninstalled(object sender, GenericEventArgs<IPlugin> e)
+ private async void OnPluginUninstalled(object sender, IPlugin e)
{
await CreateLogEntry(new ActivityLog(
string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("PluginUninstalledWithName"),
- e.Argument.Name),
+ e.Name),
NotificationType.PluginUninstalled.ToString(),
Guid.Empty))
.ConfigureAwait(false);
}
- private async void OnPluginInstalled(object sender, GenericEventArgs<VersionInfo> e)
+ private async void OnPluginInstalled(object sender, InstallationInfo e)
{
await CreateLogEntry(new ActivityLog(
string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("PluginInstalledWithName"),
- e.Argument.name),
+ e.Name),
NotificationType.PluginInstalled.ToString(),
Guid.Empty)
{
ShortOverview = string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("VersionNumber"),
- e.Argument.version)
+ e.Version)
}).ConfigureAwait(false);
}
@@ -510,11 +499,10 @@ namespace Emby.Server.Implementations.Activity
_subManager.SubtitleDownloadFailure -= OnSubtitleDownloadFailure;
- _userManager.UserCreated -= OnUserCreated;
- _userManager.UserPasswordChanged -= OnUserPasswordChanged;
- _userManager.UserDeleted -= OnUserDeleted;
- _userManager.UserPolicyUpdated -= OnUserPolicyUpdated;
- _userManager.UserLockedOut -= OnUserLockedOut;
+ _userManager.OnUserCreated -= OnUserCreated;
+ _userManager.OnUserPasswordChanged -= OnUserPasswordChanged;
+ _userManager.OnUserDeleted -= OnUserDeleted;
+ _userManager.OnUserLockedOut -= OnUserLockedOut;
}
/// <summary>
diff --git a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs
index 080cfbbd1..d4a8268b9 100644
--- a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs
+++ b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs
@@ -53,7 +53,7 @@ namespace Emby.Server.Implementations.AppBase
CommonApplicationPaths = applicationPaths;
XmlSerializer = xmlSerializer;
_fileSystem = fileSystem;
- Logger = loggerFactory.CreateLogger(GetType().Name);
+ Logger = loggerFactory.CreateLogger<BaseConfigurationManager>();
UpdateCachePath();
}
@@ -83,7 +83,7 @@ namespace Emby.Server.Implementations.AppBase
/// Gets the logger.
/// </summary>
/// <value>The logger.</value>
- protected ILogger Logger { get; private set; }
+ protected ILogger<BaseConfigurationManager> Logger { get; private set; }
/// <summary>
/// Gets the XML serializer.
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index e6410f857..5772dd479 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -45,6 +45,7 @@ using Emby.Server.Implementations.Services;
using Emby.Server.Implementations.Session;
using Emby.Server.Implementations.TV;
using Emby.Server.Implementations.Updates;
+using Emby.Server.Implementations.SyncPlay;
using MediaBrowser.Api;
using MediaBrowser.Common;
using MediaBrowser.Common.Configuration;
@@ -78,6 +79,7 @@ using MediaBrowser.Controller.Session;
using MediaBrowser.Controller.Sorting;
using MediaBrowser.Controller.Subtitles;
using MediaBrowser.Controller.TV;
+using MediaBrowser.Controller.SyncPlay;
using MediaBrowser.LocalMetadata.Savers;
using MediaBrowser.MediaEncoding.BdInfo;
using MediaBrowser.Model.Configuration;
@@ -171,7 +173,7 @@ namespace Emby.Server.Implementations
/// <summary>
/// Gets the logger.
/// </summary>
- protected ILogger Logger { get; }
+ protected ILogger<ApplicationHost> Logger { get; }
private IPlugin[] _plugins;
@@ -560,11 +562,8 @@ namespace Emby.Server.Implementations
serviceCollection.AddSingleton<IAuthenticationRepository, AuthenticationRepository>();
- serviceCollection.AddSingleton<IUserRepository, SqliteUserRepository>();
-
// TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required
serviceCollection.AddTransient(provider => new Lazy<IDtoService>(provider.GetRequiredService<IDtoService>));
- serviceCollection.AddSingleton<IUserManager, UserManager>();
// TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required
// TODO: Add StartupOptions.FFmpegPath to IConfiguration and remove this custom activation
@@ -613,6 +612,8 @@ namespace Emby.Server.Implementations
serviceCollection.AddSingleton<IPlaylistManager, PlaylistManager>();
+ serviceCollection.AddSingleton<ISyncPlayManager, SyncPlayManager>();
+
serviceCollection.AddSingleton<LiveTvDtoService>();
serviceCollection.AddSingleton<ILiveTvManager, LiveTvManager>();
@@ -655,15 +656,11 @@ namespace Emby.Server.Implementations
((SqliteDisplayPreferencesRepository)Resolve<IDisplayPreferencesRepository>()).Initialize();
((AuthenticationRepository)Resolve<IAuthenticationRepository>()).Initialize();
- ((SqliteUserRepository)Resolve<IUserRepository>()).Initialize();
SetStaticProperties();
- var userManager = (UserManager)Resolve<IUserManager>();
- userManager.Initialize();
-
var userDataRepo = (SqliteUserDataRepository)Resolve<IUserDataRepository>();
- ((SqliteItemRepository)Resolve<IItemRepository>()).Initialize(userDataRepo, userManager);
+ ((SqliteItemRepository)Resolve<IItemRepository>()).Initialize(userDataRepo, Resolve<IUserManager>());
FindParts();
}
@@ -746,7 +743,6 @@ namespace Emby.Server.Implementations
BaseItem.ProviderManager = Resolve<IProviderManager>();
BaseItem.LocalizationManager = Resolve<ILocalizationManager>();
BaseItem.ItemRepository = Resolve<IItemRepository>();
- User.UserManager = Resolve<IUserManager>();
BaseItem.FileSystem = _fileSystemManager;
BaseItem.UserDataManager = Resolve<IUserDataManager>();
BaseItem.ChannelManager = Resolve<IChannelManager>();
diff --git a/Emby.Server.Implementations/Browser/BrowserLauncher.cs b/Emby.Server.Implementations/Browser/BrowserLauncher.cs
index 7f7c6a0be..7a0294e07 100644
--- a/Emby.Server.Implementations/Browser/BrowserLauncher.cs
+++ b/Emby.Server.Implementations/Browser/BrowserLauncher.cs
@@ -41,7 +41,7 @@ namespace Emby.Server.Implementations.Browser
}
catch (Exception ex)
{
- var logger = appHost.Resolve<ILogger>();
+ var logger = appHost.Resolve<ILogger<IServerApplicationHost>>();
logger?.LogError(ex, "Failed to open browser window with URL {URL}", relativeUrl);
}
}
diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs
index 138832fb8..519c5bbef 100644
--- a/Emby.Server.Implementations/Channels/ChannelManager.cs
+++ b/Emby.Server.Implementations/Channels/ChannelManager.cs
@@ -6,6 +6,7 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller.Channels;
@@ -13,8 +14,6 @@ using MediaBrowser.Controller.Configuration;
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.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Channels;
@@ -24,6 +23,11 @@ using MediaBrowser.Model.IO;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Serialization;
using Microsoft.Extensions.Logging;
+using Episode = MediaBrowser.Controller.Entities.TV.Episode;
+using Movie = MediaBrowser.Controller.Entities.Movies.Movie;
+using MusicAlbum = MediaBrowser.Controller.Entities.Audio.MusicAlbum;
+using Season = MediaBrowser.Controller.Entities.TV.Season;
+using Series = MediaBrowser.Controller.Entities.TV.Series;
namespace Emby.Server.Implementations.Channels
{
@@ -36,7 +40,7 @@ namespace Emby.Server.Implementations.Channels
private readonly IUserDataManager _userDataManager;
private readonly IDtoService _dtoService;
private readonly ILibraryManager _libraryManager;
- private readonly ILogger _logger;
+ private readonly ILogger<ChannelManager> _logger;
private readonly IServerConfigurationManager _config;
private readonly IFileSystem _fileSystem;
private readonly IJsonSerializer _jsonSerializer;
@@ -46,14 +50,14 @@ namespace Emby.Server.Implementations.Channels
new ConcurrentDictionary<string, Tuple<DateTime, List<MediaSourceInfo>>>();
private readonly SemaphoreSlim _resourcePool = new SemaphoreSlim(1, 1);
-
+
/// <summary>
/// Initializes a new instance of the <see cref="ChannelManager"/> class.
/// </summary>
/// <param name="userManager">The user manager.</param>
/// <param name="dtoService">The dto service.</param>
/// <param name="libraryManager">The library manager.</param>
- /// <param name="loggerFactory">The logger factory.</param>
+ /// <param name="logger">The logger.</param>
/// <param name="config">The server configuration manager.</param>
/// <param name="fileSystem">The filesystem.</param>
/// <param name="userDataManager">The user data manager.</param>
@@ -791,7 +795,8 @@ namespace Emby.Server.Implementations.Channels
return result;
}
- private async Task<ChannelItemResult> GetChannelItems(IChannel channel,
+ private async Task<ChannelItemResult> GetChannelItems(
+ IChannel channel,
User user,
string externalFolderId,
ChannelItemSortField? sortField,
diff --git a/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs b/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs
index 54b621e25..e5dde48d8 100644
--- a/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs
+++ b/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs
@@ -17,7 +17,7 @@ namespace Emby.Server.Implementations.Channels
public class RefreshChannelsScheduledTask : IScheduledTask, IConfigurableScheduledTask
{
private readonly IChannelManager _channelManager;
- private readonly ILogger _logger;
+ private readonly ILogger<RefreshChannelsScheduledTask> _logger;
private readonly ILibraryManager _libraryManager;
private readonly ILocalizationManager _localization;
diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs
index 7c518d483..8fb9520d6 100644
--- a/Emby.Server.Implementations/Collections/CollectionManager.cs
+++ b/Emby.Server.Implementations/Collections/CollectionManager.cs
@@ -5,6 +5,7 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Collections;
using MediaBrowser.Controller.Configuration;
@@ -29,7 +30,7 @@ namespace Emby.Server.Implementations.Collections
private readonly ILibraryManager _libraryManager;
private readonly IFileSystem _fileSystem;
private readonly ILibraryMonitor _iLibraryMonitor;
- private readonly ILogger _logger;
+ private readonly ILogger<CollectionManager> _logger;
private readonly IProviderManager _providerManager;
private readonly ILocalizationManager _localizationManager;
private readonly IApplicationPaths _appPaths;
@@ -56,7 +57,7 @@ namespace Emby.Server.Implementations.Collections
_libraryManager = libraryManager;
_fileSystem = fileSystem;
_iLibraryMonitor = iLibraryMonitor;
- _logger = loggerFactory.CreateLogger(nameof(CollectionManager));
+ _logger = loggerFactory.CreateLogger<CollectionManager>();
_providerManager = providerManager;
_localizationManager = localizationManager;
_appPaths = appPaths;
@@ -370,7 +371,7 @@ namespace Emby.Server.Implementations.Collections
{
private readonly CollectionManager _collectionManager;
private readonly IServerConfigurationManager _config;
- private readonly ILogger _logger;
+ private readonly ILogger<CollectionManagerEntryPoint> _logger;
/// <summary>
/// Initializes a new instance of the <see cref="CollectionManagerEntryPoint"/> class.
diff --git a/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs b/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs
index 305e67e8c..94ee1ced7 100644
--- a/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs
+++ b/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs
@@ -90,7 +90,7 @@ namespace Emby.Server.Implementations.Configuration
ValidateMetadataPath(newConfig);
ValidateSslCertificate(newConfig);
- ConfigurationUpdating?.Invoke(this, new GenericEventArgs<ServerConfiguration> { Argument = newConfig });
+ ConfigurationUpdating?.Invoke(this, new GenericEventArgs<ServerConfiguration>(newConfig));
base.ReplaceConfiguration(newConfiguration);
}
diff --git a/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs b/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs
index a037415a9..fd302d136 100644
--- a/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs
+++ b/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs
@@ -1,3 +1,5 @@
+#nullable enable
+
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
@@ -129,8 +131,6 @@ namespace Emby.Server.Implementations.Cryptography
_randomNumberGenerator.Dispose();
}
- _randomNumberGenerator = null;
-
_disposed = true;
}
}
diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
index 0654132f4..f816fd54f 100644
--- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
+++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
@@ -17,7 +17,7 @@ namespace Emby.Server.Implementations.Data
/// Initializes a new instance of the <see cref="BaseSqliteRepository"/> class.
/// </summary>
/// <param name="logger">The logger.</param>
- protected BaseSqliteRepository(ILogger logger)
+ protected BaseSqliteRepository(ILogger<BaseSqliteRepository> logger)
{
Logger = logger;
}
@@ -32,7 +32,7 @@ namespace Emby.Server.Implementations.Data
/// Gets the logger.
/// </summary>
/// <value>The logger.</value>
- protected ILogger Logger { get; }
+ protected ILogger<BaseSqliteRepository> Logger { get; }
/// <summary>
/// Gets the default connection flags.
diff --git a/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs b/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs
index 37c678a5d..6c9bcff0f 100644
--- a/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs
+++ b/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs
@@ -12,7 +12,7 @@ namespace Emby.Server.Implementations.Data
public class CleanDatabaseScheduledTask : ILibraryPostScanTask
{
private readonly ILibraryManager _libraryManager;
- private readonly ILogger _logger;
+ private readonly ILogger<CleanDatabaseScheduledTask> _logger;
public CleanDatabaseScheduledTask(ILibraryManager libraryManager, ILogger<CleanDatabaseScheduledTask> logger)
{
diff --git a/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs b/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs
index d474f1c6b..63d0321b7 100644
--- a/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs
@@ -1,4 +1,4 @@
-#pragma warning disable CS1591
+#pragma warning disable CS1591
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
index ca5cd6fdd..43a593f11 100644
--- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -33,7 +35,7 @@ using SQLitePCL.pretty;
namespace Emby.Server.Implementations.Data
{
/// <summary>
- /// Class SQLiteItemRepository
+ /// Class SQLiteItemRepository.
/// </summary>
public class SqliteItemRepository : BaseSqliteRepository, IItemRepository
{
@@ -1141,24 +1143,24 @@ namespace Emby.Server.Implementations.Data
public string ToValueString(ItemImageInfo image)
{
- var delimeter = "*";
-
- var path = image.Path;
+ const string Delimeter = "*";
- if (path == null)
- {
- path = string.Empty;
- }
+ var path = image.Path ?? string.Empty;
+ var hash = image.BlurHash ?? string.Empty;
return GetPathToSave(path) +
- delimeter +
+ Delimeter +
image.DateModified.Ticks.ToString(CultureInfo.InvariantCulture) +
- delimeter +
+ Delimeter +
image.Type +
- delimeter +
+ Delimeter +
image.Width.ToString(CultureInfo.InvariantCulture) +
- delimeter +
- image.Height.ToString(CultureInfo.InvariantCulture);
+ Delimeter +
+ image.Height.ToString(CultureInfo.InvariantCulture) +
+ Delimeter +
+ // Replace delimiters with other characters.
+ // This can be removed when we migrate to a proper DB.
+ hash.Replace('*', '/').Replace('|', '\\');
}
public ItemImageInfo ItemImageInfoFromValueString(string value)
@@ -1192,6 +1194,11 @@ namespace Emby.Server.Implementations.Data
image.Width = width;
image.Height = height;
}
+
+ if (parts.Length >= 6)
+ {
+ image.BlurHash = parts[5].Replace('/', '*').Replace('\\', '|');
+ }
}
return image;
@@ -1619,11 +1626,11 @@ namespace Emby.Server.Implementations.Data
{
if (!reader.IsDBNull(index))
{
- IEnumerable<MetadataFields> GetLockedFields(string s)
+ IEnumerable<MetadataField> GetLockedFields(string s)
{
foreach (var i in s.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries))
{
- if (Enum.TryParse(i, true, out MetadataFields parsedValue))
+ if (Enum.TryParse(i, true, out MetadataField parsedValue))
{
yield return parsedValue;
}
@@ -1971,6 +1978,7 @@ namespace Emby.Server.Implementations.Data
/// Gets the chapter.
/// </summary>
/// <param name="reader">The reader.</param>
+ /// <param name="item">The item.</param>
/// <returns>ChapterInfo.</returns>
private ChapterInfo GetChapter(IReadOnlyList<IResultSetValue> reader, BaseItem item)
{
@@ -2726,7 +2734,7 @@ namespace Emby.Server.Implementations.Data
foreach (var providerId in newItem.ProviderIds)
{
- if (providerId.Key == MetadataProviders.TmdbCollection.ToString())
+ if (providerId.Key == MetadataProvider.TmdbCollection.ToString())
{
continue;
}
@@ -4316,7 +4324,7 @@ namespace Emby.Server.Implementations.Data
var index = 0;
foreach (var pair in query.ExcludeProviderIds)
{
- if (string.Equals(pair.Key, MetadataProviders.TmdbCollection.ToString(), StringComparison.OrdinalIgnoreCase))
+ if (string.Equals(pair.Key, MetadataProvider.TmdbCollection.ToString(), StringComparison.OrdinalIgnoreCase))
{
continue;
}
@@ -4345,7 +4353,7 @@ namespace Emby.Server.Implementations.Data
var index = 0;
foreach (var pair in query.HasAnyProviderId)
{
- if (string.Equals(pair.Key, MetadataProviders.TmdbCollection.ToString(), StringComparison.OrdinalIgnoreCase))
+ if (string.Equals(pair.Key, MetadataProvider.TmdbCollection.ToString(), StringComparison.OrdinalIgnoreCase))
{
continue;
}
diff --git a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs
index 6ee6230fc..b99b74ef8 100644
--- a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs
@@ -4,6 +4,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
+using Jellyfin.Data.Entities;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
diff --git a/Emby.Server.Implementations/Data/SqliteUserRepository.cs b/Emby.Server.Implementations/Data/SqliteUserRepository.cs
deleted file mode 100644
index 0c3f26974..000000000
--- a/Emby.Server.Implementations/Data/SqliteUserRepository.cs
+++ /dev/null
@@ -1,240 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Text.Json;
-using MediaBrowser.Common.Json;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Persistence;
-using Microsoft.Extensions.Logging;
-using SQLitePCL.pretty;
-
-namespace Emby.Server.Implementations.Data
-{
- /// <summary>
- /// Class SQLiteUserRepository
- /// </summary>
- public class SqliteUserRepository : BaseSqliteRepository, IUserRepository
- {
- private readonly JsonSerializerOptions _jsonOptions;
-
- public SqliteUserRepository(
- ILogger<SqliteUserRepository> logger,
- IServerApplicationPaths appPaths)
- : base(logger)
- {
- _jsonOptions = JsonDefaults.GetOptions();
-
- DbFilePath = Path.Combine(appPaths.DataPath, "users.db");
- }
-
- /// <summary>
- /// Gets the name of the repository
- /// </summary>
- /// <value>The name.</value>
- public string Name => "SQLite";
-
- /// <summary>
- /// Opens the connection to the database.
- /// </summary>
- public void Initialize()
- {
- using (var connection = GetConnection())
- {
- var localUsersTableExists = TableExists(connection, "LocalUsersv2");
-
- connection.RunQueries(new[] {
- "create table if not exists LocalUsersv2 (Id INTEGER PRIMARY KEY, guid GUID NOT NULL, data BLOB NOT NULL)",
- "drop index if exists idx_users"
- });
-
- if (!localUsersTableExists && TableExists(connection, "Users"))
- {
- TryMigrateToLocalUsersTable(connection);
- }
-
- RemoveEmptyPasswordHashes(connection);
- }
- }
-
- private void TryMigrateToLocalUsersTable(ManagedConnection connection)
- {
- try
- {
- connection.RunQueries(new[]
- {
- "INSERT INTO LocalUsersv2 (guid, data) SELECT guid,data from users"
- });
- }
- catch (Exception ex)
- {
- Logger.LogError(ex, "Error migrating users database");
- }
- }
-
- private void RemoveEmptyPasswordHashes(ManagedConnection connection)
- {
- foreach (var user in RetrieveAllUsers(connection))
- {
- // If the user password is the sha1 hash of the empty string, remove it
- if (!string.Equals(user.Password, "DA39A3EE5E6B4B0D3255BFEF95601890AFD80709", StringComparison.Ordinal)
- && !string.Equals(user.Password, "$SHA1$DA39A3EE5E6B4B0D3255BFEF95601890AFD80709", StringComparison.Ordinal))
- {
- continue;
- }
-
- user.Password = null;
- var serialized = JsonSerializer.SerializeToUtf8Bytes(user, _jsonOptions);
-
- connection.RunInTransaction(db =>
- {
- using (var statement = db.PrepareStatement("update LocalUsersv2 set data=@data where Id=@InternalId"))
- {
- statement.TryBind("@InternalId", user.InternalId);
- statement.TryBind("@data", serialized);
- statement.MoveNext();
- }
- }, TransactionMode);
- }
- }
-
- /// <summary>
- /// Save a user in the repo
- /// </summary>
- public void CreateUser(User user)
- {
- if (user == null)
- {
- throw new ArgumentNullException(nameof(user));
- }
-
- var serialized = JsonSerializer.SerializeToUtf8Bytes(user, _jsonOptions);
-
- using (var connection = GetConnection())
- {
- connection.RunInTransaction(db =>
- {
- using (var statement = db.PrepareStatement("insert into LocalUsersv2 (guid, data) values (@guid, @data)"))
- {
- statement.TryBind("@guid", user.Id.ToByteArray());
- statement.TryBind("@data", serialized);
-
- statement.MoveNext();
- }
-
- var createdUser = GetUser(user.Id, connection);
-
- if (createdUser == null)
- {
- throw new ApplicationException("created user should never be null");
- }
-
- user.InternalId = createdUser.InternalId;
-
- }, TransactionMode);
- }
- }
-
- public void UpdateUser(User user)
- {
- if (user == null)
- {
- throw new ArgumentNullException(nameof(user));
- }
-
- var serialized = JsonSerializer.SerializeToUtf8Bytes(user, _jsonOptions);
-
- using (var connection = GetConnection())
- {
- connection.RunInTransaction(db =>
- {
- using (var statement = db.PrepareStatement("update LocalUsersv2 set data=@data where Id=@InternalId"))
- {
- statement.TryBind("@InternalId", user.InternalId);
- statement.TryBind("@data", serialized);
- statement.MoveNext();
- }
-
- }, TransactionMode);
- }
- }
-
- private User GetUser(Guid guid, ManagedConnection connection)
- {
- using (var statement = connection.PrepareStatement("select id,guid,data from LocalUsersv2 where guid=@guid"))
- {
- statement.TryBind("@guid", guid);
-
- foreach (var row in statement.ExecuteQuery())
- {
- return GetUser(row);
- }
- }
-
- return null;
- }
-
- private User GetUser(IReadOnlyList<IResultSetValue> row)
- {
- var id = row[0].ToInt64();
- var guid = row[1].ReadGuidFromBlob();
-
- var user = JsonSerializer.Deserialize<User>(row[2].ToBlob(), _jsonOptions);
- user.InternalId = id;
- user.Id = guid;
- return user;
- }
-
- /// <summary>
- /// Retrieve all users from the database
- /// </summary>
- /// <returns>IEnumerable{User}.</returns>
- public List<User> RetrieveAllUsers()
- {
- using (var connection = GetConnection(true))
- {
- return new List<User>(RetrieveAllUsers(connection));
- }
- }
-
- /// <summary>
- /// Retrieve all users from the database
- /// </summary>
- /// <returns>IEnumerable{User}.</returns>
- private IEnumerable<User> RetrieveAllUsers(ManagedConnection connection)
- {
- foreach (var row in connection.Query("select id,guid,data from LocalUsersv2"))
- {
- yield return GetUser(row);
- }
- }
-
- /// <summary>
- /// Deletes the user.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <returns>Task.</returns>
- /// <exception cref="ArgumentNullException">user</exception>
- public void DeleteUser(User user)
- {
- if (user == null)
- {
- throw new ArgumentNullException(nameof(user));
- }
-
- using (var connection = GetConnection())
- {
- connection.RunInTransaction(db =>
- {
- using (var statement = db.PrepareStatement("delete from LocalUsersv2 where Id=@id"))
- {
- statement.TryBind("@id", user.InternalId);
- statement.MoveNext();
- }
- }, TransactionMode);
- }
- }
- }
-}
diff --git a/Emby.Server.Implementations/Devices/DeviceId.cs b/Emby.Server.Implementations/Devices/DeviceId.cs
index f0d43e665..fa6ac95fd 100644
--- a/Emby.Server.Implementations/Devices/DeviceId.cs
+++ b/Emby.Server.Implementations/Devices/DeviceId.cs
@@ -12,7 +12,7 @@ namespace Emby.Server.Implementations.Devices
public class DeviceId
{
private readonly IApplicationPaths _appPaths;
- private readonly ILogger _logger;
+ private readonly ILogger<DeviceId> _logger;
private readonly object _syncLock = new object();
@@ -90,7 +90,7 @@ namespace Emby.Server.Implementations.Devices
public DeviceId(IApplicationPaths appPaths, ILoggerFactory loggerFactory)
{
_appPaths = appPaths;
- _logger = loggerFactory.CreateLogger("SystemId");
+ _logger = loggerFactory.CreateLogger<DeviceId>();
}
public string Value => _id ?? (_id = GetDeviceId());
diff --git a/Emby.Server.Implementations/Devices/DeviceManager.cs b/Emby.Server.Implementations/Devices/DeviceManager.cs
index 2283f2433..f8896d69f 100644
--- a/Emby.Server.Implementations/Devices/DeviceManager.cs
+++ b/Emby.Server.Implementations/Devices/DeviceManager.cs
@@ -5,10 +5,11 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
+using Jellyfin.Data.Enums;
+using Jellyfin.Data.Entities;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices;
-using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Security;
using MediaBrowser.Model.Devices;
@@ -16,7 +17,6 @@ using MediaBrowser.Model.Events;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Session;
-using MediaBrowser.Model.Users;
namespace Emby.Server.Implementations.Devices
{
@@ -27,11 +27,10 @@ namespace Emby.Server.Implementations.Devices
private readonly IServerConfigurationManager _config;
private readonly IAuthenticationRepository _authRepo;
private readonly Dictionary<string, ClientCapabilities> _capabilitiesCache;
+ private readonly object _capabilitiesSyncLock = new object();
public event EventHandler<GenericEventArgs<Tuple<string, DeviceOptions>>> DeviceOptionsUpdated;
- private readonly object _capabilitiesSyncLock = new object();
-
public DeviceManager(
IAuthenticationRepository authRepo,
IJsonSerializer json,
@@ -62,13 +61,7 @@ namespace Emby.Server.Implementations.Devices
{
_authRepo.UpdateDeviceOptions(deviceId, options);
- if (DeviceOptionsUpdated != null)
- {
- DeviceOptionsUpdated(this, new GenericEventArgs<Tuple<string, DeviceOptions>>()
- {
- Argument = new Tuple<string, DeviceOptions>(deviceId, options)
- });
- }
+ DeviceOptionsUpdated?.Invoke(this, new GenericEventArgs<Tuple<string, DeviceOptions>>(new Tuple<string, DeviceOptions>(deviceId, options)));
}
public DeviceOptions GetDeviceOptions(string deviceId)
@@ -181,7 +174,12 @@ namespace Emby.Server.Implementations.Devices
throw new ArgumentNullException(nameof(deviceId));
}
- if (!CanAccessDevice(user.Policy, deviceId))
+ if (user.HasPermission(PermissionKind.EnableAllDevices) || user.HasPermission(PermissionKind.IsAdministrator))
+ {
+ return true;
+ }
+
+ if (!user.GetPreference(PreferenceKind.EnabledDevices).Contains(deviceId, StringComparer.OrdinalIgnoreCase))
{
var capabilities = GetCapabilities(deviceId);
@@ -193,20 +191,5 @@ namespace Emby.Server.Implementations.Devices
return true;
}
-
- private static bool CanAccessDevice(UserPolicy policy, string id)
- {
- if (policy.EnableAllDevices)
- {
- return true;
- }
-
- if (policy.IsAdministrator)
- {
- return true;
- }
-
- return policy.EnabledDevices.Contains(id, StringComparer.OrdinalIgnoreCase);
- }
}
}
diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs
index c4b65d265..7e0cfe424 100644
--- a/Emby.Server.Implementations/Dto/DtoService.cs
+++ b/Emby.Server.Implementations/Dto/DtoService.cs
@@ -6,14 +6,14 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Enums;
using MediaBrowser.Common;
using MediaBrowser.Controller.Channels;
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.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Persistence;
@@ -24,12 +24,20 @@ using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
using Microsoft.Extensions.Logging;
+using Book = MediaBrowser.Controller.Entities.Book;
+using Episode = MediaBrowser.Controller.Entities.TV.Episode;
+using Movie = MediaBrowser.Controller.Entities.Movies.Movie;
+using MusicAlbum = MediaBrowser.Controller.Entities.Audio.MusicAlbum;
+using Person = MediaBrowser.Controller.Entities.Person;
+using Photo = MediaBrowser.Controller.Entities.Photo;
+using Season = MediaBrowser.Controller.Entities.TV.Season;
+using Series = MediaBrowser.Controller.Entities.TV.Series;
namespace Emby.Server.Implementations.Dto
{
public class DtoService : IDtoService
{
- private readonly ILogger _logger;
+ private readonly ILogger<DtoService> _logger;
private readonly ILibraryManager _libraryManager;
private readonly IUserDataManager _userDataRepository;
private readonly IItemRepository _itemRepo;
@@ -384,7 +392,7 @@ namespace Emby.Server.Implementations.Dto
if (options.ContainsField(ItemFields.ChildCount))
{
- dto.ChildCount = dto.ChildCount ?? GetChildCount(folder, user);
+ dto.ChildCount ??= GetChildCount(folder, user);
}
}
@@ -414,7 +422,7 @@ namespace Emby.Server.Implementations.Dto
if (options.ContainsField(ItemFields.BasicSyncInfo))
{
- var userCanSync = user != null && user.Policy.EnableContentDownloading;
+ var userCanSync = user != null && user.HasPermission(PermissionKind.EnableContentDownloading);
if (userCanSync && item.SupportsExternalTransfer)
{
dto.SupportsSync = true;
@@ -605,7 +613,7 @@ namespace Emby.Server.Implementations.Dto
if (dictionary.TryGetValue(person.Name, out Person entity))
{
- baseItemPerson.PrimaryImageTag = GetImageCacheTag(entity, ImageType.Primary);
+ baseItemPerson.PrimaryImageTag = GetTagAndFillBlurhash(dto, entity, ImageType.Primary);
baseItemPerson.Id = entity.Id.ToString("N", CultureInfo.InvariantCulture);
list.Add(baseItemPerson);
}
@@ -654,6 +662,70 @@ namespace Emby.Server.Implementations.Dto
return _libraryManager.GetGenreId(name);
}
+ private string GetTagAndFillBlurhash(BaseItemDto dto, BaseItem item, ImageType imageType, int imageIndex = 0)
+ {
+ var image = item.GetImageInfo(imageType, imageIndex);
+ if (image != null)
+ {
+ return GetTagAndFillBlurhash(dto, item, image);
+ }
+
+ return null;
+ }
+
+ private string GetTagAndFillBlurhash(BaseItemDto dto, BaseItem item, ItemImageInfo image)
+ {
+ var tag = GetImageCacheTag(item, image);
+ if (!string.IsNullOrEmpty(image.BlurHash))
+ {
+ if (dto.ImageBlurHashes == null)
+ {
+ dto.ImageBlurHashes = new Dictionary<ImageType, Dictionary<string, string>>();
+ }
+
+ if (!dto.ImageBlurHashes.ContainsKey(image.Type))
+ {
+ dto.ImageBlurHashes[image.Type] = new Dictionary<string, string>();
+ }
+
+ dto.ImageBlurHashes[image.Type][tag] = image.BlurHash;
+ }
+
+ return tag;
+ }
+
+ private string[] GetTagsAndFillBlurhashes(BaseItemDto dto, BaseItem item, ImageType imageType, int limit)
+ {
+ return GetTagsAndFillBlurhashes(dto, item, imageType, item.GetImages(imageType).Take(limit).ToList());
+ }
+
+ private string[] GetTagsAndFillBlurhashes(BaseItemDto dto, BaseItem item, ImageType imageType, List<ItemImageInfo> images)
+ {
+ var tags = GetImageTags(item, images);
+ var hashes = new Dictionary<string, string>();
+ for (int i = 0; i < images.Count; i++)
+ {
+ var img = images[i];
+ if (!string.IsNullOrEmpty(img.BlurHash))
+ {
+ var tag = tags[i];
+ hashes[tag] = img.BlurHash;
+ }
+ }
+
+ if (hashes.Count > 0)
+ {
+ if (dto.ImageBlurHashes == null)
+ {
+ dto.ImageBlurHashes = new Dictionary<ImageType, Dictionary<string, string>>();
+ }
+
+ dto.ImageBlurHashes[imageType] = hashes;
+ }
+
+ return tags;
+ }
+
/// <summary>
/// Sets simple property values on a DTOBaseItem
/// </summary>
@@ -674,8 +746,8 @@ namespace Emby.Server.Implementations.Dto
dto.LockData = item.IsLocked;
dto.ForcedSortName = item.ForcedSortName;
}
- dto.Container = item.Container;
+ dto.Container = item.Container;
dto.EndDate = item.EndDate;
if (options.ContainsField(ItemFields.ExternalUrls))
@@ -694,10 +766,12 @@ namespace Emby.Server.Implementations.Dto
dto.AspectRatio = hasAspectRatio.AspectRatio;
}
+ dto.ImageBlurHashes = new Dictionary<ImageType, Dictionary<string, string>>();
+
var backdropLimit = options.GetImageLimit(ImageType.Backdrop);
if (backdropLimit > 0)
{
- dto.BackdropImageTags = GetImageTags(item, item.GetImages(ImageType.Backdrop).Take(backdropLimit).ToList());
+ dto.BackdropImageTags = GetTagsAndFillBlurhashes(dto, item, ImageType.Backdrop, backdropLimit);
}
if (options.ContainsField(ItemFields.ScreenshotImageTags))
@@ -705,7 +779,7 @@ namespace Emby.Server.Implementations.Dto
var screenshotLimit = options.GetImageLimit(ImageType.Screenshot);
if (screenshotLimit > 0)
{
- dto.ScreenshotImageTags = GetImageTags(item, item.GetImages(ImageType.Screenshot).Take(screenshotLimit).ToList());
+ dto.ScreenshotImageTags = GetTagsAndFillBlurhashes(dto, item, ImageType.Screenshot, screenshotLimit);
}
}
@@ -721,12 +795,11 @@ namespace Emby.Server.Implementations.Dto
// Prevent implicitly captured closure
var currentItem = item;
- foreach (var image in currentItem.ImageInfos.Where(i => !currentItem.AllowsMultipleImages(i.Type))
- .ToList())
+ foreach (var image in currentItem.ImageInfos.Where(i => !currentItem.AllowsMultipleImages(i.Type)))
{
if (options.GetImageLimit(image.Type) > 0)
{
- var tag = GetImageCacheTag(item, image);
+ var tag = GetTagAndFillBlurhash(dto, item, image);
if (tag != null)
{
@@ -871,8 +944,7 @@ namespace Emby.Server.Implementations.Dto
if (albumParent != null)
{
dto.AlbumId = albumParent.Id;
-
- dto.AlbumPrimaryImageTag = GetImageCacheTag(albumParent, ImageType.Primary);
+ dto.AlbumPrimaryImageTag = GetTagAndFillBlurhash(dto, albumParent, ImageType.Primary);
}
//if (options.ContainsField(ItemFields.MediaSourceCount))
@@ -1099,7 +1171,7 @@ namespace Emby.Server.Implementations.Dto
episodeSeries = episodeSeries ?? episode.Series;
if (episodeSeries != null)
{
- dto.SeriesPrimaryImageTag = GetImageCacheTag(episodeSeries, ImageType.Primary);
+ dto.SeriesPrimaryImageTag = GetTagAndFillBlurhash(dto, episodeSeries, ImageType.Primary);
}
}
@@ -1145,7 +1217,7 @@ namespace Emby.Server.Implementations.Dto
series = series ?? season.Series;
if (series != null)
{
- dto.SeriesPrimaryImageTag = GetImageCacheTag(series, ImageType.Primary);
+ dto.SeriesPrimaryImageTag = GetTagAndFillBlurhash(dto, series, ImageType.Primary);
}
}
}
@@ -1275,7 +1347,7 @@ namespace Emby.Server.Implementations.Dto
if (image != null)
{
dto.ParentLogoItemId = GetDtoId(parent);
- dto.ParentLogoImageTag = GetImageCacheTag(parent, image);
+ dto.ParentLogoImageTag = GetTagAndFillBlurhash(dto, parent, image);
}
}
if (artLimit > 0 && !(imageTags != null && imageTags.ContainsKey(ImageType.Art)) && dto.ParentArtItemId == null)
@@ -1285,7 +1357,7 @@ namespace Emby.Server.Implementations.Dto
if (image != null)
{
dto.ParentArtItemId = GetDtoId(parent);
- dto.ParentArtImageTag = GetImageCacheTag(parent, image);
+ dto.ParentArtImageTag = GetTagAndFillBlurhash(dto, parent, image);
}
}
if (thumbLimit > 0 && !(imageTags != null && imageTags.ContainsKey(ImageType.Thumb)) && (dto.ParentThumbItemId == null || parent is Series) && !(parent is ICollectionFolder) && !(parent is UserView))
@@ -1295,7 +1367,7 @@ namespace Emby.Server.Implementations.Dto
if (image != null)
{
dto.ParentThumbItemId = GetDtoId(parent);
- dto.ParentThumbImageTag = GetImageCacheTag(parent, image);
+ dto.ParentThumbImageTag = GetTagAndFillBlurhash(dto, parent, image);
}
}
if (backdropLimit > 0 && !((dto.BackdropImageTags != null && dto.BackdropImageTags.Length > 0) || (dto.ParentBackdropImageTags != null && dto.ParentBackdropImageTags.Length > 0)))
@@ -1305,7 +1377,7 @@ namespace Emby.Server.Implementations.Dto
if (images.Count > 0)
{
dto.ParentBackdropItemId = GetDtoId(parent);
- dto.ParentBackdropImageTags = GetImageTags(parent, images);
+ dto.ParentBackdropImageTags = GetTagsAndFillBlurhashes(dto, parent, ImageType.Backdrop, images);
}
}
diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
index b69a126b3..e71e437ac 100644
--- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj
+++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
@@ -34,13 +34,13 @@
<PackageReference Include="Microsoft.AspNetCore.ResponseCompression" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.2.1" />
- <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.4" />
- <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.1.4" />
- <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.4" />
- <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.4" />
+ <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.5" />
+ <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.1.5" />
+ <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.5" />
+ <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.5" />
<PackageReference Include="Mono.Nat" Version="2.0.1" />
<PackageReference Include="prometheus-net.DotNetRuntime" Version="3.3.1" />
- <PackageReference Include="ServiceStack.Text.Core" Version="5.8.0" />
+ <PackageReference Include="ServiceStack.Text.Core" Version="5.9.0" />
<PackageReference Include="sharpcompress" Version="0.25.0" />
<PackageReference Include="SQLitePCL.pretty.netstandard" Version="2.1.0" />
<PackageReference Include="DotNet.Glob" Version="3.0.9" />
@@ -54,6 +54,7 @@
<TargetFramework>netstandard2.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
+ <TreatWarningsAsErrors Condition=" '$(Configuration)' == 'Release'" >true</TreatWarningsAsErrors>
</PropertyGroup>
<!-- Code Analyzers-->
diff --git a/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs b/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
index 878cee23c..9fce49425 100644
--- a/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
+++ b/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
@@ -23,7 +23,7 @@ namespace Emby.Server.Implementations.EntryPoints
public class ExternalPortForwarding : IServerEntryPoint
{
private readonly IServerApplicationHost _appHost;
- private readonly ILogger _logger;
+ private readonly ILogger<ExternalPortForwarding> _logger;
private readonly IServerConfigurationManager _config;
private readonly IDeviceDiscovery _deviceDiscovery;
diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
index 8e3236407..02ae55071 100644
--- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
@@ -6,6 +6,7 @@ using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
@@ -28,7 +29,7 @@ namespace Emby.Server.Implementations.EntryPoints
private readonly ISessionManager _sessionManager;
private readonly IUserManager _userManager;
- private readonly ILogger _logger;
+ private readonly ILogger<LibraryChangedNotifier> _logger;
/// <summary>
/// The library changed sync lock.
@@ -302,7 +303,7 @@ namespace Emby.Server.Implementations.EntryPoints
.Select(x => x.First())
.ToList();
- SendChangeNotifications(_itemsAdded.ToList(), itemsUpdated, _itemsRemoved.ToList(), foldersAddedTo, foldersRemovedFrom, CancellationToken.None);
+ SendChangeNotifications(_itemsAdded.ToList(), itemsUpdated, _itemsRemoved.ToList(), foldersAddedTo, foldersRemovedFrom, CancellationToken.None).GetAwaiter().GetResult();
if (LibraryUpdateTimer != null)
{
@@ -327,7 +328,7 @@ namespace Emby.Server.Implementations.EntryPoints
/// <param name="foldersAddedTo">The folders added to.</param>
/// <param name="foldersRemovedFrom">The folders removed from.</param>
/// <param name="cancellationToken">The cancellation token.</param>
- private async void SendChangeNotifications(List<BaseItem> itemsAdded, List<BaseItem> itemsUpdated, List<BaseItem> itemsRemoved, List<Folder> foldersAddedTo, List<Folder> foldersRemovedFrom, CancellationToken cancellationToken)
+ private async Task SendChangeNotifications(List<BaseItem> itemsAdded, List<BaseItem> itemsUpdated, List<BaseItem> itemsRemoved, List<Folder> foldersAddedTo, List<Folder> foldersRemovedFrom, CancellationToken cancellationToken)
{
var userIds = _sessionManager.Sessions
.Select(i => i.UserId)
diff --git a/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs b/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs
index 41c0c5115..632735910 100644
--- a/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs
@@ -4,6 +4,7 @@ using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Plugins;
@@ -17,7 +18,7 @@ namespace Emby.Server.Implementations.EntryPoints
private readonly ILiveTvManager _liveTvManager;
private readonly ISessionManager _sessionManager;
private readonly IUserManager _userManager;
- private readonly ILogger _logger;
+ private readonly ILogger<RecordingNotifier> _logger;
public RecordingNotifier(
ISessionManager sessionManager,
@@ -42,29 +43,29 @@ namespace Emby.Server.Implementations.EntryPoints
return Task.CompletedTask;
}
- private void OnLiveTvManagerSeriesTimerCreated(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e)
+ private async void OnLiveTvManagerSeriesTimerCreated(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e)
{
- SendMessage("SeriesTimerCreated", e.Argument);
+ await SendMessage("SeriesTimerCreated", e.Argument).ConfigureAwait(false);
}
- private void OnLiveTvManagerTimerCreated(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e)
+ private async void OnLiveTvManagerTimerCreated(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e)
{
- SendMessage("TimerCreated", e.Argument);
+ await SendMessage("TimerCreated", e.Argument).ConfigureAwait(false);
}
- private void OnLiveTvManagerSeriesTimerCancelled(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e)
+ private async void OnLiveTvManagerSeriesTimerCancelled(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e)
{
- SendMessage("SeriesTimerCancelled", e.Argument);
+ await SendMessage("SeriesTimerCancelled", e.Argument).ConfigureAwait(false);
}
- private void OnLiveTvManagerTimerCancelled(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e)
+ private async void OnLiveTvManagerTimerCancelled(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e)
{
- SendMessage("TimerCancelled", e.Argument);
+ await SendMessage("TimerCancelled", e.Argument).ConfigureAwait(false);
}
- private async void SendMessage(string name, TimerEventInfo info)
+ private async Task SendMessage(string name, TimerEventInfo info)
{
- var users = _userManager.Users.Where(i => i.Policy.EnableLiveTvAccess).Select(i => i.Id).ToList();
+ var users = _userManager.Users.Where(i => i.HasPermission(PermissionKind.EnableLiveTvAccess)).Select(i => i.Id).ToList();
try
{
diff --git a/Emby.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs b/Emby.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs
deleted file mode 100644
index 54f4b67e6..000000000
--- a/Emby.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs
+++ /dev/null
@@ -1,77 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Tasks;
-
-namespace Emby.Server.Implementations.EntryPoints
-{
- /// <summary>
- /// Class RefreshUsersMetadata.
- /// </summary>
- public class RefreshUsersMetadata : IScheduledTask, IConfigurableScheduledTask
- {
- /// <summary>
- /// The user manager.
- /// </summary>
- private readonly IUserManager _userManager;
- private readonly IFileSystem _fileSystem;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="RefreshUsersMetadata" /> class.
- /// </summary>
- public RefreshUsersMetadata(IUserManager userManager, IFileSystem fileSystem)
- {
- _userManager = userManager;
- _fileSystem = fileSystem;
- }
-
- /// <inheritdoc />
- public string Name => "Refresh Users";
-
- /// <inheritdoc />
- public string Key => "RefreshUsers";
-
- /// <inheritdoc />
- public string Description => "Refresh user infos";
-
- /// <inheritdoc />
- public string Category => "Library";
-
- /// <inheritdoc />
- public bool IsHidden => true;
-
- /// <inheritdoc />
- public bool IsEnabled => true;
-
- /// <inheritdoc />
- public bool IsLogged => true;
-
- /// <inheritdoc />
- public async Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
- {
- foreach (var user in _userManager.Users)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- await user.RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(_fileSystem)), cancellationToken).ConfigureAwait(false);
- }
- }
-
- /// <inheritdoc />
- public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
- {
- return new[]
- {
- new TaskTriggerInfo
- {
- IntervalTicks = TimeSpan.FromDays(1).Ticks,
- Type = TaskTriggerInfo.TriggerInterval
- }
- };
- }
- }
-}
diff --git a/Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs b/Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs
index e1dbb663b..083fe4237 100644
--- a/Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs
@@ -3,15 +3,16 @@ using System.Collections.Generic;
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
using MediaBrowser.Common.Plugins;
using MediaBrowser.Common.Updates;
using MediaBrowser.Controller;
-using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Tasks;
+using MediaBrowser.Model.Updates;
namespace Emby.Server.Implementations.EntryPoints
{
@@ -67,10 +68,8 @@ namespace Emby.Server.Implementations.EntryPoints
/// <inheritdoc />
public Task RunAsync()
{
- _userManager.UserDeleted += OnUserDeleted;
- _userManager.UserUpdated += OnUserUpdated;
- _userManager.UserPolicyUpdated += OnUserPolicyUpdated;
- _userManager.UserConfigurationUpdated += OnUserConfigurationUpdated;
+ _userManager.OnUserDeleted += OnUserDeleted;
+ _userManager.OnUserUpdated += OnUserUpdated;
_appHost.HasPendingRestartChanged += OnHasPendingRestartChanged;
@@ -85,29 +84,29 @@ namespace Emby.Server.Implementations.EntryPoints
return Task.CompletedTask;
}
- private void OnPackageInstalling(object sender, InstallationEventArgs e)
+ private async void OnPackageInstalling(object sender, InstallationInfo e)
{
- SendMessageToAdminSessions("PackageInstalling", e.InstallationInfo);
+ await SendMessageToAdminSessions("PackageInstalling", e).ConfigureAwait(false);
}
- private void OnPackageInstallationCancelled(object sender, InstallationEventArgs e)
+ private async void OnPackageInstallationCancelled(object sender, InstallationInfo e)
{
- SendMessageToAdminSessions("PackageInstallationCancelled", e.InstallationInfo);
+ await SendMessageToAdminSessions("PackageInstallationCancelled", e).ConfigureAwait(false);
}
- private void OnPackageInstallationCompleted(object sender, InstallationEventArgs e)
+ private async void OnPackageInstallationCompleted(object sender, InstallationInfo e)
{
- SendMessageToAdminSessions("PackageInstallationCompleted", e.InstallationInfo);
+ await SendMessageToAdminSessions("PackageInstallationCompleted", e).ConfigureAwait(false);
}
- private void OnPackageInstallationFailed(object sender, InstallationFailedEventArgs e)
+ private async void OnPackageInstallationFailed(object sender, InstallationFailedEventArgs e)
{
- SendMessageToAdminSessions("PackageInstallationFailed", e.InstallationInfo);
+ await SendMessageToAdminSessions("PackageInstallationFailed", e.InstallationInfo).ConfigureAwait(false);
}
- private void OnTaskCompleted(object sender, TaskCompletionEventArgs e)
+ private async void OnTaskCompleted(object sender, TaskCompletionEventArgs e)
{
- SendMessageToAdminSessions("ScheduledTaskEnded", e.Result);
+ await SendMessageToAdminSessions("ScheduledTaskEnded", e.Result).ConfigureAwait(false);
}
/// <summary>
@@ -115,9 +114,9 @@ namespace Emby.Server.Implementations.EntryPoints
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The e.</param>
- private void OnPluginUninstalled(object sender, GenericEventArgs<IPlugin> e)
+ private async void OnPluginUninstalled(object sender, IPlugin e)
{
- SendMessageToAdminSessions("PluginUninstalled", e.Argument.GetPluginInfo());
+ await SendMessageToAdminSessions("PluginUninstalled", e).ConfigureAwait(false);
}
/// <summary>
@@ -125,9 +124,9 @@ namespace Emby.Server.Implementations.EntryPoints
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
- private void OnHasPendingRestartChanged(object sender, EventArgs e)
+ private async void OnHasPendingRestartChanged(object sender, EventArgs e)
{
- _sessionManager.SendRestartRequiredNotification(CancellationToken.None);
+ await _sessionManager.SendRestartRequiredNotification(CancellationToken.None).ConfigureAwait(false);
}
/// <summary>
@@ -135,11 +134,11 @@ namespace Emby.Server.Implementations.EntryPoints
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The e.</param>
- private void OnUserUpdated(object sender, GenericEventArgs<User> e)
+ private async void OnUserUpdated(object sender, GenericEventArgs<User> e)
{
var dto = _userManager.GetUserDto(e.Argument);
- SendMessageToUserSession(e.Argument, "UserUpdated", dto);
+ await SendMessageToUserSession(e.Argument, "UserUpdated", dto).ConfigureAwait(false);
}
/// <summary>
@@ -147,26 +146,12 @@ namespace Emby.Server.Implementations.EntryPoints
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The e.</param>
- private void OnUserDeleted(object sender, GenericEventArgs<User> e)
+ private async void OnUserDeleted(object sender, GenericEventArgs<User> e)
{
- SendMessageToUserSession(e.Argument, "UserDeleted", e.Argument.Id.ToString("N", CultureInfo.InvariantCulture));
+ await SendMessageToUserSession(e.Argument, "UserDeleted", e.Argument.Id.ToString("N", CultureInfo.InvariantCulture)).ConfigureAwait(false);
}
- private void OnUserPolicyUpdated(object sender, GenericEventArgs<User> e)
- {
- var dto = _userManager.GetUserDto(e.Argument);
-
- SendMessageToUserSession(e.Argument, "UserPolicyUpdated", dto);
- }
-
- private void OnUserConfigurationUpdated(object sender, GenericEventArgs<User> e)
- {
- var dto = _userManager.GetUserDto(e.Argument);
-
- SendMessageToUserSession(e.Argument, "UserConfigurationUpdated", dto);
- }
-
- private async void SendMessageToAdminSessions<T>(string name, T data)
+ private async Task SendMessageToAdminSessions<T>(string name, T data)
{
try
{
@@ -178,7 +163,7 @@ namespace Emby.Server.Implementations.EntryPoints
}
}
- private async void SendMessageToUserSession<T>(User user, string name, T data)
+ private async Task SendMessageToUserSession<T>(User user, string name, T data)
{
try
{
@@ -209,10 +194,8 @@ namespace Emby.Server.Implementations.EntryPoints
{
if (dispose)
{
- _userManager.UserDeleted -= OnUserDeleted;
- _userManager.UserUpdated -= OnUserUpdated;
- _userManager.UserPolicyUpdated -= OnUserPolicyUpdated;
- _userManager.UserConfigurationUpdated -= OnUserConfigurationUpdated;
+ _userManager.OnUserDeleted -= OnUserDeleted;
+ _userManager.OnUserUpdated -= OnUserUpdated;
_installationManager.PluginUninstalled -= OnPluginUninstalled;
_installationManager.PackageInstalling -= OnPackageInstalling;
diff --git a/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs b/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs
index 50ba0f8fa..ecdce89ce 100644
--- a/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs
+++ b/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs
@@ -3,6 +3,7 @@ using System.Threading.Tasks;
using Emby.Server.Implementations.Udp;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Plugins;
+using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.EntryPoints
@@ -20,8 +21,9 @@ namespace Emby.Server.Implementations.EntryPoints
/// <summary>
/// The logger.
/// </summary>
- private readonly ILogger _logger;
+ private readonly ILogger<UdpServerEntryPoint> _logger;
private readonly IServerApplicationHost _appHost;
+ private readonly IConfiguration _config;
/// <summary>
/// The UDP server.
@@ -35,19 +37,21 @@ namespace Emby.Server.Implementations.EntryPoints
/// </summary>
public UdpServerEntryPoint(
ILogger<UdpServerEntryPoint> logger,
- IServerApplicationHost appHost)
+ IServerApplicationHost appHost,
+ IConfiguration configuration)
{
_logger = logger;
_appHost = appHost;
-
+ _config = configuration;
}
/// <inheritdoc />
- public async Task RunAsync()
+ public Task RunAsync()
{
- _udpServer = new UdpServer(_logger, _appHost);
+ _udpServer = new UdpServer(_logger, _appHost, _config);
_udpServer.Start(PortNumber, _cancellationTokenSource.Token);
+ return Task.CompletedTask;
}
/// <inheritdoc />
diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs
index d66bb7638..87977494a 100644
--- a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs
+++ b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs
@@ -22,7 +22,7 @@ namespace Emby.Server.Implementations.HttpClientManager
/// </summary>
public class HttpClientManager : IHttpClient
{
- private readonly ILogger _logger;
+ private readonly ILogger<HttpClientManager> _logger;
private readonly IApplicationPaths _appPaths;
private readonly IFileSystem _fileSystem;
private readonly IApplicationHost _appHost;
diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
index 7de4f168c..1b6e4b554 100644
--- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
+++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
@@ -41,7 +41,7 @@ namespace Emby.Server.Implementations.HttpServer
/// </summary>
public const string DefaultRedirectKey = "HttpListenerHost:DefaultRedirectPath";
- private readonly ILogger _logger;
+ private readonly ILogger<HttpListenerHost> _logger;
private readonly ILoggerFactory _loggerFactory;
private readonly IServerConfigurationManager _config;
private readonly INetworkManager _networkManager;
@@ -230,7 +230,9 @@ namespace Emby.Server.Implementations.HttpServer
httpRes.StatusCode = statusCode;
- var errContent = NormalizeExceptionMessage(ex) ?? string.Empty;
+ var errContent = _hostEnvironment.IsDevelopment()
+ ? (NormalizeExceptionMessage(ex) ?? string.Empty)
+ : "Error processing request.";
httpRes.ContentType = "text/plain";
httpRes.ContentLength = errContent.Length;
await httpRes.WriteAsync(errContent).ConfigureAwait(false);
@@ -397,7 +399,7 @@ namespace Emby.Server.Implementations.HttpServer
var response = context.Response;
var localPath = context.Request.Path.ToString();
- var req = new WebSocketSharpRequest(request, response, request.Path, _logger);
+ var req = new WebSocketSharpRequest(request, response, request.Path);
return RequestHandler(req, request.GetDisplayUrl(), request.Host.ToString(), localPath, context.RequestAborted);
}
diff --git a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
index 2e9ecc4ae..cc4797790 100644
--- a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
+++ b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
@@ -37,7 +37,7 @@ namespace Emby.Server.Implementations.HttpServer
/// <summary>
/// The logger.
/// </summary>
- private readonly ILogger _logger;
+ private readonly ILogger<HttpResultFactory> _logger;
private readonly IFileSystem _fileSystem;
private readonly IJsonSerializer _jsonSerializer;
private readonly IStreamHelper _streamHelper;
@@ -50,12 +50,13 @@ namespace Emby.Server.Implementations.HttpServer
_fileSystem = fileSystem;
_jsonSerializer = jsonSerializer;
_streamHelper = streamHelper;
- _logger = loggerfactory.CreateLogger("HttpResultFactory");
+ _logger = loggerfactory.CreateLogger<HttpResultFactory>();
}
/// <summary>
/// Gets the result.
/// </summary>
+ /// <param name="requestContext">The request context.</param>
/// <param name="content">The content.</param>
/// <param name="contentType">Type of the content.</param>
/// <param name="responseHeaders">The response headers.</param>
@@ -255,16 +256,20 @@ namespace Emby.Server.Implementations.HttpServer
{
var acceptEncoding = request.Headers[HeaderNames.AcceptEncoding].ToString();
- if (string.IsNullOrEmpty(acceptEncoding))
+ if (!string.IsNullOrEmpty(acceptEncoding))
{
- //if (_brotliCompressor != null && acceptEncoding.IndexOf("br", StringComparison.OrdinalIgnoreCase) != -1)
+ // if (_brotliCompressor != null && acceptEncoding.IndexOf("br", StringComparison.OrdinalIgnoreCase) != -1)
// return "br";
- if (acceptEncoding.IndexOf("deflate", StringComparison.OrdinalIgnoreCase) != -1)
+ if (acceptEncoding.Contains("deflate", StringComparison.OrdinalIgnoreCase))
+ {
return "deflate";
+ }
- if (acceptEncoding.IndexOf("gzip", StringComparison.OrdinalIgnoreCase) != -1)
+ if (acceptEncoding.Contains("gzip", StringComparison.OrdinalIgnoreCase))
+ {
return "gzip";
+ }
}
return null;
diff --git a/Emby.Server.Implementations/HttpServer/ResponseFilter.cs b/Emby.Server.Implementations/HttpServer/ResponseFilter.cs
index 85c3db9b2..3ab5dbc16 100644
--- a/Emby.Server.Implementations/HttpServer/ResponseFilter.cs
+++ b/Emby.Server.Implementations/HttpServer/ResponseFilter.cs
@@ -1,5 +1,4 @@
using System;
-using System.Collections.Generic;
using System.Globalization;
using System.Text;
using MediaBrowser.Controller.Net;
diff --git a/Emby.Server.Implementations/HttpServer/Security/AuthService.cs b/Emby.Server.Implementations/HttpServer/Security/AuthService.cs
index 256b24924..072e94cdd 100644
--- a/Emby.Server.Implementations/HttpServer/Security/AuthService.cs
+++ b/Emby.Server.Implementations/HttpServer/Security/AuthService.cs
@@ -2,11 +2,12 @@
using System;
using System.Linq;
-using System.Security.Authentication;
using Emby.Server.Implementations.SocketSharp;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Enums;
using MediaBrowser.Common.Net;
+using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Security;
using MediaBrowser.Controller.Session;
@@ -45,7 +46,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
public User Authenticate(HttpRequest request, IAuthenticationAttributes authAttributes)
{
- var req = new WebSocketSharpRequest(request, null, request.Path, _logger);
+ var req = new WebSocketSharpRequest(request, null, request.Path);
var user = ValidateUser(req, authAttributes);
return user;
}
@@ -90,7 +91,8 @@ namespace Emby.Server.Implementations.HttpServer.Security
!string.IsNullOrEmpty(auth.Client) &&
!string.IsNullOrEmpty(auth.Device))
{
- _sessionManager.LogSessionActivity(auth.Client,
+ _sessionManager.LogSessionActivity(
+ auth.Client,
auth.Version,
auth.DeviceId,
auth.Device,
@@ -104,21 +106,21 @@ namespace Emby.Server.Implementations.HttpServer.Security
private void ValidateUserAccess(
User user,
IRequest request,
- IAuthenticationAttributes authAttribtues,
+ IAuthenticationAttributes authAttributes,
AuthorizationInfo auth)
{
- if (user.Policy.IsDisabled)
+ if (user.HasPermission(PermissionKind.IsDisabled))
{
throw new SecurityException("User account has been disabled.");
}
- if (!user.Policy.EnableRemoteAccess && !_networkManager.IsInLocalNetwork(request.RemoteIp))
+ if (!user.HasPermission(PermissionKind.EnableRemoteAccess) && !_networkManager.IsInLocalNetwork(request.RemoteIp))
{
throw new SecurityException("User account has been disabled.");
}
- if (!user.Policy.IsAdministrator
- && !authAttribtues.EscapeParentalControl
+ if (!user.HasPermission(PermissionKind.IsAdministrator)
+ && !authAttributes.EscapeParentalControl
&& !user.IsParentalScheduleAllowed())
{
request.Response.Headers.Add("X-Application-Error-Code", "ParentalControl");
@@ -180,7 +182,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
{
if (roles.Contains("admin", StringComparer.OrdinalIgnoreCase))
{
- if (user == null || !user.Policy.IsAdministrator)
+ if (user == null || !user.HasPermission(PermissionKind.IsAdministrator))
{
throw new SecurityException("User does not have admin access.");
}
@@ -188,7 +190,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
if (roles.Contains("delete", StringComparer.OrdinalIgnoreCase))
{
- if (user == null || !user.Policy.EnableContentDeletion)
+ if (user == null || !user.HasPermission(PermissionKind.EnableContentDeletion))
{
throw new SecurityException("User does not have delete access.");
}
@@ -196,7 +198,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
if (roles.Contains("download", StringComparer.OrdinalIgnoreCase))
{
- if (user == null || !user.Policy.EnableContentDownloading)
+ if (user == null || !user.HasPermission(PermissionKind.EnableContentDownloading))
{
throw new SecurityException("User does not have download access.");
}
diff --git a/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs b/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs
index 129faeaab..9558cb4c6 100644
--- a/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs
+++ b/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs
@@ -149,9 +149,9 @@ namespace Emby.Server.Implementations.HttpServer.Security
{
info.User = _userManager.GetUserById(tokenInfo.UserId);
- if (info.User != null && !string.Equals(info.User.Name, tokenInfo.UserName, StringComparison.OrdinalIgnoreCase))
+ if (info.User != null && !string.Equals(info.User.Username, tokenInfo.UserName, StringComparison.OrdinalIgnoreCase))
{
- tokenInfo.UserName = info.User.Name;
+ tokenInfo.UserName = info.User.Username;
updateToken = true;
}
}
diff --git a/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs b/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs
index 166952c64..03fcfa53d 100644
--- a/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs
+++ b/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs
@@ -1,7 +1,7 @@
#pragma warning disable CS1591
using System;
-using MediaBrowser.Controller.Entities;
+using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Security;
diff --git a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs
index 095725c50..c64d57339 100644
--- a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs
+++ b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs
@@ -24,7 +24,7 @@ namespace Emby.Server.Implementations.HttpServer
/// <summary>
/// The logger.
/// </summary>
- private readonly ILogger _logger;
+ private readonly ILogger<WebSocketConnection> _logger;
/// <summary>
/// The json serializer options.
@@ -78,6 +78,9 @@ namespace Emby.Server.Implementations.HttpServer
/// <value>The last activity date.</value>
public DateTime LastActivityDate { get; private set; }
+ /// <inheritdoc />
+ public DateTime LastKeepAliveDate { get; set; }
+
/// <summary>
/// Gets or sets the query string.
/// </summary>
@@ -218,7 +221,42 @@ namespace Emby.Server.Implementations.HttpServer
Connection = this
};
- await OnReceive(info).ConfigureAwait(false);
+ if (info.MessageType.Equals("KeepAlive", StringComparison.Ordinal))
+ {
+ await SendKeepAliveResponse();
+ }
+ else
+ {
+ await OnReceive(info).ConfigureAwait(false);
+ }
+ }
+
+ private Task SendKeepAliveResponse()
+ {
+ LastKeepAliveDate = DateTime.UtcNow;
+ return SendAsync(new WebSocketMessage<string>
+ {
+ MessageType = "KeepAlive"
+ }, CancellationToken.None);
+ }
+
+ /// <inheritdoc />
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ /// <summary>
+ /// Releases unmanaged and - optionally - managed resources.
+ /// </summary>
+ /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
+ protected virtual void Dispose(bool dispose)
+ {
+ if (dispose)
+ {
+ _socket.Dispose();
+ }
}
}
}
diff --git a/Emby.Server.Implementations/IO/LibraryMonitor.cs b/Emby.Server.Implementations/IO/LibraryMonitor.cs
index eb5e190aa..49bca7dac 100644
--- a/Emby.Server.Implementations/IO/LibraryMonitor.cs
+++ b/Emby.Server.Implementations/IO/LibraryMonitor.cs
@@ -18,7 +18,7 @@ namespace Emby.Server.Implementations.IO
{
public class LibraryMonitor : ILibraryMonitor
{
- private readonly ILogger _logger;
+ private readonly ILogger<LibraryMonitor> _logger;
private readonly ILibraryManager _libraryManager;
private readonly IServerConfigurationManager _configurationManager;
private readonly IFileSystem _fileSystem;
diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs
index 7461ec4f1..2bcfc82b6 100644
--- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs
+++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs
@@ -20,7 +20,7 @@ namespace Emby.Server.Implementations.IO
/// </summary>
public class ManagedFileSystem : IFileSystem
{
- protected ILogger Logger;
+ protected ILogger<ManagedFileSystem> Logger;
private readonly List<IShortcutHandler> _shortcutHandlers = new List<IShortcutHandler>();
private readonly string _tempPath;
diff --git a/Emby.Server.Implementations/IStartupOptions.cs b/Emby.Server.Implementations/IStartupOptions.cs
index 16b68170b..0b9f80538 100644
--- a/Emby.Server.Implementations/IStartupOptions.cs
+++ b/Emby.Server.Implementations/IStartupOptions.cs
@@ -1,3 +1,7 @@
+#pragma warning disable CS1591
+
+using System;
+
namespace Emby.Server.Implementations
{
public interface IStartupOptions
@@ -36,5 +40,10 @@ namespace Emby.Server.Implementations
/// Gets the value of the --plugin-manifest-url command line option.
/// </summary>
string PluginManifestUrl { get; }
+
+ /// <summary>
+ /// Gets the value of the --published-server-url command line option.
+ /// </summary>
+ Uri PublishedServerUrl { get; }
}
}
diff --git a/Emby.Server.Implementations/Images/ArtistImageProvider.cs b/Emby.Server.Implementations/Images/ArtistImageProvider.cs
new file mode 100644
index 000000000..52896720e
--- /dev/null
+++ b/Emby.Server.Implementations/Images/ArtistImageProvider.cs
@@ -0,0 +1,60 @@
+#pragma warning disable CS1591
+
+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.Library;
+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
+{
+ /// <summary>
+ /// Class ArtistImageProvider.
+ /// </summary>
+ public class ArtistImageProvider : BaseDynamicImageProvider<MusicArtist>
+ {
+ /// <summary>
+ /// The library manager.
+ /// </summary>
+ private readonly ILibraryManager _libraryManager;
+
+ public ArtistImageProvider(IFileSystem fileSystem, IProviderManager providerManager, IApplicationPaths applicationPaths, IImageProcessor imageProcessor, ILibraryManager libraryManager) : base(fileSystem, providerManager, applicationPaths, imageProcessor)
+ {
+ _libraryManager = libraryManager;
+ }
+
+ /// <summary>
+ /// Get children objects used to create an artist image.
+ /// </summary>
+ /// <param name="item">The artist used to create the image.</param>
+ /// <returns>Any relevant children objects.</returns>
+ protected override IReadOnlyList<BaseItem> GetItemsWithImages(BaseItem item)
+ {
+ return Array.Empty<BaseItem>();
+
+ // TODO enable this when BaseDynamicImageProvider objects are configurable
+ // return _libraryManager.GetItemList(new InternalItemsQuery
+ // {
+ // ArtistIds = new[] { item.Id },
+ // IncludeItemTypes = new[] { typeof(MusicAlbum).Name },
+ // OrderBy = new[] { (ItemSortBy.Random, SortOrder.Ascending) },
+ // Limit = 4,
+ // Recursive = true,
+ // ImageTypes = new[] { ImageType.Primary },
+ // DtoOptions = new DtoOptions(false)
+ // });
+ }
+ }
+}
diff --git a/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs b/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs
index fd50f156a..57302b506 100644
--- a/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs
+++ b/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs
@@ -194,7 +194,8 @@ namespace Emby.Server.Implementations.Images
return outputPath;
}
- protected virtual string CreateImage(BaseItem item,
+ protected virtual string CreateImage(
+ BaseItem item,
IReadOnlyCollection<BaseItem> itemsWithImages,
string outputPathWithoutExtension,
ImageType imageType,
@@ -214,7 +215,12 @@ namespace Emby.Server.Implementations.Images
if (imageType == ImageType.Primary)
{
- if (item is UserView || item is Playlist || item is MusicGenre || item is Genre || item is PhotoAlbum)
+ if (item is UserView
+ || item is Playlist
+ || item is MusicGenre
+ || item is Genre
+ || item is PhotoAlbum
+ || item is MusicArtist)
{
return CreateSquareCollage(item, itemsWithImages, outputPath);
}
@@ -225,7 +231,7 @@ namespace Emby.Server.Implementations.Images
throw new ArgumentException("Unexpected image type", nameof(imageType));
}
- public bool HasChanged(BaseItem item, IDirectoryService directoryServicee)
+ public bool HasChanged(BaseItem item, IDirectoryService directoryService)
{
if (!Supports(item))
{
@@ -236,6 +242,7 @@ namespace Emby.Server.Implementations.Images
{
return true;
}
+
if (SupportedImages.Contains(ImageType.Thumb) && HasChanged(item, ImageType.Thumb))
{
return true;
diff --git a/Emby.Server.Implementations/UserViews/CollectionFolderImageProvider.cs b/Emby.Server.Implementations/Images/CollectionFolderImageProvider.cs
index a3f3f6cb4..dc8062b45 100644
--- a/Emby.Server.Implementations/UserViews/CollectionFolderImageProvider.cs
+++ b/Emby.Server.Implementations/Images/CollectionFolderImageProvider.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.IO;
@@ -11,7 +13,7 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Querying;
-namespace Emby.Server.Implementations.UserViews
+namespace Emby.Server.Implementations.Images
{
public class CollectionFolderImageProvider : BaseDynamicImageProvider<CollectionFolder>
{
diff --git a/Emby.Server.Implementations/UserViews/DynamicImageProvider.cs b/Emby.Server.Implementations/Images/DynamicImageProvider.cs
index 78ac95f85..ca0aa4a9f 100644
--- a/Emby.Server.Implementations/UserViews/DynamicImageProvider.cs
+++ b/Emby.Server.Implementations/Images/DynamicImageProvider.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.IO;
@@ -14,18 +16,16 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
-namespace Emby.Server.Implementations.UserViews
+namespace Emby.Server.Implementations.Images
{
public class DynamicImageProvider : BaseDynamicImageProvider<UserView>
{
private readonly IUserManager _userManager;
- private readonly ILibraryManager _libraryManager;
- public DynamicImageProvider(IFileSystem fileSystem, IProviderManager providerManager, IApplicationPaths applicationPaths, IImageProcessor imageProcessor, IUserManager userManager, ILibraryManager libraryManager)
+ public DynamicImageProvider(IFileSystem fileSystem, IProviderManager providerManager, IApplicationPaths applicationPaths, IImageProcessor imageProcessor, IUserManager userManager)
: base(fileSystem, providerManager, applicationPaths, imageProcessor)
{
_userManager = userManager;
- _libraryManager = libraryManager;
}
protected override IReadOnlyList<BaseItem> GetItemsWithImages(BaseItem item)
diff --git a/Emby.Server.Implementations/UserViews/FolderImageProvider.cs b/Emby.Server.Implementations/Images/FolderImageProvider.cs
index 4655cd928..e9523386e 100644
--- a/Emby.Server.Implementations/UserViews/FolderImageProvider.cs
+++ b/Emby.Server.Implementations/Images/FolderImageProvider.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
using Emby.Server.Implementations.Images;
using MediaBrowser.Common.Configuration;
@@ -11,7 +13,7 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Querying;
-namespace Emby.Server.Implementations.UserViews
+namespace Emby.Server.Implementations.Images
{
public abstract class BaseFolderImageProvider<T> : BaseDynamicImageProvider<T>
where T : Folder, new()
@@ -75,16 +77,12 @@ namespace Emby.Server.Implementations.UserViews
return false;
}
- var folder = item as Folder;
- if (folder != null)
+ if (item is Folder && item.IsTopParent)
{
- if (folder.IsTopParent)
- {
- return false;
- }
+ return false;
}
+
return true;
- //return item.SourceType == SourceType.Library;
}
}
diff --git a/Emby.Server.Implementations/Playlists/PlaylistImageProvider.cs b/Emby.Server.Implementations/Images/GenreImageProvider.cs
index bb56d9771..d2aeccdb2 100644
--- a/Emby.Server.Implementations/Playlists/PlaylistImageProvider.cs
+++ b/Emby.Server.Implementations/Images/GenreImageProvider.cs
@@ -1,6 +1,6 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
-using System.Linq;
-using Emby.Server.Implementations.Images;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto;
@@ -9,66 +9,21 @@ using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Playlists;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Querying;
-namespace Emby.Server.Implementations.Playlists
+namespace Emby.Server.Implementations.Images
{
- public class PlaylistImageProvider : BaseDynamicImageProvider<Playlist>
- {
- public PlaylistImageProvider(IFileSystem fileSystem, IProviderManager providerManager, IApplicationPaths applicationPaths, IImageProcessor imageProcessor) : base(fileSystem, providerManager, applicationPaths, imageProcessor)
- {
- }
-
- protected override IReadOnlyList<BaseItem> GetItemsWithImages(BaseItem item)
- {
- var playlist = (Playlist)item;
-
- return playlist.GetManageableItems()
- .Select(i =>
- {
- var subItem = i.Item2;
-
- var episode = subItem as Episode;
-
- if (episode != null)
- {
- var series = episode.Series;
- if (series != null && series.HasImage(ImageType.Primary))
- {
- return series;
- }
- }
-
- if (subItem.HasImage(ImageType.Primary))
- {
- return subItem;
- }
-
- var parent = subItem.GetOwner() ?? subItem.GetParent();
-
- if (parent != null && parent.HasImage(ImageType.Primary))
- {
- if (parent is MusicAlbum)
- {
- return parent;
- }
- }
-
- return null;
- })
- .Where(i => i != null)
- .GroupBy(x => x.Id)
- .Select(x => x.First())
- .ToList();
- }
- }
-
+ /// <summary>
+ /// Class MusicGenreImageProvider.
+ /// </summary>
public class MusicGenreImageProvider : BaseDynamicImageProvider<MusicGenre>
{
+ /// <summary>
+ /// The library manager.
+ /// </summary>
private readonly ILibraryManager _libraryManager;
public MusicGenreImageProvider(IFileSystem fileSystem, IProviderManager providerManager, IApplicationPaths applicationPaths, IImageProcessor imageProcessor, ILibraryManager libraryManager) : base(fileSystem, providerManager, applicationPaths, imageProcessor)
@@ -76,6 +31,11 @@ namespace Emby.Server.Implementations.Playlists
_libraryManager = libraryManager;
}
+ /// <summary>
+ /// Get children objects used to create an music genre image.
+ /// </summary>
+ /// <param name="item">The music genre used to create the image.</param>
+ /// <returns>Any relevant children objects.</returns>
protected override IReadOnlyList<BaseItem> GetItemsWithImages(BaseItem item)
{
return _libraryManager.GetItemList(new InternalItemsQuery
@@ -91,8 +51,14 @@ namespace Emby.Server.Implementations.Playlists
}
}
+ /// <summary>
+ /// Class GenreImageProvider.
+ /// </summary>
public class GenreImageProvider : BaseDynamicImageProvider<Genre>
{
+ /// <summary>
+ /// The library manager.
+ /// </summary>
private readonly ILibraryManager _libraryManager;
public GenreImageProvider(IFileSystem fileSystem, IProviderManager providerManager, IApplicationPaths applicationPaths, IImageProcessor imageProcessor, ILibraryManager libraryManager) : base(fileSystem, providerManager, applicationPaths, imageProcessor)
@@ -100,6 +66,11 @@ namespace Emby.Server.Implementations.Playlists
_libraryManager = libraryManager;
}
+ /// <summary>
+ /// Get children objects used to create an genre image.
+ /// </summary>
+ /// <param name="item">The genre used to create the image.</param>
+ /// <returns>Any relevant children objects.</returns>
protected override IReadOnlyList<BaseItem> GetItemsWithImages(BaseItem item)
{
return _libraryManager.GetItemList(new InternalItemsQuery
diff --git a/Emby.Server.Implementations/Images/PlaylistImageProvider.cs b/Emby.Server.Implementations/Images/PlaylistImageProvider.cs
new file mode 100644
index 000000000..0ce1b91e8
--- /dev/null
+++ b/Emby.Server.Implementations/Images/PlaylistImageProvider.cs
@@ -0,0 +1,66 @@
+#pragma warning disable CS1591
+
+using System.Collections.Generic;
+using System.Linq;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Controller.Drawing;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.Playlists;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.IO;
+
+namespace Emby.Server.Implementations.Images
+{
+ public class PlaylistImageProvider : BaseDynamicImageProvider<Playlist>
+ {
+ public PlaylistImageProvider(IFileSystem fileSystem, IProviderManager providerManager, IApplicationPaths applicationPaths, IImageProcessor imageProcessor) : base(fileSystem, providerManager, applicationPaths, imageProcessor)
+ {
+ }
+
+ protected override IReadOnlyList<BaseItem> GetItemsWithImages(BaseItem item)
+ {
+ var playlist = (Playlist)item;
+
+ return playlist.GetManageableItems()
+ .Select(i =>
+ {
+ var subItem = i.Item2;
+
+ var episode = subItem as Episode;
+
+ if (episode != null)
+ {
+ var series = episode.Series;
+ if (series != null && series.HasImage(ImageType.Primary))
+ {
+ return series;
+ }
+ }
+
+ if (subItem.HasImage(ImageType.Primary))
+ {
+ return subItem;
+ }
+
+ var parent = subItem.GetOwner() ?? subItem.GetParent();
+
+ if (parent != null && parent.HasImage(ImageType.Primary))
+ {
+ if (parent is MusicAlbum)
+ {
+ return parent;
+ }
+ }
+
+ return null;
+ })
+ .Where(i => i != null)
+ .GroupBy(x => x.Id)
+ .Select(x => x.First())
+ .ToList();
+ }
+ }
+}
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index 0b86b2db7..7d74ced6c 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -17,14 +17,16 @@ using Emby.Server.Implementations.Library.Resolvers;
using Emby.Server.Implementations.Library.Validators;
using Emby.Server.Implementations.Playlists;
using Emby.Server.Implementations.ScheduledTasks;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Enums;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
@@ -35,6 +37,7 @@ using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dlna;
+using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
@@ -44,17 +47,20 @@ using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Tasks;
using MediaBrowser.Providers.MediaInfo;
using Microsoft.Extensions.Logging;
+using Episode = MediaBrowser.Controller.Entities.TV.Episode;
+using Genre = MediaBrowser.Controller.Entities.Genre;
+using Person = MediaBrowser.Controller.Entities.Person;
using SortOrder = MediaBrowser.Model.Entities.SortOrder;
using VideoResolver = Emby.Naming.Video.VideoResolver;
namespace Emby.Server.Implementations.Library
{
/// <summary>
- /// Class LibraryManager
+ /// Class LibraryManager.
/// </summary>
public class LibraryManager : ILibraryManager
{
- private readonly ILogger _logger;
+ private readonly ILogger<LibraryManager> _logger;
private readonly ITaskManager _taskManager;
private readonly IUserManager _userManager;
private readonly IUserDataManager _userDataRepository;
@@ -67,6 +73,7 @@ namespace Emby.Server.Implementations.Library
private readonly IFileSystem _fileSystem;
private readonly IItemRepository _itemRepository;
private readonly ConcurrentDictionary<Guid, BaseItem> _libraryItemsCache;
+ private readonly IImageProcessor _imageProcessor;
private NamingOptions _namingOptions;
private string[] _videoFileExtensions;
@@ -135,6 +142,13 @@ namespace Emby.Server.Implementations.Library
/// <param name="userManager">The user manager.</param>
/// <param name="configurationManager">The configuration manager.</param>
/// <param name="userDataRepository">The user data repository.</param>
+ /// <param name="libraryMonitorFactory">The library monitor.</param>
+ /// <param name="fileSystem">The file system.</param>
+ /// <param name="providerManagerFactory">The provider manager.</param>
+ /// <param name="userviewManagerFactory">The userview manager.</param>
+ /// <param name="mediaEncoder">The media encoder.</param>
+ /// <param name="itemRepository">The item repository.</param>
+ /// <param name="imageProcessor">The image processor.</param>
public LibraryManager(
IServerApplicationHost appHost,
ILogger<LibraryManager> logger,
@@ -147,7 +161,8 @@ namespace Emby.Server.Implementations.Library
Lazy<IProviderManager> providerManagerFactory,
Lazy<IUserViewManager> userviewManagerFactory,
IMediaEncoder mediaEncoder,
- IItemRepository itemRepository)
+ IItemRepository itemRepository,
+ IImageProcessor imageProcessor)
{
_appHost = appHost;
_logger = logger;
@@ -161,6 +176,7 @@ namespace Emby.Server.Implementations.Library
_userviewManagerFactory = userviewManagerFactory;
_mediaEncoder = mediaEncoder;
_itemRepository = itemRepository;
+ _imageProcessor = imageProcessor;
_libraryItemsCache = new ConcurrentDictionary<Guid, BaseItem>();
@@ -498,8 +514,8 @@ namespace Emby.Server.Implementations.Library
return key.GetMD5();
}
- public BaseItem ResolvePath(FileSystemMetadata fileInfo, Folder parent = null)
- => ResolvePath(fileInfo, new DirectoryService(_fileSystem), null, parent);
+ public BaseItem ResolvePath(FileSystemMetadata fileInfo, Folder parent = null, bool allowIgnorePath = true)
+ => ResolvePath(fileInfo, new DirectoryService(_fileSystem), null, parent, allowIgnorePath: allowIgnorePath);
private BaseItem ResolvePath(
FileSystemMetadata fileInfo,
@@ -507,7 +523,8 @@ namespace Emby.Server.Implementations.Library
IItemResolver[] resolvers,
Folder parent = null,
string collectionType = null,
- LibraryOptions libraryOptions = null)
+ LibraryOptions libraryOptions = null,
+ bool allowIgnorePath = true)
{
if (fileInfo == null)
{
@@ -531,7 +548,7 @@ namespace Emby.Server.Implementations.Library
};
// Return null if ignore rules deem that we should do so
- if (IgnoreFile(args.FileInfo, args.Parent))
+ if (allowIgnorePath && IgnoreFile(args.FileInfo, args.Parent))
{
return null;
}
@@ -695,7 +712,9 @@ namespace Emby.Server.Implementations.Library
Directory.CreateDirectory(rootFolderPath);
- var rootFolder = GetItemById(GetNewItemId(rootFolderPath, typeof(AggregateFolder))) as AggregateFolder ?? ((Folder)ResolvePath(_fileSystem.GetDirectoryInfo(rootFolderPath))).DeepCopy<Folder, AggregateFolder>();
+ var rootFolder = GetItemById(GetNewItemId(rootFolderPath, typeof(AggregateFolder))) as AggregateFolder ??
+ ((Folder) ResolvePath(_fileSystem.GetDirectoryInfo(rootFolderPath), allowIgnorePath: false))
+ .DeepCopy<Folder, AggregateFolder>();
// In case program data folder was moved
if (!string.Equals(rootFolder.Path, rootFolderPath, StringComparison.Ordinal))
@@ -776,7 +795,7 @@ namespace Emby.Server.Implementations.Library
if (tmpItem == null)
{
_logger.LogDebug("Creating new userRootFolder with DeepCopy");
- tmpItem = ((Folder)ResolvePath(_fileSystem.GetDirectoryInfo(userRootPath))).DeepCopy<Folder, UserRootFolder>();
+ tmpItem = ((Folder)ResolvePath(_fileSystem.GetDirectoryInfo(userRootPath), allowIgnorePath: false)).DeepCopy<Folder, UserRootFolder>();
}
// In case program data folder was moved
@@ -1524,7 +1543,8 @@ namespace Emby.Server.Implementations.Library
}
// Handle grouping
- if (user != null && !string.IsNullOrEmpty(view.ViewType) && UserView.IsEligibleForGrouping(view.ViewType) && user.Configuration.GroupedFolders.Length > 0)
+ if (user != null && !string.IsNullOrEmpty(view.ViewType) && UserView.IsEligibleForGrouping(view.ViewType)
+ && user.GetPreference(PreferenceKind.GroupedFolders).Length > 0)
{
return GetUserRootFolder()
.GetChildren(user, true)
@@ -1815,10 +1835,90 @@ namespace Emby.Server.Implementations.Library
}
}
- public void UpdateImages(BaseItem item)
+ private bool ImageNeedsRefresh(ItemImageInfo image)
{
- _itemRepository.SaveImages(item);
+ if (image.Path != null && image.IsLocalFile)
+ {
+ if (image.Width == 0 || image.Height == 0 || string.IsNullOrEmpty(image.BlurHash))
+ {
+ return true;
+ }
+ try
+ {
+ return _fileSystem.GetLastWriteTimeUtc(image.Path) != image.DateModified;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Cannot get file info for {0}", image.Path);
+ return false;
+ }
+ }
+
+ return image.Path != null && !image.IsLocalFile;
+ }
+
+ public void UpdateImages(BaseItem item, bool forceUpdate = false)
+ {
+ if (item == null)
+ {
+ throw new ArgumentNullException(nameof(item));
+ }
+
+ var outdated = forceUpdate ? item.ImageInfos.Where(i => i.Path != null).ToArray() : item.ImageInfos.Where(ImageNeedsRefresh).ToArray();
+ if (outdated.Length == 0)
+ {
+ RegisterItem(item);
+ return;
+ }
+
+ foreach (var img in outdated)
+ {
+ var image = img;
+ if (!img.IsLocalFile)
+ {
+ try
+ {
+ var index = item.GetImageIndex(img);
+ image = ConvertImageToLocal(item, img, index).ConfigureAwait(false).GetAwaiter().GetResult();
+ }
+ catch (ArgumentException)
+ {
+ _logger.LogWarning("Cannot get image index for {0}", img.Path);
+ continue;
+ }
+ catch (InvalidOperationException)
+ {
+ _logger.LogWarning("Cannot fetch image from {0}", img.Path);
+ continue;
+ }
+ }
+
+ ImageDimensions size = _imageProcessor.GetImageDimensions(item, image);
+ image.Width = size.Width;
+ image.Height = size.Height;
+
+ try
+ {
+ image.BlurHash = _imageProcessor.GetImageBlurHash(image.Path);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Cannot compute blurhash for {0}", image.Path);
+ image.BlurHash = string.Empty;
+ }
+
+ try
+ {
+ image.DateModified = _fileSystem.GetLastWriteTimeUtc(image.Path);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Cannot update DateModified for {0}", image.Path);
+ }
+ }
+
+ _itemRepository.SaveImages(item);
RegisterItem(item);
}
@@ -1839,7 +1939,7 @@ namespace Emby.Server.Implementations.Library
item.DateLastSaved = DateTime.UtcNow;
- RegisterItem(item);
+ UpdateImages(item, updateReason >= ItemUpdateType.ImageUpdate);
}
_itemRepository.SaveItems(itemsList, cancellationToken);
diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs
index 01fe98f3a..02add6600 100644
--- a/Emby.Server.Implementations/Library/MediaSourceManager.cs
+++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs
@@ -7,6 +7,8 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Enums;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Entities;
@@ -14,7 +16,6 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
@@ -33,7 +34,7 @@ namespace Emby.Server.Implementations.Library
private readonly ILibraryManager _libraryManager;
private readonly IJsonSerializer _jsonSerializer;
private readonly IFileSystem _fileSystem;
- private readonly ILogger _logger;
+ private readonly ILogger<MediaSourceManager> _logger;
private readonly IUserDataManager _userDataManager;
private readonly IMediaEncoder _mediaEncoder;
private readonly ILocalizationManager _localizationManager;
@@ -190,10 +191,7 @@ namespace Emby.Server.Implementations.Library
{
if (string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase))
{
- if (!user.Policy.EnableAudioPlaybackTranscoding)
- {
- source.SupportsTranscoding = false;
- }
+ source.SupportsTranscoding = user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding);
}
}
}
@@ -352,7 +350,9 @@ namespace Emby.Server.Implementations.Library
private void SetDefaultSubtitleStreamIndex(MediaSourceInfo source, UserItemData userData, User user, bool allowRememberingSelection)
{
- if (userData.SubtitleStreamIndex.HasValue && user.Configuration.RememberSubtitleSelections && user.Configuration.SubtitleMode != SubtitlePlaybackMode.None && allowRememberingSelection)
+ if (userData.SubtitleStreamIndex.HasValue
+ && user.RememberSubtitleSelections
+ && user.SubtitleMode != SubtitlePlaybackMode.None && allowRememberingSelection)
{
var index = userData.SubtitleStreamIndex.Value;
// Make sure the saved index is still valid
@@ -363,26 +363,27 @@ namespace Emby.Server.Implementations.Library
}
}
- var preferredSubs = string.IsNullOrEmpty(user.Configuration.SubtitleLanguagePreference)
- ? Array.Empty<string>() : NormalizeLanguage(user.Configuration.SubtitleLanguagePreference);
+
+ var preferredSubs = string.IsNullOrEmpty(user.SubtitleLanguagePreference)
+ ? Array.Empty<string>() : NormalizeLanguage(user.SubtitleLanguagePreference);
var defaultAudioIndex = source.DefaultAudioStreamIndex;
var audioLangage = defaultAudioIndex == null
? null
: source.MediaStreams.Where(i => i.Type == MediaStreamType.Audio && i.Index == defaultAudioIndex).Select(i => i.Language).FirstOrDefault();
- source.DefaultSubtitleStreamIndex = MediaStreamSelector.GetDefaultSubtitleStreamIndex(source.MediaStreams,
+ source.DefaultSubtitleStreamIndex = MediaStreamSelector.GetDefaultSubtitleStreamIndex(
+ source.MediaStreams,
preferredSubs,
- user.Configuration.SubtitleMode,
+ user.SubtitleMode,
audioLangage);
- MediaStreamSelector.SetSubtitleStreamScores(source.MediaStreams, preferredSubs,
- user.Configuration.SubtitleMode, audioLangage);
+ MediaStreamSelector.SetSubtitleStreamScores(source.MediaStreams, preferredSubs, user.SubtitleMode, audioLangage);
}
private void SetDefaultAudioStreamIndex(MediaSourceInfo source, UserItemData userData, User user, bool allowRememberingSelection)
{
- if (userData.AudioStreamIndex.HasValue && user.Configuration.RememberAudioSelections && allowRememberingSelection)
+ if (userData.AudioStreamIndex.HasValue && user.RememberAudioSelections && allowRememberingSelection)
{
var index = userData.AudioStreamIndex.Value;
// Make sure the saved index is still valid
@@ -393,11 +394,11 @@ namespace Emby.Server.Implementations.Library
}
}
- var preferredAudio = string.IsNullOrEmpty(user.Configuration.AudioLanguagePreference)
+ var preferredAudio = string.IsNullOrEmpty(user.AudioLanguagePreference)
? Array.Empty<string>()
- : NormalizeLanguage(user.Configuration.AudioLanguagePreference);
+ : NormalizeLanguage(user.AudioLanguagePreference);
- source.DefaultAudioStreamIndex = MediaStreamSelector.GetDefaultAudioStreamIndex(source.MediaStreams, preferredAudio, user.Configuration.PlayDefaultAudioTrack);
+ source.DefaultAudioStreamIndex = MediaStreamSelector.GetDefaultAudioStreamIndex(source.MediaStreams, preferredAudio, user.PlayDefaultAudioTrack);
}
public void SetDefaultAudioAndSubtitleStreamIndexes(BaseItem item, MediaSourceInfo source, User user)
@@ -521,11 +522,7 @@ namespace Emby.Server.Implementations.Library
SetDefaultAudioAndSubtitleStreamIndexes(item, clone, user);
}
- return new Tuple<LiveStreamResponse, IDirectStreamProvider>(new LiveStreamResponse
- {
- MediaSource = clone
-
- }, liveStream as IDirectStreamProvider);
+ return new Tuple<LiveStreamResponse, IDirectStreamProvider>(new LiveStreamResponse(clone), liveStream as IDirectStreamProvider);
}
private static void AddMediaInfo(MediaSourceInfo mediaSource, bool isAudio)
@@ -538,7 +535,7 @@ namespace Emby.Server.Implementations.Library
mediaSource.RunTimeTicks = null;
}
- var audioStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == MediaBrowser.Model.Entities.MediaStreamType.Audio);
+ var audioStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio);
if (audioStream == null || audioStream.Index == -1)
{
@@ -549,7 +546,7 @@ namespace Emby.Server.Implementations.Library
mediaSource.DefaultAudioStreamIndex = audioStream.Index;
}
- var videoStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == MediaBrowser.Model.Entities.MediaStreamType.Video);
+ var videoStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video);
if (videoStream != null)
{
if (!videoStream.BitRate.HasValue)
@@ -560,17 +557,14 @@ namespace Emby.Server.Implementations.Library
{
videoStream.BitRate = 30000000;
}
-
else if (width >= 1900)
{
videoStream.BitRate = 20000000;
}
-
else if (width >= 1200)
{
videoStream.BitRate = 8000000;
}
-
else if (width >= 700)
{
videoStream.BitRate = 2000000;
@@ -674,13 +668,14 @@ namespace Emby.Server.Implementations.Library
mediaSource.AnalyzeDurationMs = 3000;
}
- mediaInfo = await _mediaEncoder.GetMediaInfo(new MediaInfoRequest
+ mediaInfo = await _mediaEncoder.GetMediaInfo(
+ new MediaInfoRequest
{
MediaSource = mediaSource,
MediaType = isAudio ? DlnaProfileType.Audio : DlnaProfileType.Video,
ExtractChapters = false
-
- }, cancellationToken).ConfigureAwait(false);
+ },
+ cancellationToken).ConfigureAwait(false);
if (cacheFilePath != null)
{
diff --git a/Emby.Server.Implementations/Library/MediaStreamSelector.cs b/Emby.Server.Implementations/Library/MediaStreamSelector.cs
index e27145a1d..ca904c4ec 100644
--- a/Emby.Server.Implementations/Library/MediaStreamSelector.cs
+++ b/Emby.Server.Implementations/Library/MediaStreamSelector.cs
@@ -3,7 +3,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using MediaBrowser.Model.Configuration;
+using Jellyfin.Data.Enums;
using MediaBrowser.Model.Entities;
namespace Emby.Server.Implementations.Library
diff --git a/Emby.Server.Implementations/Library/MusicManager.cs b/Emby.Server.Implementations/Library/MusicManager.cs
index 1ec578371..0bdc59914 100644
--- a/Emby.Server.Implementations/Library/MusicManager.cs
+++ b/Emby.Server.Implementations/Library/MusicManager.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
@@ -10,6 +11,7 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Playlists;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
+using MusicAlbum = MediaBrowser.Controller.Entities.Audio.MusicAlbum;
namespace Emby.Server.Implementations.Library
{
@@ -75,7 +77,6 @@ namespace Emby.Server.Implementations.Library
{
return Guid.Empty;
}
-
}).Where(i => !i.Equals(Guid.Empty)).ToArray();
return GetInstantMixFromGenreIds(genreIds, user, dtoOptions);
@@ -105,32 +106,27 @@ namespace Emby.Server.Implementations.Library
return GetInstantMixFromGenreIds(new[] { item.Id }, user, dtoOptions);
}
- var playlist = item as Playlist;
- if (playlist != null)
+ if (item is Playlist playlist)
{
return GetInstantMixFromPlaylist(playlist, user, dtoOptions);
}
- var album = item as MusicAlbum;
- if (album != null)
+ if (item is MusicAlbum album)
{
return GetInstantMixFromAlbum(album, user, dtoOptions);
}
- var artist = item as MusicArtist;
- if (artist != null)
+ if (item is MusicArtist artist)
{
return GetInstantMixFromArtist(artist, user, dtoOptions);
}
- var song = item as Audio;
- if (song != null)
+ if (item is Audio song)
{
return GetInstantMixFromSong(song, user, dtoOptions);
}
- var folder = item as Folder;
- if (folder != null)
+ if (item is Folder folder)
{
return GetInstantMixFromFolder(folder, user, dtoOptions);
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
index 6c9ba7c27..ebfe95d0a 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
@@ -109,7 +109,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
IEnumerable<FileSystemMetadata> list,
bool allowSubfolders,
IDirectoryService directoryService,
- ILogger logger,
+ ILogger<MusicAlbumResolver> logger,
IFileSystem fileSystem,
ILibraryManager libraryManager)
{
diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/BoxSetResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/BoxSetResolver.cs
index e4bc4a469..295e9e120 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Movies/BoxSetResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Movies/BoxSetResolver.cs
@@ -69,7 +69,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
if (!string.IsNullOrEmpty(id))
{
- item.SetProviderId(MetadataProviders.Tmdb, id);
+ item.SetProviderId(MetadataProvider.Tmdb, id);
}
}
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
index cb67c8aa7..baf0e3cf9 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
@@ -350,7 +350,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
if (!string.IsNullOrWhiteSpace(tmdbid))
{
- item.SetProviderId(MetadataProviders.Tmdb, tmdbid);
+ item.SetProviderId(MetadataProvider.Tmdb, tmdbid);
}
}
@@ -361,7 +361,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
if (!string.IsNullOrWhiteSpace(imdbid))
{
- item.SetProviderId(MetadataProviders.Imdb, imdbid);
+ item.SetProviderId(MetadataProvider.Imdb, imdbid);
}
}
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
index 18145b7f1..7f8800a64 100644
--- a/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
@@ -16,7 +16,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
private readonly IServerConfigurationManager _config;
private readonly ILibraryManager _libraryManager;
private readonly ILocalizationManager _localization;
- private readonly ILogger _logger;
+ private readonly ILogger<SeasonResolver> _logger;
/// <summary>
/// Initializes a new instance of the <see cref="SeasonResolver"/> class.
diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
index dd6bd8ee8..2f206f74c 100644
--- a/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
@@ -20,7 +20,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
public class SeriesResolver : FolderResolver<Series>
{
private readonly IFileSystem _fileSystem;
- private readonly ILogger _logger;
+ private readonly ILogger<SeriesResolver> _logger;
private readonly ILibraryManager _libraryManager;
/// <summary>
@@ -119,7 +119,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
IEnumerable<FileSystemMetadata> fileSystemChildren,
IDirectoryService directoryService,
IFileSystem fileSystem,
- ILogger logger,
+ ILogger<SeriesResolver> logger,
ILibraryManager libraryManager,
bool isTvContentType)
{
@@ -217,7 +217,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
if (!string.IsNullOrEmpty(id))
{
- item.SetProviderId(MetadataProviders.Tvdb, id);
+ item.SetProviderId(MetadataProvider.Tvdb, id);
}
}
}
diff --git a/Emby.Server.Implementations/Library/SearchEngine.cs b/Emby.Server.Implementations/Library/SearchEngine.cs
index 59a77607d..f37cb3e9d 100644
--- a/Emby.Server.Implementations/Library/SearchEngine.cs
+++ b/Emby.Server.Implementations/Library/SearchEngine.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
@@ -12,12 +13,14 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Search;
using Microsoft.Extensions.Logging;
+using Genre = MediaBrowser.Controller.Entities.Genre;
+using Person = MediaBrowser.Controller.Entities.Person;
namespace Emby.Server.Implementations.Library
{
public class SearchEngine : ISearchEngine
{
- private readonly ILogger _logger;
+ private readonly ILogger<SearchEngine> _logger;
private readonly ILibraryManager _libraryManager;
private readonly IUserManager _userManager;
diff --git a/Emby.Server.Implementations/Library/UserDataManager.cs b/Emby.Server.Implementations/Library/UserDataManager.cs
index a9772a078..9e17fa672 100644
--- a/Emby.Server.Implementations/Library/UserDataManager.cs
+++ b/Emby.Server.Implementations/Library/UserDataManager.cs
@@ -5,6 +5,7 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.Threading;
+using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
@@ -13,6 +14,7 @@ using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using Microsoft.Extensions.Logging;
+using Book = MediaBrowser.Controller.Entities.Book;
namespace Emby.Server.Implementations.Library
{
@@ -26,7 +28,7 @@ namespace Emby.Server.Implementations.Library
private readonly ConcurrentDictionary<string, UserItemData> _userData =
new ConcurrentDictionary<string, UserItemData>(StringComparer.OrdinalIgnoreCase);
- private readonly ILogger _logger;
+ private readonly ILogger<UserDataManager> _logger;
private readonly IServerConfigurationManager _config;
private readonly IUserManager _userManager;
private readonly IUserDataRepository _repository;
@@ -240,7 +242,7 @@ namespace Emby.Server.Implementations.Library
{
// Enforce MinResumeDuration
var durationSeconds = TimeSpan.FromTicks(runtimeTicks).TotalSeconds;
- if (durationSeconds < _config.Configuration.MinResumeDurationSeconds)
+ if (durationSeconds < _config.Configuration.MinResumeDurationSeconds && !(item is Book))
{
positionTicks = 0;
data.Played = playedToCompletion = true;
diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs
deleted file mode 100644
index b8feb5535..000000000
--- a/Emby.Server.Implementations/Library/UserManager.cs
+++ /dev/null
@@ -1,1132 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Text;
-using System.Text.RegularExpressions;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Common.Cryptography;
-using MediaBrowser.Common.Events;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Authentication;
-using MediaBrowser.Controller.Devices;
-using MediaBrowser.Controller.Drawing;
-using MediaBrowser.Controller.Dto;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Controller.Persistence;
-using MediaBrowser.Controller.Plugins;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Controller.Security;
-using MediaBrowser.Controller.Session;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Cryptography;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Serialization;
-using MediaBrowser.Model.Users;
-using Microsoft.Extensions.Logging;
-
-namespace Emby.Server.Implementations.Library
-{
- /// <summary>
- /// Class UserManager.
- /// </summary>
- public class UserManager : IUserManager
- {
- private readonly object _policySyncLock = new object();
- private readonly object _configSyncLock = new object();
-
- private readonly ILogger _logger;
- private readonly IUserRepository _userRepository;
- private readonly IXmlSerializer _xmlSerializer;
- private readonly IJsonSerializer _jsonSerializer;
- private readonly INetworkManager _networkManager;
- private readonly IImageProcessor _imageProcessor;
- private readonly Lazy<IDtoService> _dtoServiceFactory;
- private readonly IServerApplicationHost _appHost;
- private readonly IFileSystem _fileSystem;
- private readonly ICryptoProvider _cryptoProvider;
-
- private ConcurrentDictionary<Guid, User> _users;
-
- private IAuthenticationProvider[] _authenticationProviders;
- private DefaultAuthenticationProvider _defaultAuthenticationProvider;
-
- private InvalidAuthProvider _invalidAuthProvider;
-
- private IPasswordResetProvider[] _passwordResetProviders;
- private DefaultPasswordResetProvider _defaultPasswordResetProvider;
-
- private IDtoService DtoService => _dtoServiceFactory.Value;
-
- public UserManager(
- ILogger<UserManager> logger,
- IUserRepository userRepository,
- IXmlSerializer xmlSerializer,
- INetworkManager networkManager,
- IImageProcessor imageProcessor,
- Lazy<IDtoService> dtoServiceFactory,
- IServerApplicationHost appHost,
- IJsonSerializer jsonSerializer,
- IFileSystem fileSystem,
- ICryptoProvider cryptoProvider)
- {
- _logger = logger;
- _userRepository = userRepository;
- _xmlSerializer = xmlSerializer;
- _networkManager = networkManager;
- _imageProcessor = imageProcessor;
- _dtoServiceFactory = dtoServiceFactory;
- _appHost = appHost;
- _jsonSerializer = jsonSerializer;
- _fileSystem = fileSystem;
- _cryptoProvider = cryptoProvider;
- _users = null;
- }
-
- public event EventHandler<GenericEventArgs<User>> UserPasswordChanged;
-
- /// <summary>
- /// Occurs when [user updated].
- /// </summary>
- public event EventHandler<GenericEventArgs<User>> UserUpdated;
-
- public event EventHandler<GenericEventArgs<User>> UserPolicyUpdated;
-
- public event EventHandler<GenericEventArgs<User>> UserConfigurationUpdated;
-
- public event EventHandler<GenericEventArgs<User>> UserLockedOut;
-
- public event EventHandler<GenericEventArgs<User>> UserCreated;
-
- /// <summary>
- /// Occurs when [user deleted].
- /// </summary>
- public event EventHandler<GenericEventArgs<User>> UserDeleted;
-
- /// <inheritdoc />
- public IEnumerable<User> Users => _users.Values;
-
- /// <inheritdoc />
- public IEnumerable<Guid> UsersIds => _users.Keys;
-
- /// <summary>
- /// Called when [user updated].
- /// </summary>
- /// <param name="user">The user.</param>
- private void OnUserUpdated(User user)
- {
- UserUpdated?.Invoke(this, new GenericEventArgs<User> { Argument = user });
- }
-
- /// <summary>
- /// Called when [user deleted].
- /// </summary>
- /// <param name="user">The user.</param>
- private void OnUserDeleted(User user)
- {
- UserDeleted?.Invoke(this, new GenericEventArgs<User> { Argument = user });
- }
-
- public NameIdPair[] GetAuthenticationProviders()
- {
- return _authenticationProviders
- .Where(i => i.IsEnabled)
- .OrderBy(i => i is DefaultAuthenticationProvider ? 0 : 1)
- .ThenBy(i => i.Name)
- .Select(i => new NameIdPair
- {
- Name = i.Name,
- Id = GetAuthenticationProviderId(i)
- })
- .ToArray();
- }
-
- public NameIdPair[] GetPasswordResetProviders()
- {
- return _passwordResetProviders
- .Where(i => i.IsEnabled)
- .OrderBy(i => i is DefaultPasswordResetProvider ? 0 : 1)
- .ThenBy(i => i.Name)
- .Select(i => new NameIdPair
- {
- Name = i.Name,
- Id = GetPasswordResetProviderId(i)
- })
- .ToArray();
- }
-
- public void AddParts(IEnumerable<IAuthenticationProvider> authenticationProviders, IEnumerable<IPasswordResetProvider> passwordResetProviders)
- {
- _authenticationProviders = authenticationProviders.ToArray();
-
- _defaultAuthenticationProvider = _authenticationProviders.OfType<DefaultAuthenticationProvider>().First();
-
- _invalidAuthProvider = _authenticationProviders.OfType<InvalidAuthProvider>().First();
-
- _passwordResetProviders = passwordResetProviders.ToArray();
-
- _defaultPasswordResetProvider = passwordResetProviders.OfType<DefaultPasswordResetProvider>().First();
- }
-
- /// <inheritdoc />
- public User GetUserById(Guid id)
- {
- if (id == Guid.Empty)
- {
- throw new ArgumentException("Guid can't be empty", nameof(id));
- }
-
- _users.TryGetValue(id, out User user);
- return user;
- }
-
- public User GetUserByName(string name)
- {
- if (string.IsNullOrWhiteSpace(name))
- {
- throw new ArgumentException("Invalid username", nameof(name));
- }
-
- return Users.FirstOrDefault(u => string.Equals(u.Name, name, StringComparison.OrdinalIgnoreCase));
- }
-
- public void Initialize()
- {
- LoadUsers();
-
- var users = Users;
-
- // If there are no local users with admin rights, make them all admins
- if (!users.Any(i => i.Policy.IsAdministrator))
- {
- foreach (var user in users)
- {
- user.Policy.IsAdministrator = true;
- UpdateUserPolicy(user, user.Policy, false);
- }
- }
- }
-
- public static bool IsValidUsername(string username)
- {
- // This is some regex that matches only on unicode "word" characters, as well as -, _ and @
- // In theory this will cut out most if not all 'control' characters which should help minimize any weirdness
- // Usernames can contain letters (a-z + whatever else unicode is cool with), numbers (0-9), at-signs (@), dashes (-), underscores (_), apostrophes ('), and periods (.)
- return Regex.IsMatch(username, @"^[\w\-'._@]*$");
- }
-
- private static bool IsValidUsernameCharacter(char i)
- => IsValidUsername(i.ToString(CultureInfo.InvariantCulture));
-
- public string MakeValidUsername(string username)
- {
- if (IsValidUsername(username))
- {
- return username;
- }
-
- // Usernames can contain letters (a-z), numbers (0-9), dashes (-), underscores (_), apostrophes ('), and periods (.)
- var builder = new StringBuilder();
-
- foreach (var c in username)
- {
- if (IsValidUsernameCharacter(c))
- {
- builder.Append(c);
- }
- }
-
- return builder.ToString();
- }
-
- public async Task<User> AuthenticateUser(
- string username,
- string password,
- string hashedPassword,
- string remoteEndPoint,
- bool isUserSession)
- {
- if (string.IsNullOrWhiteSpace(username))
- {
- _logger.LogInformation("Authentication request without username has been denied (IP: {IP}).", remoteEndPoint);
- throw new ArgumentNullException(nameof(username));
- }
-
- var user = Users.FirstOrDefault(i => string.Equals(username, i.Name, StringComparison.OrdinalIgnoreCase));
-
- var success = false;
- IAuthenticationProvider authenticationProvider = null;
-
- if (user != null)
- {
- var authResult = await AuthenticateLocalUser(username, password, hashedPassword, user, remoteEndPoint).ConfigureAwait(false);
- authenticationProvider = authResult.authenticationProvider;
- success = authResult.success;
- }
- else
- {
- // user is null
- var authResult = await AuthenticateLocalUser(username, password, hashedPassword, null, remoteEndPoint).ConfigureAwait(false);
- authenticationProvider = authResult.authenticationProvider;
- string updatedUsername = authResult.username;
- success = authResult.success;
-
- if (success
- && authenticationProvider != null
- && !(authenticationProvider is DefaultAuthenticationProvider))
- {
- // Trust the username returned by the authentication provider
- username = updatedUsername;
-
- // Search the database for the user again
- // the authentication provider might have created it
- user = Users
- .FirstOrDefault(i => string.Equals(username, i.Name, StringComparison.OrdinalIgnoreCase));
-
- if (authenticationProvider is IHasNewUserPolicy hasNewUserPolicy)
- {
- var policy = hasNewUserPolicy.GetNewUserPolicy();
- UpdateUserPolicy(user, policy, true);
- }
- }
- }
-
- if (success && user != null && authenticationProvider != null)
- {
- var providerId = GetAuthenticationProviderId(authenticationProvider);
-
- if (!string.Equals(providerId, user.Policy.AuthenticationProviderId, StringComparison.OrdinalIgnoreCase))
- {
- user.Policy.AuthenticationProviderId = providerId;
- UpdateUserPolicy(user, user.Policy, true);
- }
- }
-
- if (user == null)
- {
- _logger.LogInformation("Authentication request for {UserName} has been denied (IP: {IP}).", username, remoteEndPoint);
- throw new AuthenticationException("Invalid username or password entered.");
- }
-
- if (user.Policy.IsDisabled)
- {
- _logger.LogInformation("Authentication request for {UserName} has been denied because this account is currently disabled (IP: {IP}).", username, remoteEndPoint);
- throw new SecurityException($"The {user.Name} account is currently disabled. Please consult with your administrator.");
- }
-
- if (!user.Policy.EnableRemoteAccess && !_networkManager.IsInLocalNetwork(remoteEndPoint))
- {
- _logger.LogInformation("Authentication request for {UserName} forbidden: remote access disabled and user not in local network (IP: {IP}).", username, remoteEndPoint);
- throw new SecurityException("Forbidden.");
- }
-
- if (!user.IsParentalScheduleAllowed())
- {
- _logger.LogInformation("Authentication request for {UserName} is not allowed at this time due parental restrictions (IP: {IP}).", username, remoteEndPoint);
- throw new SecurityException("User is not allowed access at this time.");
- }
-
- // Update LastActivityDate and LastLoginDate, then save
- if (success)
- {
- if (isUserSession)
- {
- user.LastActivityDate = user.LastLoginDate = DateTime.UtcNow;
- UpdateUser(user);
- }
-
- ResetInvalidLoginAttemptCount(user);
- _logger.LogInformation("Authentication request for {UserName} has succeeded.", user.Name);
- }
- else
- {
- IncrementInvalidLoginAttemptCount(user);
- _logger.LogInformation("Authentication request for {UserName} has been denied (IP: {IP}).", user.Name, remoteEndPoint);
- }
-
- return success ? user : null;
- }
-
-#nullable enable
-
- private static string GetAuthenticationProviderId(IAuthenticationProvider provider)
- {
- return provider.GetType().FullName;
- }
-
- private static string GetPasswordResetProviderId(IPasswordResetProvider provider)
- {
- return provider.GetType().FullName;
- }
-
- private IAuthenticationProvider GetAuthenticationProvider(User user)
- {
- return GetAuthenticationProviders(user)[0];
- }
-
- private IPasswordResetProvider GetPasswordResetProvider(User user)
- {
- return GetPasswordResetProviders(user)[0];
- }
-
- private IAuthenticationProvider[] GetAuthenticationProviders(User? user)
- {
- var authenticationProviderId = user?.Policy.AuthenticationProviderId;
-
- var providers = _authenticationProviders.Where(i => i.IsEnabled).ToArray();
-
- if (!string.IsNullOrEmpty(authenticationProviderId))
- {
- providers = providers.Where(i => string.Equals(authenticationProviderId, GetAuthenticationProviderId(i), StringComparison.OrdinalIgnoreCase)).ToArray();
- }
-
- if (providers.Length == 0)
- {
- // Assign the user to the InvalidAuthProvider since no configured auth provider was valid/found
- _logger.LogWarning("User {UserName} was found with invalid/missing Authentication Provider {AuthenticationProviderId}. Assigning user to InvalidAuthProvider until this is corrected", user?.Name, user?.Policy.AuthenticationProviderId);
- providers = new IAuthenticationProvider[] { _invalidAuthProvider };
- }
-
- return providers;
- }
-
- private IPasswordResetProvider[] GetPasswordResetProviders(User? user)
- {
- var passwordResetProviderId = user?.Policy.PasswordResetProviderId;
-
- var providers = _passwordResetProviders.Where(i => i.IsEnabled).ToArray();
-
- if (!string.IsNullOrEmpty(passwordResetProviderId))
- {
- providers = providers.Where(i => string.Equals(passwordResetProviderId, GetPasswordResetProviderId(i), StringComparison.OrdinalIgnoreCase)).ToArray();
- }
-
- if (providers.Length == 0)
- {
- providers = new IPasswordResetProvider[] { _defaultPasswordResetProvider };
- }
-
- return providers;
- }
-
- private async Task<(string username, bool success)> AuthenticateWithProvider(
- IAuthenticationProvider provider,
- string username,
- string password,
- User? resolvedUser)
- {
- try
- {
- var authenticationResult = provider is IRequiresResolvedUser requiresResolvedUser
- ? await requiresResolvedUser.Authenticate(username, password, resolvedUser).ConfigureAwait(false)
- : await provider.Authenticate(username, password).ConfigureAwait(false);
-
- if (authenticationResult.Username != username)
- {
- _logger.LogDebug("Authentication provider provided updated username {1}", authenticationResult.Username);
- username = authenticationResult.Username;
- }
-
- return (username, true);
- }
- catch (AuthenticationException ex)
- {
- _logger.LogError(ex, "Error authenticating with provider {Provider}", provider.Name);
-
- return (username, false);
- }
- }
-
- private async Task<(IAuthenticationProvider? authenticationProvider, string username, bool success)> AuthenticateLocalUser(
- string username,
- string password,
- string hashedPassword,
- User? user,
- string remoteEndPoint)
- {
- bool success = false;
- IAuthenticationProvider? authenticationProvider = null;
-
- foreach (var provider in GetAuthenticationProviders(user))
- {
- var providerAuthResult = await AuthenticateWithProvider(provider, username, password, user).ConfigureAwait(false);
- var updatedUsername = providerAuthResult.username;
- success = providerAuthResult.success;
-
- if (success)
- {
- authenticationProvider = provider;
- username = updatedUsername;
- break;
- }
- }
-
- if (!success
- && _networkManager.IsInLocalNetwork(remoteEndPoint)
- && user?.Configuration.EnableLocalPassword == true
- && !string.IsNullOrEmpty(user.EasyPassword))
- {
- // Check easy password
- var passwordHash = PasswordHash.Parse(user.EasyPassword);
- var hash = _cryptoProvider.ComputeHash(
- passwordHash.Id,
- Encoding.UTF8.GetBytes(password),
- passwordHash.Salt.ToArray());
- success = passwordHash.Hash.SequenceEqual(hash);
- }
-
- return (authenticationProvider, username, success);
- }
-
- private void ResetInvalidLoginAttemptCount(User user)
- {
- user.Policy.InvalidLoginAttemptCount = 0;
- UpdateUserPolicy(user, user.Policy, false);
- }
-
- private void IncrementInvalidLoginAttemptCount(User user)
- {
- int invalidLogins = ++user.Policy.InvalidLoginAttemptCount;
- int maxInvalidLogins = user.Policy.LoginAttemptsBeforeLockout;
- if (maxInvalidLogins > 0
- && invalidLogins >= maxInvalidLogins)
- {
- user.Policy.IsDisabled = true;
- UserLockedOut?.Invoke(this, new GenericEventArgs<User>(user));
- _logger.LogWarning(
- "Disabling user {UserName} due to {Attempts} unsuccessful login attempts.",
- user.Name,
- invalidLogins);
- }
-
- UpdateUserPolicy(user, user.Policy, false);
- }
-
- /// <summary>
- /// Loads the users from the repository.
- /// </summary>
- private void LoadUsers()
- {
- var users = _userRepository.RetrieveAllUsers();
-
- // There always has to be at least one user.
- if (users.Count != 0)
- {
- _users = new ConcurrentDictionary<Guid, User>(
- users.Select(x => new KeyValuePair<Guid, User>(x.Id, x)));
- return;
- }
-
- var defaultName = Environment.UserName;
- if (string.IsNullOrWhiteSpace(defaultName))
- {
- defaultName = "MyJellyfinUser";
- }
-
- _logger.LogWarning("No users, creating one with username {UserName}", defaultName);
-
- var name = MakeValidUsername(defaultName);
-
- var user = InstantiateNewUser(name);
-
- user.DateLastSaved = DateTime.UtcNow;
-
- _userRepository.CreateUser(user);
-
- user.Policy.IsAdministrator = true;
- user.Policy.EnableContentDeletion = true;
- user.Policy.EnableRemoteControlOfOtherUsers = true;
- UpdateUserPolicy(user, user.Policy, false);
-
- _users = new ConcurrentDictionary<Guid, User>();
- _users[user.Id] = user;
- }
-
-#nullable restore
-
- public UserDto GetUserDto(User user, string remoteEndPoint = null)
- {
- if (user == null)
- {
- throw new ArgumentNullException(nameof(user));
- }
-
- bool hasConfiguredPassword = GetAuthenticationProvider(user).HasPassword(user);
- bool hasConfiguredEasyPassword = !string.IsNullOrEmpty(GetAuthenticationProvider(user).GetEasyPasswordHash(user));
-
- bool hasPassword = user.Configuration.EnableLocalPassword && !string.IsNullOrEmpty(remoteEndPoint) && _networkManager.IsInLocalNetwork(remoteEndPoint) ?
- hasConfiguredEasyPassword :
- hasConfiguredPassword;
-
- UserDto dto = new UserDto
- {
- Id = user.Id,
- Name = user.Name,
- HasPassword = hasPassword,
- HasConfiguredPassword = hasConfiguredPassword,
- HasConfiguredEasyPassword = hasConfiguredEasyPassword,
- LastActivityDate = user.LastActivityDate,
- LastLoginDate = user.LastLoginDate,
- Configuration = user.Configuration,
- ServerId = _appHost.SystemId,
- Policy = user.Policy
- };
-
- if (!hasPassword && _users.Count == 1)
- {
- dto.EnableAutoLogin = true;
- }
-
- ItemImageInfo image = user.GetImageInfo(ImageType.Primary, 0);
-
- if (image != null)
- {
- dto.PrimaryImageTag = GetImageCacheTag(user, image);
-
- try
- {
- DtoService.AttachPrimaryImageAspectRatio(dto, user);
- }
- catch (Exception ex)
- {
- // Have to use a catch-all unfortunately because some .net image methods throw plain Exceptions
- _logger.LogError(ex, "Error generating PrimaryImageAspectRatio for {User}", user.Name);
- }
- }
-
- return dto;
- }
-
- public PublicUserDto GetPublicUserDto(User user, string remoteEndPoint = null)
- {
- if (user == null)
- {
- throw new ArgumentNullException(nameof(user));
- }
-
- IAuthenticationProvider authenticationProvider = GetAuthenticationProvider(user);
- bool hasConfiguredPassword = authenticationProvider.HasPassword(user);
- bool hasConfiguredEasyPassword = !string.IsNullOrEmpty(authenticationProvider.GetEasyPasswordHash(user));
-
- bool hasPassword = user.Configuration.EnableLocalPassword &&
- !string.IsNullOrEmpty(remoteEndPoint) &&
- _networkManager.IsInLocalNetwork(remoteEndPoint) ? hasConfiguredEasyPassword : hasConfiguredPassword;
-
- PublicUserDto dto = new PublicUserDto
- {
- Name = user.Name,
- HasPassword = hasPassword,
- HasConfiguredPassword = hasConfiguredPassword,
- };
-
- return dto;
- }
-
- public UserDto GetOfflineUserDto(User user)
- {
- var dto = GetUserDto(user);
-
- dto.ServerName = _appHost.FriendlyName;
-
- return dto;
- }
-
- private string GetImageCacheTag(BaseItem item, ItemImageInfo image)
- {
- try
- {
- return _imageProcessor.GetImageCacheTag(item, image);
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error getting {ImageType} image info for {ImagePath}", image.Type, image.Path);
- return null;
- }
- }
-
- /// <summary>
- /// Refreshes metadata for each user
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- public async Task RefreshUsersMetadata(CancellationToken cancellationToken)
- {
- foreach (var user in Users)
- {
- await user.RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(_fileSystem)), cancellationToken).ConfigureAwait(false);
- }
- }
-
- /// <summary>
- /// Renames the user.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <param name="newName">The new name.</param>
- /// <returns>Task.</returns>
- /// <exception cref="ArgumentNullException">user</exception>
- /// <exception cref="ArgumentException"></exception>
- public async Task RenameUser(User user, string newName)
- {
- if (user == null)
- {
- throw new ArgumentNullException(nameof(user));
- }
-
- if (string.IsNullOrWhiteSpace(newName))
- {
- throw new ArgumentException("Invalid username", nameof(newName));
- }
-
- if (user.Name.Equals(newName, StringComparison.Ordinal))
- {
- throw new ArgumentException("The new and old names must be different.");
- }
-
- if (Users.Any(
- u => u.Id != user.Id && u.Name.Equals(newName, StringComparison.OrdinalIgnoreCase)))
- {
- throw new ArgumentException(string.Format(
- CultureInfo.InvariantCulture,
- "A user with the name '{0}' already exists.",
- newName));
- }
-
- await user.Rename(newName).ConfigureAwait(false);
-
- OnUserUpdated(user);
- }
-
- /// <summary>
- /// Updates the user.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <exception cref="ArgumentNullException">user</exception>
- /// <exception cref="ArgumentException"></exception>
- public void UpdateUser(User user)
- {
- if (user == null)
- {
- throw new ArgumentNullException(nameof(user));
- }
-
- if (user.Id == Guid.Empty)
- {
- throw new ArgumentException("Id can't be empty.", nameof(user));
- }
-
- if (!_users.ContainsKey(user.Id))
- {
- throw new ArgumentException(
- string.Format(
- CultureInfo.InvariantCulture,
- "A user '{0}' with Id {1} does not exist.",
- user.Name,
- user.Id),
- nameof(user));
- }
-
- user.DateModified = DateTime.UtcNow;
- user.DateLastSaved = DateTime.UtcNow;
-
- _userRepository.UpdateUser(user);
-
- OnUserUpdated(user);
- }
-
- /// <summary>
- /// Creates the user.
- /// </summary>
- /// <param name="name">The name.</param>
- /// <returns>User.</returns>
- /// <exception cref="ArgumentNullException">name</exception>
- /// <exception cref="ArgumentException"></exception>
- public User CreateUser(string name)
- {
- if (string.IsNullOrWhiteSpace(name))
- {
- throw new ArgumentNullException(nameof(name));
- }
-
- if (!IsValidUsername(name))
- {
- throw new ArgumentException("Usernames can contain unicode symbols, numbers (0-9), dashes (-), underscores (_), apostrophes ('), and periods (.)");
- }
-
- if (Users.Any(u => u.Name.Equals(name, StringComparison.OrdinalIgnoreCase)))
- {
- throw new ArgumentException(string.Format("A user with the name '{0}' already exists.", name));
- }
-
- var user = InstantiateNewUser(name);
-
- _users[user.Id] = user;
-
- user.DateLastSaved = DateTime.UtcNow;
-
- _userRepository.CreateUser(user);
-
- EventHelper.QueueEventIfNotNull(UserCreated, this, new GenericEventArgs<User> { Argument = user }, _logger);
-
- return user;
- }
-
- /// <inheritdoc />
- /// <exception cref="ArgumentNullException">The <c>user</c> is <c>null</c>.</exception>
- /// <exception cref="ArgumentException">The <c>user</c> doesn't exist, or is the last administrator.</exception>
- /// <exception cref="InvalidOperationException">The <c>user</c> can't be deleted; there are no other users.</exception>
- public void DeleteUser(User user)
- {
- if (user == null)
- {
- throw new ArgumentNullException(nameof(user));
- }
-
- if (!_users.ContainsKey(user.Id))
- {
- throw new ArgumentException(string.Format(
- CultureInfo.InvariantCulture,
- "The user cannot be deleted because there is no user with the Name {0} and Id {1}.",
- user.Name,
- user.Id));
- }
-
- if (_users.Count == 1)
- {
- throw new InvalidOperationException(string.Format(
- CultureInfo.InvariantCulture,
- "The user '{0}' cannot be deleted because there must be at least one user in the system.",
- user.Name));
- }
-
- if (user.Policy.IsAdministrator
- && Users.Count(i => i.Policy.IsAdministrator) == 1)
- {
- throw new ArgumentException(
- string.Format(
- CultureInfo.InvariantCulture,
- "The user '{0}' cannot be deleted because there must be at least one admin user in the system.",
- user.Name),
- nameof(user));
- }
-
- var configPath = GetConfigurationFilePath(user);
-
- _userRepository.DeleteUser(user);
-
- // Delete user config dir
- lock (_configSyncLock)
- lock (_policySyncLock)
- {
- try
- {
- Directory.Delete(user.ConfigurationDirectoryPath, true);
- }
- catch (IOException ex)
- {
- _logger.LogError(ex, "Error deleting user config dir: {Path}", user.ConfigurationDirectoryPath);
- }
- }
-
- _users.TryRemove(user.Id, out _);
-
- OnUserDeleted(user);
- }
-
- /// <summary>
- /// Resets the password by clearing it.
- /// </summary>
- /// <returns>Task.</returns>
- public Task ResetPassword(User user)
- {
- return ChangePassword(user, string.Empty);
- }
-
- public void ResetEasyPassword(User user)
- {
- ChangeEasyPassword(user, string.Empty, null);
- }
-
- public async Task ChangePassword(User user, string newPassword)
- {
- if (user == null)
- {
- throw new ArgumentNullException(nameof(user));
- }
-
- await GetAuthenticationProvider(user).ChangePassword(user, newPassword).ConfigureAwait(false);
-
- UpdateUser(user);
-
- UserPasswordChanged?.Invoke(this, new GenericEventArgs<User>(user));
- }
-
- public void ChangeEasyPassword(User user, string newPassword, string newPasswordHash)
- {
- if (user == null)
- {
- throw new ArgumentNullException(nameof(user));
- }
-
- GetAuthenticationProvider(user).ChangeEasyPassword(user, newPassword, newPasswordHash);
-
- UpdateUser(user);
-
- UserPasswordChanged?.Invoke(this, new GenericEventArgs<User>(user));
- }
-
- /// <summary>
- /// Instantiates the new user.
- /// </summary>
- /// <param name="name">The name.</param>
- /// <returns>User.</returns>
- private static User InstantiateNewUser(string name)
- {
- return new User
- {
- Name = name,
- Id = Guid.NewGuid(),
- DateCreated = DateTime.UtcNow,
- DateModified = DateTime.UtcNow
- };
- }
-
- public async Task<ForgotPasswordResult> StartForgotPasswordProcess(string enteredUsername, bool isInNetwork)
- {
- var user = string.IsNullOrWhiteSpace(enteredUsername) ?
- null :
- GetUserByName(enteredUsername);
-
- var action = ForgotPasswordAction.InNetworkRequired;
-
- if (user != null && isInNetwork)
- {
- var passwordResetProvider = GetPasswordResetProvider(user);
- return await passwordResetProvider.StartForgotPasswordProcess(user, isInNetwork).ConfigureAwait(false);
- }
- else
- {
- return new ForgotPasswordResult
- {
- Action = action,
- PinFile = string.Empty
- };
- }
- }
-
- public async Task<PinRedeemResult> RedeemPasswordResetPin(string pin)
- {
- foreach (var provider in _passwordResetProviders)
- {
- var result = await provider.RedeemPasswordResetPin(pin).ConfigureAwait(false);
- if (result.Success)
- {
- return result;
- }
- }
-
- return new PinRedeemResult
- {
- Success = false,
- UsersReset = Array.Empty<string>()
- };
- }
-
- public UserPolicy GetUserPolicy(User user)
- {
- var path = GetPolicyFilePath(user);
- if (!File.Exists(path))
- {
- return GetDefaultPolicy();
- }
-
- try
- {
- lock (_policySyncLock)
- {
- return (UserPolicy)_xmlSerializer.DeserializeFromFile(typeof(UserPolicy), path);
- }
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error reading policy file: {Path}", path);
-
- return GetDefaultPolicy();
- }
- }
-
- private static UserPolicy GetDefaultPolicy()
- {
- return new UserPolicy
- {
- EnableContentDownloading = true,
- EnableSyncTranscoding = true
- };
- }
-
- public void UpdateUserPolicy(Guid userId, UserPolicy userPolicy)
- {
- var user = GetUserById(userId);
- UpdateUserPolicy(user, userPolicy, true);
- }
-
- private void UpdateUserPolicy(User user, UserPolicy userPolicy, bool fireEvent)
- {
- // The xml serializer will output differently if the type is not exact
- if (userPolicy.GetType() != typeof(UserPolicy))
- {
- var json = _jsonSerializer.SerializeToString(userPolicy);
- userPolicy = _jsonSerializer.DeserializeFromString<UserPolicy>(json);
- }
-
- var path = GetPolicyFilePath(user);
-
- Directory.CreateDirectory(Path.GetDirectoryName(path));
-
- lock (_policySyncLock)
- {
- _xmlSerializer.SerializeToFile(userPolicy, path);
- user.Policy = userPolicy;
- }
-
- if (fireEvent)
- {
- UserPolicyUpdated?.Invoke(this, new GenericEventArgs<User> { Argument = user });
- }
- }
-
- private static string GetPolicyFilePath(User user)
- {
- return Path.Combine(user.ConfigurationDirectoryPath, "policy.xml");
- }
-
- private static string GetConfigurationFilePath(User user)
- {
- return Path.Combine(user.ConfigurationDirectoryPath, "config.xml");
- }
-
- public UserConfiguration GetUserConfiguration(User user)
- {
- var path = GetConfigurationFilePath(user);
-
- if (!File.Exists(path))
- {
- return new UserConfiguration();
- }
-
- try
- {
- lock (_configSyncLock)
- {
- return (UserConfiguration)_xmlSerializer.DeserializeFromFile(typeof(UserConfiguration), path);
- }
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error reading policy file: {Path}", path);
-
- return new UserConfiguration();
- }
- }
-
- public void UpdateConfiguration(Guid userId, UserConfiguration config)
- {
- var user = GetUserById(userId);
- UpdateConfiguration(user, config);
- }
-
- public void UpdateConfiguration(User user, UserConfiguration config)
- {
- UpdateConfiguration(user, config, true);
- }
-
- private void UpdateConfiguration(User user, UserConfiguration config, bool fireEvent)
- {
- var path = GetConfigurationFilePath(user);
-
- // The xml serializer will output differently if the type is not exact
- if (config.GetType() != typeof(UserConfiguration))
- {
- var json = _jsonSerializer.SerializeToString(config);
- config = _jsonSerializer.DeserializeFromString<UserConfiguration>(json);
- }
-
- Directory.CreateDirectory(Path.GetDirectoryName(path));
-
- lock (_configSyncLock)
- {
- _xmlSerializer.SerializeToFile(config, path);
- user.Configuration = config;
- }
-
- if (fireEvent)
- {
- UserConfigurationUpdated?.Invoke(this, new GenericEventArgs<User> { Argument = user });
- }
- }
- }
-
- public class DeviceAccessEntryPoint : IServerEntryPoint
- {
- private IUserManager _userManager;
- private IAuthenticationRepository _authRepo;
- private IDeviceManager _deviceManager;
- private ISessionManager _sessionManager;
-
- public DeviceAccessEntryPoint(IUserManager userManager, IAuthenticationRepository authRepo, IDeviceManager deviceManager, ISessionManager sessionManager)
- {
- _userManager = userManager;
- _authRepo = authRepo;
- _deviceManager = deviceManager;
- _sessionManager = sessionManager;
- }
-
- public Task RunAsync()
- {
- _userManager.UserPolicyUpdated += _userManager_UserPolicyUpdated;
-
- return Task.CompletedTask;
- }
-
- private void _userManager_UserPolicyUpdated(object sender, GenericEventArgs<User> e)
- {
- var user = e.Argument;
- if (!user.Policy.EnableAllDevices)
- {
- UpdateDeviceAccess(user);
- }
- }
-
- private void UpdateDeviceAccess(User user)
- {
- var existing = _authRepo.Get(new AuthenticationInfoQuery
- {
- UserId = user.Id
-
- }).Items;
-
- foreach (var authInfo in existing)
- {
- if (!string.IsNullOrEmpty(authInfo.DeviceId) && !_deviceManager.CanAccessDevice(user, authInfo.DeviceId))
- {
- _sessionManager.Logout(authInfo);
- }
- }
- }
-
- public void Dispose()
- {
-
- }
- }
-}
diff --git a/Emby.Server.Implementations/Library/UserViewManager.cs b/Emby.Server.Implementations/Library/UserViewManager.cs
index 322819b05..f51657c63 100644
--- a/Emby.Server.Implementations/Library/UserViewManager.cs
+++ b/Emby.Server.Implementations/Library/UserViewManager.cs
@@ -5,6 +5,8 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;
@@ -17,6 +19,8 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Library;
using MediaBrowser.Model.Querying;
+using Genre = MediaBrowser.Controller.Entities.Genre;
+using Person = MediaBrowser.Controller.Entities.Person;
namespace Emby.Server.Implementations.Library
{
@@ -125,12 +129,12 @@ namespace Emby.Server.Implementations.Library
if (!query.IncludeHidden)
{
- list = list.Where(i => !user.Configuration.MyMediaExcludes.Contains(i.Id.ToString("N", CultureInfo.InvariantCulture))).ToList();
+ list = list.Where(i => !user.GetPreference(PreferenceKind.MyMediaExcludes).Contains(i.Id.ToString("N", CultureInfo.InvariantCulture))).ToList();
}
var sorted = _libraryManager.Sort(list, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending).ToList();
- var orders = user.Configuration.OrderedViews.ToList();
+ var orders = user.GetPreference(PreferenceKind.OrderedViews).ToList();
return list
.OrderBy(i =>
@@ -165,7 +169,13 @@ namespace Emby.Server.Implementations.Library
return GetUserSubViewWithName(name, parentId, type, sortName);
}
- private Folder GetUserView(List<ICollectionFolder> parents, string viewType, string localizationKey, string sortName, User user, string[] presetViews)
+ private Folder GetUserView(
+ List<ICollectionFolder> parents,
+ string viewType,
+ string localizationKey,
+ string sortName,
+ Jellyfin.Data.Entities.User user,
+ string[] presetViews)
{
if (parents.Count == 1 && parents.All(i => string.Equals(i.CollectionType, viewType, StringComparison.OrdinalIgnoreCase)))
{
@@ -270,7 +280,8 @@ namespace Emby.Server.Implementations.Library
{
parents = _libraryManager.GetUserRootFolder().GetChildren(user, true)
.Where(i => i is Folder)
- .Where(i => !user.Configuration.LatestItemsExcludes.Contains(i.Id.ToString("N", CultureInfo.InvariantCulture)))
+ .Where(i => !user.GetPreference(PreferenceKind.LatestItemExcludes)
+ .Contains(i.Id.ToString("N", CultureInfo.InvariantCulture)))
.ToList();
}
@@ -331,12 +342,11 @@ namespace Emby.Server.Implementations.Library
var excludeItemTypes = includeItemTypes.Length == 0 && mediaTypes.Count == 0 ? new[]
{
- typeof(Person).Name,
- typeof(Studio).Name,
- typeof(Year).Name,
- typeof(MusicGenre).Name,
- typeof(Genre).Name
-
+ nameof(Person),
+ nameof(Studio),
+ nameof(Year),
+ nameof(MusicGenre),
+ nameof(Genre)
} : Array.Empty<string>();
var query = new InternalItemsQuery(user)
diff --git a/Emby.Server.Implementations/Library/Validators/ArtistsPostScanTask.cs b/Emby.Server.Implementations/Library/Validators/ArtistsPostScanTask.cs
index 2af8ff5cb..d51f9aaa7 100644
--- a/Emby.Server.Implementations/Library/Validators/ArtistsPostScanTask.cs
+++ b/Emby.Server.Implementations/Library/Validators/ArtistsPostScanTask.cs
@@ -16,7 +16,7 @@ namespace Emby.Server.Implementations.Library.Validators
/// The _library manager.
/// </summary>
private readonly ILibraryManager _libraryManager;
- private readonly ILogger _logger;
+ private readonly ILogger<ArtistsValidator> _logger;
private readonly IItemRepository _itemRepo;
/// <summary>
@@ -27,7 +27,7 @@ namespace Emby.Server.Implementations.Library.Validators
/// <param name="itemRepo">The item repository.</param>
public ArtistsPostScanTask(
ILibraryManager libraryManager,
- ILogger<ArtistsPostScanTask> logger,
+ ILogger<ArtistsValidator> logger,
IItemRepository itemRepo)
{
_libraryManager = libraryManager;
diff --git a/Emby.Server.Implementations/Library/Validators/ArtistsValidator.cs b/Emby.Server.Implementations/Library/Validators/ArtistsValidator.cs
index 1497f4a3a..8a6bd5e78 100644
--- a/Emby.Server.Implementations/Library/Validators/ArtistsValidator.cs
+++ b/Emby.Server.Implementations/Library/Validators/ArtistsValidator.cs
@@ -24,7 +24,7 @@ namespace Emby.Server.Implementations.Library.Validators
/// <summary>
/// The logger.
/// </summary>
- private readonly ILogger _logger;
+ private readonly ILogger<ArtistsValidator> _logger;
private readonly IItemRepository _itemRepo;
/// <summary>
@@ -33,7 +33,7 @@ namespace Emby.Server.Implementations.Library.Validators
/// <param name="libraryManager">The library manager.</param>
/// <param name="logger">The logger.</param>
/// <param name="itemRepo">The item repository.</param>
- public ArtistsValidator(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo)
+ public ArtistsValidator(ILibraryManager libraryManager, ILogger<ArtistsValidator> logger, IItemRepository itemRepo)
{
_libraryManager = libraryManager;
_logger = logger;
diff --git a/Emby.Server.Implementations/Library/Validators/GenresPostScanTask.cs b/Emby.Server.Implementations/Library/Validators/GenresPostScanTask.cs
index 251785dfd..d21d2887b 100644
--- a/Emby.Server.Implementations/Library/Validators/GenresPostScanTask.cs
+++ b/Emby.Server.Implementations/Library/Validators/GenresPostScanTask.cs
@@ -16,7 +16,7 @@ namespace Emby.Server.Implementations.Library.Validators
/// The _library manager.
/// </summary>
private readonly ILibraryManager _libraryManager;
- private readonly ILogger _logger;
+ private readonly ILogger<GenresValidator> _logger;
private readonly IItemRepository _itemRepo;
/// <summary>
@@ -27,7 +27,7 @@ namespace Emby.Server.Implementations.Library.Validators
/// <param name="itemRepo">The item repository.</param>
public GenresPostScanTask(
ILibraryManager libraryManager,
- ILogger<GenresPostScanTask> logger,
+ ILogger<GenresValidator> logger,
IItemRepository itemRepo)
{
_libraryManager = libraryManager;
diff --git a/Emby.Server.Implementations/Library/Validators/GenresValidator.cs b/Emby.Server.Implementations/Library/Validators/GenresValidator.cs
index b0cd5f87a..e59c62e23 100644
--- a/Emby.Server.Implementations/Library/Validators/GenresValidator.cs
+++ b/Emby.Server.Implementations/Library/Validators/GenresValidator.cs
@@ -21,7 +21,7 @@ namespace Emby.Server.Implementations.Library.Validators
/// <summary>
/// The logger.
/// </summary>
- private readonly ILogger _logger;
+ private readonly ILogger<GenresValidator> _logger;
/// <summary>
/// Initializes a new instance of the <see cref="GenresValidator"/> class.
@@ -29,7 +29,7 @@ namespace Emby.Server.Implementations.Library.Validators
/// <param name="libraryManager">The library manager.</param>
/// <param name="logger">The logger.</param>
/// <param name="itemRepo">The item repository.</param>
- public GenresValidator(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo)
+ public GenresValidator(ILibraryManager libraryManager, ILogger<GenresValidator> logger, IItemRepository itemRepo)
{
_libraryManager = libraryManager;
_logger = logger;
diff --git a/Emby.Server.Implementations/Library/Validators/MusicGenresPostScanTask.cs b/Emby.Server.Implementations/Library/Validators/MusicGenresPostScanTask.cs
index 9d8690116..be119866b 100644
--- a/Emby.Server.Implementations/Library/Validators/MusicGenresPostScanTask.cs
+++ b/Emby.Server.Implementations/Library/Validators/MusicGenresPostScanTask.cs
@@ -16,7 +16,7 @@ namespace Emby.Server.Implementations.Library.Validators
/// The library manager.
/// </summary>
private readonly ILibraryManager _libraryManager;
- private readonly ILogger _logger;
+ private readonly ILogger<MusicGenresValidator> _logger;
private readonly IItemRepository _itemRepo;
/// <summary>
@@ -27,7 +27,7 @@ namespace Emby.Server.Implementations.Library.Validators
/// <param name="itemRepo">The item repository.</param>
public MusicGenresPostScanTask(
ILibraryManager libraryManager,
- ILogger<MusicGenresPostScanTask> logger,
+ ILogger<MusicGenresValidator> logger,
IItemRepository itemRepo)
{
_libraryManager = libraryManager;
diff --git a/Emby.Server.Implementations/Library/Validators/MusicGenresValidator.cs b/Emby.Server.Implementations/Library/Validators/MusicGenresValidator.cs
index 5ee4ca72e..1ecf4c87c 100644
--- a/Emby.Server.Implementations/Library/Validators/MusicGenresValidator.cs
+++ b/Emby.Server.Implementations/Library/Validators/MusicGenresValidator.cs
@@ -20,7 +20,7 @@ namespace Emby.Server.Implementations.Library.Validators
/// <summary>
/// The logger.
/// </summary>
- private readonly ILogger _logger;
+ private readonly ILogger<MusicGenresValidator> _logger;
private readonly IItemRepository _itemRepo;
/// <summary>
@@ -29,7 +29,7 @@ namespace Emby.Server.Implementations.Library.Validators
/// <param name="libraryManager">The library manager.</param>
/// <param name="logger">The logger.</param>
/// <param name="itemRepo">The item repository.</param>
- public MusicGenresValidator(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo)
+ public MusicGenresValidator(ILibraryManager libraryManager, ILogger<MusicGenresValidator> logger, IItemRepository itemRepo)
{
_libraryManager = libraryManager;
_logger = logger;
diff --git a/Emby.Server.Implementations/Library/Validators/StudiosPostScanTask.cs b/Emby.Server.Implementations/Library/Validators/StudiosPostScanTask.cs
index 2f8f906b9..c682b156b 100644
--- a/Emby.Server.Implementations/Library/Validators/StudiosPostScanTask.cs
+++ b/Emby.Server.Implementations/Library/Validators/StudiosPostScanTask.cs
@@ -17,7 +17,7 @@ namespace Emby.Server.Implementations.Library.Validators
/// </summary>
private readonly ILibraryManager _libraryManager;
- private readonly ILogger _logger;
+ private readonly ILogger<StudiosValidator> _logger;
private readonly IItemRepository _itemRepo;
/// <summary>
@@ -28,7 +28,7 @@ namespace Emby.Server.Implementations.Library.Validators
/// <param name="itemRepo">The item repository.</param>
public StudiosPostScanTask(
ILibraryManager libraryManager,
- ILogger<StudiosPostScanTask> logger,
+ ILogger<StudiosValidator> logger,
IItemRepository itemRepo)
{
_libraryManager = libraryManager;
diff --git a/Emby.Server.Implementations/Library/Validators/StudiosValidator.cs b/Emby.Server.Implementations/Library/Validators/StudiosValidator.cs
index 15e7a0dbb..7a6cd11df 100644
--- a/Emby.Server.Implementations/Library/Validators/StudiosValidator.cs
+++ b/Emby.Server.Implementations/Library/Validators/StudiosValidator.cs
@@ -24,7 +24,7 @@ namespace Emby.Server.Implementations.Library.Validators
/// <summary>
/// The logger.
/// </summary>
- private readonly ILogger _logger;
+ private readonly ILogger<StudiosValidator> _logger;
/// <summary>
/// Initializes a new instance of the <see cref="StudiosValidator" /> class.
@@ -32,7 +32,7 @@ namespace Emby.Server.Implementations.Library.Validators
/// <param name="libraryManager">The library manager.</param>
/// <param name="logger">The logger.</param>
/// <param name="itemRepo">The item repository.</param>
- public StudiosValidator(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo)
+ public StudiosValidator(ILibraryManager libraryManager, ILogger<StudiosValidator> logger, IItemRepository itemRepo)
{
_libraryManager = libraryManager;
_logger = logger;
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
index 3efe1ee25..9fb5aec29 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
@@ -46,7 +46,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
private const int TunerDiscoveryDurationMs = 3000;
private readonly IServerApplicationHost _appHost;
- private readonly ILogger _logger;
+ private readonly ILogger<EmbyTV> _logger;
private readonly IHttpClient _httpClient;
private readonly IServerConfigurationManager _config;
private readonly IJsonSerializer _jsonSerializer;
@@ -140,11 +140,11 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
}
- private void OnNamedConfigurationUpdated(object sender, ConfigurationUpdateEventArgs e)
+ private async void OnNamedConfigurationUpdated(object sender, ConfigurationUpdateEventArgs e)
{
if (string.Equals(e.Key, "livetv", StringComparison.OrdinalIgnoreCase))
{
- OnRecordingFoldersChanged();
+ await CreateRecordingFolders().ConfigureAwait(false);
}
}
@@ -155,11 +155,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return CreateRecordingFolders();
}
- private async void OnRecordingFoldersChanged()
- {
- await CreateRecordingFolders().ConfigureAwait(false);
- }
-
internal async Task CreateRecordingFolders()
{
try
@@ -1334,7 +1329,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
await CreateRecordingFolders().ConfigureAwait(false);
TriggerRefresh(recordPath);
- EnforceKeepUpTo(timer, seriesPath);
+ await EnforceKeepUpTo(timer, seriesPath).ConfigureAwait(false);
};
await recorder.Record(directStreamProvider, mediaStreamInfo, recordPath, duration, onStarted, activeRecordingInfo.CancellationTokenSource.Token).ConfigureAwait(false);
@@ -1494,7 +1489,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return item;
}
- private async void EnforceKeepUpTo(TimerInfo timer, string seriesPath)
+ private async Task EnforceKeepUpTo(TimerInfo timer, string seriesPath)
{
if (string.IsNullOrWhiteSpace(timer.SeriesTimerId))
{
@@ -1898,22 +1893,22 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
writer.WriteStartDocument(true);
writer.WriteStartElement("tvshow");
string id;
- if (timer.SeriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), out id))
+ if (timer.SeriesProviderIds.TryGetValue(MetadataProvider.Tvdb.ToString(), out id))
{
writer.WriteElementString("id", id);
}
- if (timer.SeriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out id))
+ if (timer.SeriesProviderIds.TryGetValue(MetadataProvider.Imdb.ToString(), out id))
{
writer.WriteElementString("imdb_id", id);
}
- if (timer.SeriesProviderIds.TryGetValue(MetadataProviders.Tmdb.ToString(), out id))
+ if (timer.SeriesProviderIds.TryGetValue(MetadataProvider.Tmdb.ToString(), out id))
{
writer.WriteElementString("tmdbid", id);
}
- if (timer.SeriesProviderIds.TryGetValue(MetadataProviders.Zap2It.ToString(), out id))
+ if (timer.SeriesProviderIds.TryGetValue(MetadataProvider.Zap2It.ToString(), out id))
{
writer.WriteElementString("zap2itid", id);
}
@@ -2080,14 +2075,14 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
writer.WriteElementString("credits", person);
}
- var tmdbCollection = item.GetProviderId(MetadataProviders.TmdbCollection);
+ var tmdbCollection = item.GetProviderId(MetadataProvider.TmdbCollection);
if (!string.IsNullOrEmpty(tmdbCollection))
{
writer.WriteElementString("collectionnumber", tmdbCollection);
}
- var imdb = item.GetProviderId(MetadataProviders.Imdb);
+ var imdb = item.GetProviderId(MetadataProvider.Imdb);
if (!string.IsNullOrEmpty(imdb))
{
if (!isSeriesEpisode)
@@ -2101,7 +2096,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
lockData = false;
}
- var tvdb = item.GetProviderId(MetadataProviders.Tvdb);
+ var tvdb = item.GetProviderId(MetadataProvider.Tvdb);
if (!string.IsNullOrEmpty(tvdb))
{
writer.WriteElementString("tvdbid", tvdb);
@@ -2110,7 +2105,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
lockData = false;
}
- var tmdb = item.GetProviderId(MetadataProviders.Tmdb);
+ var tmdb = item.GetProviderId(MetadataProvider.Tmdb);
if (!string.IsNullOrEmpty(tmdb))
{
writer.WriteElementString("tmdbid", tmdb);
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
index bc86cc59a..70dd8f321 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
@@ -117,7 +117,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
onStarted();
// Important - don't await the log task or we won't be able to kill ffmpeg when the user stops playback
- StartStreamingLog(_process.StandardError.BaseStream, _logFileStream);
+ _ = StartStreamingLog(_process.StandardError.BaseStream, _logFileStream);
_logger.LogInformation("ffmpeg recording process started for {0}", _targetPath);
@@ -321,7 +321,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
}
- private async void StartStreamingLog(Stream source, Stream target)
+ private async Task StartStreamingLog(Stream source, Stream target)
{
try
{
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
index 7ebb043d8..285a59a24 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
@@ -109,7 +109,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
if (startDate < now)
{
- TimerFired?.Invoke(this, new GenericEventArgs<TimerInfo> { Argument = item });
+ TimerFired?.Invoke(this, new GenericEventArgs<TimerInfo>(item));
return;
}
@@ -151,7 +151,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
var timer = GetAll().FirstOrDefault(i => string.Equals(i.Id, timerId, StringComparison.OrdinalIgnoreCase));
if (timer != null)
{
- TimerFired?.Invoke(this, new GenericEventArgs<TimerInfo> { Argument = timer });
+ TimerFired?.Invoke(this, new GenericEventArgs<TimerInfo>(timer));
}
}
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
index 89b81fd96..a65b3ee03 100644
--- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
@@ -24,7 +24,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
{
public class SchedulesDirect : IListingsProvider
{
- private readonly ILogger _logger;
+ private readonly ILogger<SchedulesDirect> _logger;
private readonly IJsonSerializer _jsonSerializer;
private readonly IHttpClient _httpClient;
private readonly SemaphoreSlim _tokenSemaphore = new SemaphoreSlim(1, 1);
@@ -342,7 +342,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
{
info.SeriesId = programId.Substring(0, 10);
- info.SeriesProviderIds[MetadataProviders.Zap2It.ToString()] = info.SeriesId;
+ info.SeriesProviderIds[MetadataProvider.Zap2It.ToString()] = info.SeriesId;
if (details.metadata != null)
{
diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
index 07f8539c5..077b5c7e5 100644
--- a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
+++ b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
@@ -26,7 +26,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
{
private readonly IServerConfigurationManager _config;
private readonly IHttpClient _httpClient;
- private readonly ILogger _logger;
+ private readonly ILogger<XmlTvListingsProvider> _logger;
private readonly IFileSystem _fileSystem;
private readonly IZipClient _zipClient;
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs b/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs
index a59c1090e..49ad73af3 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs
@@ -26,7 +26,7 @@ namespace Emby.Server.Implementations.LiveTv
private const string ServiceName = "Emby";
- private readonly ILogger _logger;
+ private readonly ILogger<LiveTvDtoService> _logger;
private readonly IImageProcessor _imageProcessor;
private readonly IDtoService _dtoService;
private readonly IApplicationHost _appHost;
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
index 1b10f2d27..e39092f97 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
@@ -7,17 +7,15 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Emby.Server.Implementations.Library;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Enums;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Progress;
-using MediaBrowser.Controller;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Movies;
-using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Persistence;
@@ -33,6 +31,8 @@ using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Tasks;
using Microsoft.Extensions.Logging;
+using Episode = MediaBrowser.Controller.Entities.TV.Episode;
+using Movie = MediaBrowser.Controller.Entities.Movies.Movie;
namespace Emby.Server.Implementations.LiveTv
{
@@ -46,7 +46,7 @@ namespace Emby.Server.Implementations.LiveTv
private const string EtagKey = "ProgramEtag";
private readonly IServerConfigurationManager _config;
- private readonly ILogger _logger;
+ private readonly ILogger<LiveTvManager> _logger;
private readonly IItemRepository _itemRepo;
private readonly IUserManager _userManager;
private readonly IDtoService _dtoService;
@@ -148,27 +148,18 @@ namespace Emby.Server.Implementations.LiveTv
{
var timerId = e.Argument;
- TimerCancelled?.Invoke(this, new GenericEventArgs<TimerEventInfo>
- {
- Argument = new TimerEventInfo
- {
- Id = timerId
- }
- });
+ TimerCancelled?.Invoke(this, new GenericEventArgs<TimerEventInfo>(new TimerEventInfo(timerId)));
}
private void OnEmbyTvTimerCreated(object sender, GenericEventArgs<TimerInfo> e)
{
var timer = e.Argument;
- TimerCreated?.Invoke(this, new GenericEventArgs<TimerEventInfo>
- {
- Argument = new TimerEventInfo
+ TimerCreated?.Invoke(this, new GenericEventArgs<TimerEventInfo>(
+ new TimerEventInfo(timer.Id)
{
- ProgramId = _tvDtoService.GetInternalProgramId(timer.ProgramId),
- Id = timer.Id
- }
- });
+ ProgramId = _tvDtoService.GetInternalProgramId(timer.ProgramId)
+ }));
}
public List<NameIdPair> GetTunerHostTypes()
@@ -707,7 +698,6 @@ namespace Emby.Server.Implementations.LiveTv
{
Path = info.ThumbImageUrl,
Type = ImageType.Thumb
-
}, 0);
}
}
@@ -720,7 +710,6 @@ namespace Emby.Server.Implementations.LiveTv
{
Path = info.LogoImageUrl,
Type = ImageType.Logo
-
}, 0);
}
}
@@ -733,7 +722,6 @@ namespace Emby.Server.Implementations.LiveTv
{
Path = info.BackdropImageUrl,
Type = ImageType.Backdrop
-
}, 0);
}
}
@@ -771,7 +759,8 @@ namespace Emby.Server.Implementations.LiveTv
var dto = _dtoService.GetBaseItemDto(program, new DtoOptions(), user);
- var list = new List<Tuple<BaseItemDto, string, string>>() {
+ var list = new List<Tuple<BaseItemDto, string, string>>
+ {
new Tuple<BaseItemDto, string, string>(dto, program.ExternalId, program.ExternalSeriesId)
};
@@ -788,22 +777,12 @@ namespace Emby.Server.Implementations.LiveTv
if (query.OrderBy.Count == 0)
{
- if (query.IsAiring ?? false)
- {
- // Unless something else was specified, order by start date to take advantage of a specialized index
- query.OrderBy = new[]
- {
- (ItemSortBy.StartDate, SortOrder.Ascending)
- };
- }
- else
+
+ // Unless something else was specified, order by start date to take advantage of a specialized index
+ query.OrderBy = new[]
{
- // Unless something else was specified, order by start date to take advantage of a specialized index
- query.OrderBy = new[]
- {
- (ItemSortBy.StartDate, SortOrder.Ascending)
- };
- }
+ (ItemSortBy.StartDate, SortOrder.Ascending)
+ };
}
RemoveFields(options);
@@ -1734,13 +1713,7 @@ namespace Emby.Server.Implementations.LiveTv
if (!(service is EmbyTV.EmbyTV))
{
- TimerCancelled?.Invoke(this, new GenericEventArgs<TimerEventInfo>
- {
- Argument = new TimerEventInfo
- {
- Id = id
- }
- });
+ TimerCancelled?.Invoke(this, new GenericEventArgs<TimerEventInfo>(new TimerEventInfo(id)));
}
}
@@ -1757,13 +1730,7 @@ namespace Emby.Server.Implementations.LiveTv
await service.CancelSeriesTimerAsync(timer.ExternalId, CancellationToken.None).ConfigureAwait(false);
- SeriesTimerCancelled?.Invoke(this, new GenericEventArgs<TimerEventInfo>
- {
- Argument = new TimerEventInfo
- {
- Id = id
- }
- });
+ SeriesTimerCancelled?.Invoke(this, new GenericEventArgs<TimerEventInfo>(new TimerEventInfo(id)));
}
public async Task<TimerInfoDto> GetTimer(string id, CancellationToken cancellationToken)
@@ -2082,14 +2049,11 @@ namespace Emby.Server.Implementations.LiveTv
if (!(service is EmbyTV.EmbyTV))
{
- TimerCreated?.Invoke(this, new GenericEventArgs<TimerEventInfo>
- {
- Argument = new TimerEventInfo
+ TimerCreated?.Invoke(this, new GenericEventArgs<TimerEventInfo>(
+ new TimerEventInfo(newTimerId)
{
- ProgramId = _tvDtoService.GetInternalProgramId(info.ProgramId),
- Id = newTimerId
- }
- });
+ ProgramId = _tvDtoService.GetInternalProgramId(info.ProgramId)
+ }));
}
}
@@ -2114,14 +2078,11 @@ namespace Emby.Server.Implementations.LiveTv
await service.CreateSeriesTimerAsync(info, cancellationToken).ConfigureAwait(false);
}
- SeriesTimerCreated?.Invoke(this, new GenericEventArgs<TimerEventInfo>
- {
- Argument = new TimerEventInfo
+ SeriesTimerCreated?.Invoke(this, new GenericEventArgs<TimerEventInfo>(
+ new TimerEventInfo(newTimerId)
{
- ProgramId = _tvDtoService.GetInternalProgramId(info.ProgramId),
- Id = newTimerId
- }
- });
+ ProgramId = _tvDtoService.GetInternalProgramId(info.ProgramId)
+ }));
}
public async Task UpdateTimer(TimerInfoDto timer, CancellationToken cancellationToken)
@@ -2206,20 +2167,19 @@ namespace Emby.Server.Implementations.LiveTv
var info = new LiveTvInfo
{
Services = services,
- IsEnabled = services.Length > 0
+ IsEnabled = services.Length > 0,
+ EnabledUsers = _userManager.Users
+ .Where(IsLiveTvEnabled)
+ .Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture))
+ .ToArray()
};
- info.EnabledUsers = _userManager.Users
- .Where(IsLiveTvEnabled)
- .Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture))
- .ToArray();
-
return info;
}
private bool IsLiveTvEnabled(User user)
{
- return user.Policy.EnableLiveTvAccess && (Services.Count > 1 || GetConfiguration().TunerHosts.Length > 0);
+ return user.HasPermission(PermissionKind.EnableLiveTvAccess) && (Services.Count > 1 || GetConfiguration().TunerHosts.Length > 0);
}
public IEnumerable<User> GetEnabledUsers()
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
index 7f63991d0..f3fc41352 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
@@ -23,7 +23,7 @@ namespace Emby.Server.Implementations.LiveTv
private const string StreamIdDelimeterString = "_";
private readonly ILiveTvManager _liveTvManager;
- private readonly ILogger _logger;
+ private readonly ILogger<LiveTvMediaSourceProvider> _logger;
private readonly IMediaSourceManager _mediaSourceManager;
private readonly IServerApplicationHost _appHost;
diff --git a/Emby.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs b/Emby.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs
index 1056a33b9..8e7d60a15 100644
--- a/Emby.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs
+++ b/Emby.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
index 80ee1ee33..ba3594efd 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
@@ -22,14 +22,14 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
public abstract class BaseTunerHost
{
protected readonly IServerConfigurationManager Config;
- protected readonly ILogger Logger;
+ protected readonly ILogger<BaseTunerHost> Logger;
protected IJsonSerializer JsonSerializer;
protected readonly IFileSystem FileSystem;
private readonly ConcurrentDictionary<string, ChannelCache> _channelCache =
new ConcurrentDictionary<string, ChannelCache>(StringComparer.OrdinalIgnoreCase);
- protected BaseTunerHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IFileSystem fileSystem)
+ protected BaseTunerHost(IServerConfigurationManager config, ILogger<BaseTunerHost> logger, IJsonSerializer jsonSerializer, IFileSystem fileSystem)
{
Config = config;
Logger = logger;
diff --git a/Emby.Server.Implementations/Localization/Core/bn.json b/Emby.Server.Implementations/Localization/Core/bn.json
index 4949b10e6..77007845f 100644
--- a/Emby.Server.Implementations/Localization/Core/bn.json
+++ b/Emby.Server.Implementations/Localization/Core/bn.json
@@ -93,5 +93,12 @@
"HeaderFavoriteSongs": "প্রিয় গানগুলো",
"HeaderFavoriteShows": "প্রিয় শোগুলো",
"TasksLibraryCategory": "গ্রন্থাগার",
- "TasksMaintenanceCategory": "রক্ষণাবেক্ষণ"
+ "TasksMaintenanceCategory": "রক্ষণাবেক্ষণ",
+ "TaskRefreshLibrary": "স্ক্যান মিডিয়া লাইব্রেরি",
+ "TaskRefreshChapterImagesDescription": "অধ্যায়গুলিতে থাকা ভিডিওগুলির জন্য থাম্বনেইল তৈরি ।",
+ "TaskRefreshChapterImages": "অধ্যায়ের চিত্রগুলি বের করুন",
+ "TaskCleanCacheDescription": "সিস্টেমে আর প্রয়োজন নেই ক্যাশ, ফাইলগুলি মুছে ফেলুন।",
+ "TaskCleanCache": "ক্লিন ক্যাশ ডিরেক্টরি",
+ "TasksChannelsCategory": "ইন্টারনেট চ্যানেল",
+ "TasksApplicationCategory": "আবেদন"
}
diff --git a/Emby.Server.Implementations/Localization/Core/ca.json b/Emby.Server.Implementations/Localization/Core/ca.json
index 7464ac1c0..2c802a39e 100644
--- a/Emby.Server.Implementations/Localization/Core/ca.json
+++ b/Emby.Server.Implementations/Localization/Core/ca.json
@@ -92,5 +92,27 @@
"UserStoppedPlayingItemWithValues": "{0} ha parat de reproduir {1}",
"ValueHasBeenAddedToLibrary": "{0} ha sigut afegit a la teva llibreria",
"ValueSpecialEpisodeName": "Especial - {0}",
- "VersionNumber": "Versió {0}"
+ "VersionNumber": "Versió {0}",
+ "TaskDownloadMissingSubtitlesDescription": "Cerca a internet els subtítols que faltin a partir de la configuració de metadades.",
+ "TaskDownloadMissingSubtitles": "Descarrega els subtítols que faltin",
+ "TaskRefreshChannelsDescription": "Actualitza la informació dels canals d'internet.",
+ "TaskRefreshChannels": "Actualitza Canals",
+ "TaskCleanTranscodeDescription": "Elimina els arxius temporals de transcodificacions que tinguin més d'un dia.",
+ "TaskCleanTranscode": "Neteja les transcodificacions",
+ "TaskUpdatePluginsDescription": "Actualitza les extensions que estan configurades per actualitzar-se automàticament.",
+ "TaskUpdatePlugins": "Actualitza les extensions",
+ "TaskRefreshPeopleDescription": "Actualitza les metadades dels actors i directors de la teva mediateca.",
+ "TaskRefreshPeople": "Actualitza Persones",
+ "TaskCleanLogsDescription": "Esborra els logs que tinguin més de {0} dies.",
+ "TaskCleanLogs": "Neteja els registres",
+ "TaskRefreshLibraryDescription": "Escaneja la mediateca buscant fitxers nous i refresca les metadades.",
+ "TaskRefreshLibrary": "Escaneja la biblioteca de mitjans",
+ "TaskRefreshChapterImagesDescription": "Crea les miniatures dels vídeos que tinguin capítols.",
+ "TaskRefreshChapterImages": "Extreure les imatges dels capítols",
+ "TaskCleanCacheDescription": "Elimina els arxius temporals que ja no són necessaris per al servidor.",
+ "TaskCleanCache": "Elimina arxius temporals",
+ "TasksChannelsCategory": "Canals d'internet",
+ "TasksApplicationCategory": "Aplicació",
+ "TasksLibraryCategory": "Biblioteca",
+ "TasksMaintenanceCategory": "Manteniment"
}
diff --git a/Emby.Server.Implementations/Localization/Core/es_419.json b/Emby.Server.Implementations/Localization/Core/es_419.json
new file mode 100644
index 000000000..b0fdc8386
--- /dev/null
+++ b/Emby.Server.Implementations/Localization/Core/es_419.json
@@ -0,0 +1,117 @@
+{
+ "LabelRunningTimeValue": "Duración: {0}",
+ "ValueSpecialEpisodeName": "Especial - {0}",
+ "Sync": "Sincronizar",
+ "Songs": "Canciones",
+ "Shows": "Programas",
+ "Playlists": "Listas de reproducción",
+ "Photos": "Fotos",
+ "Movies": "Películas",
+ "HeaderNextUp": "A continuación",
+ "HeaderLiveTV": "TV en vivo",
+ "HeaderFavoriteSongs": "Canciones favoritas",
+ "HeaderFavoriteArtists": "Artistas favoritos",
+ "HeaderFavoriteAlbums": "Álbumes favoritos",
+ "HeaderFavoriteEpisodes": "Episodios favoritos",
+ "HeaderFavoriteShows": "Programas favoritos",
+ "HeaderContinueWatching": "Continuar viendo",
+ "HeaderAlbumArtists": "Artistas del álbum",
+ "Genres": "Géneros",
+ "Folders": "Carpetas",
+ "Favorites": "Favoritos",
+ "Collections": "Colecciones",
+ "Channels": "Canales",
+ "Books": "Libros",
+ "Artists": "Artistas",
+ "Albums": "Álbumes",
+ "TaskDownloadMissingSubtitlesDescription": "Busca subtítulos faltantes en Internet basándose en la configuración de metadatos.",
+ "TaskDownloadMissingSubtitles": "Descargar subtítulos faltantes",
+ "TaskRefreshChannelsDescription": "Actualiza la información de canales de Internet.",
+ "TaskRefreshChannels": "Actualizar canales",
+ "TaskCleanTranscodeDescription": "Elimina archivos transcodificados que tengan más de un día.",
+ "TaskCleanTranscode": "Limpiar directorio de transcodificado",
+ "TaskUpdatePluginsDescription": "Descarga e instala actualizaciones para complementos que están configurados para actualizarse automáticamente.",
+ "TaskUpdatePlugins": "Actualizar complementos",
+ "TaskRefreshPeopleDescription": "Actualiza metadatos de actores y directores en tu biblioteca de medios.",
+ "TaskRefreshPeople": "Actualizar personas",
+ "TaskCleanLogsDescription": "Elimina archivos de registro con más de {0} días de antigüedad.",
+ "TaskCleanLogs": "Limpiar directorio de registros",
+ "TaskRefreshLibraryDescription": "Escanea tu biblioteca de medios por archivos nuevos y actualiza los metadatos.",
+ "TaskRefreshLibrary": "Escanear biblioteca de medios",
+ "TaskRefreshChapterImagesDescription": "Crea miniaturas para videos que tienen capítulos.",
+ "TaskRefreshChapterImages": "Extraer imágenes de los capítulos",
+ "TaskCleanCacheDescription": "Elimina archivos caché que ya no son necesarios para el sistema.",
+ "TaskCleanCache": "Limpiar directorio caché",
+ "TasksChannelsCategory": "Canales de Internet",
+ "TasksApplicationCategory": "Aplicación",
+ "TasksLibraryCategory": "Biblioteca",
+ "TasksMaintenanceCategory": "Mantenimiento",
+ "VersionNumber": "Versión {0}",
+ "ValueHasBeenAddedToLibrary": "{0} se ha añadido a tu biblioteca de medios",
+ "UserStoppedPlayingItemWithValues": "{0} ha terminado de reproducir {1} en {2}",
+ "UserStartedPlayingItemWithValues": "{0} está reproduciendo {1} en {2}",
+ "UserPolicyUpdatedWithName": "La política de usuario ha sido actualizada para {0}",
+ "UserPasswordChangedWithName": "Se ha cambiado la contraseña para el usuario {0}",
+ "UserOnlineFromDevice": "{0} está en línea desde {1}",
+ "UserOfflineFromDevice": "{0} se ha desconectado desde {1}",
+ "UserLockedOutWithName": "El usuario {0} ha sido bloqueado",
+ "UserDownloadingItemWithValues": "{0} está descargando {1}",
+ "UserDeletedWithName": "El usuario {0} ha sido eliminado",
+ "UserCreatedWithName": "El usuario {0} ha sido creado",
+ "User": "Usuario",
+ "TvShows": "Programas de TV",
+ "System": "Sistema",
+ "SubtitleDownloadFailureFromForItem": "Falló la descarga de subtítulos desde {0} para {1}",
+ "StartupEmbyServerIsLoading": "El servidor Jellyfin está cargando. Por favor, intente de nuevo pronto.",
+ "ServerNameNeedsToBeRestarted": "{0} debe ser reiniciado",
+ "ScheduledTaskStartedWithName": "{0} iniciado",
+ "ScheduledTaskFailedWithName": "{0} falló",
+ "ProviderValue": "Proveedor: {0}",
+ "PluginUpdatedWithName": "{0} fue actualizado",
+ "PluginUninstalledWithName": "{0} fue desinstalado",
+ "PluginInstalledWithName": "{0} fue instalado",
+ "Plugin": "Complemento",
+ "NotificationOptionVideoPlaybackStopped": "Reproducción de video detenida",
+ "NotificationOptionVideoPlayback": "Reproducción de video iniciada",
+ "NotificationOptionUserLockedOut": "Usuario bloqueado",
+ "NotificationOptionTaskFailed": "Falla de tarea programada",
+ "NotificationOptionServerRestartRequired": "Se necesita reiniciar el servidor",
+ "NotificationOptionPluginUpdateInstalled": "Actualización de complemento instalada",
+ "NotificationOptionPluginUninstalled": "Complemento desinstalado",
+ "NotificationOptionPluginInstalled": "Complemento instalado",
+ "NotificationOptionPluginError": "Falla de complemento",
+ "NotificationOptionNewLibraryContent": "Nuevo contenido agregado",
+ "NotificationOptionInstallationFailed": "Falla de instalación",
+ "NotificationOptionCameraImageUploaded": "Imagen de la cámara subida",
+ "NotificationOptionAudioPlaybackStopped": "Reproducción de audio detenida",
+ "NotificationOptionAudioPlayback": "Reproducción de audio iniciada",
+ "NotificationOptionApplicationUpdateInstalled": "Actualización de la aplicación instalada",
+ "NotificationOptionApplicationUpdateAvailable": "Actualización de la aplicación disponible",
+ "NewVersionIsAvailable": "Una nueva versión del Servidor Jellyfin está disponible para descargar.",
+ "NameSeasonUnknown": "Temporada desconocida",
+ "NameSeasonNumber": "Temporada {0}",
+ "NameInstallFailed": "Falló la instalación de {0}",
+ "MusicVideos": "Videos musicales",
+ "Music": "Música",
+ "MixedContent": "Contenido mezclado",
+ "MessageServerConfigurationUpdated": "Se ha actualizado la configuración del servidor",
+ "MessageNamedServerConfigurationUpdatedWithValue": "Se ha actualizado la sección {0} de la configuración del servidor",
+ "MessageApplicationUpdatedTo": "El servidor Jellyfin ha sido actualizado a {0}",
+ "MessageApplicationUpdated": "El servidor Jellyfin ha sido actualizado",
+ "Latest": "Recientes",
+ "LabelIpAddressValue": "Dirección IP: {0}",
+ "ItemRemovedWithName": "{0} fue removido de la biblioteca",
+ "ItemAddedWithName": "{0} fue agregado a la biblioteca",
+ "Inherit": "Heredar",
+ "HomeVideos": "Videos caseros",
+ "HeaderRecordingGroups": "Grupos de grabación",
+ "HeaderCameraUploads": "Subidas desde la cámara",
+ "FailedLoginAttemptWithUserName": "Intento fallido de inicio de sesión desde {0}",
+ "DeviceOnlineWithName": "{0} está conectado",
+ "DeviceOfflineWithName": "{0} se ha desconectado",
+ "ChapterNameValue": "Capítulo {0}",
+ "CameraImageUploadedFrom": "Una nueva imagen de cámara ha sido subida desde {0}",
+ "AuthenticationSucceededWithUserName": "{0} autenticado con éxito",
+ "Application": "Aplicación",
+ "AppDeviceValues": "App: {0}, Dispositivo: {1}"
+}
diff --git a/Emby.Server.Implementations/Localization/Core/fr.json b/Emby.Server.Implementations/Localization/Core/fr.json
index 150952d8b..47ebe1254 100644
--- a/Emby.Server.Implementations/Localization/Core/fr.json
+++ b/Emby.Server.Implementations/Localization/Core/fr.json
@@ -5,7 +5,7 @@
"Artists": "Artistes",
"AuthenticationSucceededWithUserName": "{0} authentifié avec succès",
"Books": "Livres",
- "CameraImageUploadedFrom": "Une nouvelle photographie a été chargée depuis {0}",
+ "CameraImageUploadedFrom": "Une photo a été chargée depuis {0}",
"Channels": "Chaînes",
"ChapterNameValue": "Chapitre {0}",
"Collections": "Collections",
@@ -15,7 +15,7 @@
"Favorites": "Favoris",
"Folders": "Dossiers",
"Genres": "Genres",
- "HeaderAlbumArtists": "Artistes de l'album",
+ "HeaderAlbumArtists": "Artistes",
"HeaderCameraUploads": "Photos transférées",
"HeaderContinueWatching": "Continuer à regarder",
"HeaderFavoriteAlbums": "Albums favoris",
diff --git a/Emby.Server.Implementations/Localization/Core/nb.json b/Emby.Server.Implementations/Localization/Core/nb.json
index 5637ce346..1b55c2e38 100644
--- a/Emby.Server.Implementations/Localization/Core/nb.json
+++ b/Emby.Server.Implementations/Localization/Core/nb.json
@@ -101,5 +101,18 @@
"TaskRefreshLibrary": "Skann mediebibliotek",
"TaskRefreshChapterImagesDescription": "Lager forhåndsvisningsbilder for videoer som har kapitler.",
"TaskRefreshChapterImages": "Trekk ut Kapittelbilder",
- "TaskCleanCacheDescription": "Sletter mellomlagrede filer som ikke lengre trengs av systemet."
+ "TaskCleanCacheDescription": "Sletter mellomlagrede filer som ikke lengre trengs av systemet.",
+ "TaskDownloadMissingSubtitlesDescription": "Søker etter manglende underteksting på nett basert på metadatakonfigurasjon.",
+ "TaskDownloadMissingSubtitles": "Last ned manglende underteksting",
+ "TaskRefreshChannelsDescription": "Frisker opp internettkanalinformasjon.",
+ "TaskRefreshChannels": "Oppfrisk kanaler",
+ "TaskCleanTranscodeDescription": "Sletter omkodede filer som er mer enn én dag gamle.",
+ "TaskCleanTranscode": "Tøm transkodingmappe",
+ "TaskUpdatePluginsDescription": "Laster ned og installerer oppdateringer for utvidelser som er stilt inn til å oppdatere automatisk.",
+ "TaskUpdatePlugins": "Oppdater utvidelser",
+ "TaskRefreshPeopleDescription": "Oppdaterer metadata for skuespillere og regissører i mediebiblioteket ditt.",
+ "TaskRefreshPeople": "Oppfrisk personer",
+ "TaskCleanLogsDescription": "Sletter loggfiler som er eldre enn {0} dager gamle.",
+ "TaskCleanLogs": "Tøm loggmappe",
+ "TaskRefreshLibraryDescription": "Skanner mediebibliotekene dine for nye filer og oppdaterer metadata."
}
diff --git a/Emby.Server.Implementations/Localization/Core/sl-SI.json b/Emby.Server.Implementations/Localization/Core/sl-SI.json
index 60c58d472..329c562e7 100644
--- a/Emby.Server.Implementations/Localization/Core/sl-SI.json
+++ b/Emby.Server.Implementations/Localization/Core/sl-SI.json
@@ -113,5 +113,6 @@
"TasksChannelsCategory": "Spletni kanali",
"TasksApplicationCategory": "Aplikacija",
"TasksLibraryCategory": "Knjižnica",
- "TasksMaintenanceCategory": "Vzdrževanje"
+ "TasksMaintenanceCategory": "Vzdrževanje",
+ "TaskDownloadMissingSubtitlesDescription": "Na podlagi nastavitev metapodatkov poišče manjkajoče podnapise na internetu."
}
diff --git a/Emby.Server.Implementations/Localization/Core/ta.json b/Emby.Server.Implementations/Localization/Core/ta.json
new file mode 100644
index 000000000..f722dd8c0
--- /dev/null
+++ b/Emby.Server.Implementations/Localization/Core/ta.json
@@ -0,0 +1,99 @@
+{
+ "VersionNumber": "பதிப்பு {0}",
+ "ValueSpecialEpisodeName": "சிறப்பு - {0}",
+ "TasksMaintenanceCategory": "பராமரிப்பு",
+ "TaskCleanCache": "தற்காலிக சேமிப்பு கோப்பகத்தை சுத்தம் செய்யவும்",
+ "TaskRefreshChapterImages": "அத்தியாயப் படங்களை பிரித்தெடுக்கவும்",
+ "TaskRefreshPeople": "மக்களைப் புதுப்பிக்கவும்",
+ "TaskCleanTranscode": "டிரான்ஸ்கோட் கோப்பகத்தை சுத்தம் செய்யவும்",
+ "TaskRefreshChannelsDescription": "இணையச் சேனல் தகவல்களைப் புதுப்பிக்கிறது.",
+ "System": "ஒருங்கியம்",
+ "NotificationOptionTaskFailed": "திட்டமிடப்பட்ட பணி தோல்வியடைந்தது",
+ "NotificationOptionPluginUpdateInstalled": "உட்செருகி புதுப்பிக்கப்பட்டது",
+ "NotificationOptionPluginUninstalled": "உட்செருகி நீக்கப்பட்டது",
+ "NotificationOptionPluginInstalled": "உட்செருகி நிறுவப்பட்டது",
+ "NotificationOptionPluginError": "உட்செருகி செயலிழந்தது",
+ "NotificationOptionCameraImageUploaded": "புகைப்படம் பதிவேற்றப்பட்டது",
+ "MixedContent": "கலப்பு உள்ளடக்கங்கள்",
+ "MessageServerConfigurationUpdated": "சேவையக அமைப்புகள் புதுப்பிக்கப்பட்டன",
+ "MessageApplicationUpdatedTo": "ஜெல்லிஃபின் சேவையகம் {0} இற்கு புதுப்பிக்கப்பட்டது",
+ "MessageApplicationUpdated": "ஜெல்லிஃபின் சேவையகம் புதுப்பிக்கப்பட்டது",
+ "Inherit": "மரபரிமையாகப் பெறு",
+ "HeaderRecordingGroups": "பதிவு குழுக்கள்",
+ "HeaderCameraUploads": "புகைப்பட பதிவேற்றங்கள்",
+ "Folders": "கோப்புறைகள்",
+ "FailedLoginAttemptWithUserName": "{0} இலிருந்து உள்நுழைவு முயற்சி தோல்வியடைந்தது",
+ "DeviceOnlineWithName": "{0} இணைக்கப்பட்டது",
+ "DeviceOfflineWithName": "{0} துண்டிக்கப்பட்டது",
+ "Collections": "தொகுப்புகள்",
+ "CameraImageUploadedFrom": "{0} இலிருந்து புதிய புகைப்படம் பதிவேற்றப்பட்டது",
+ "AppDeviceValues": "செயலி: {0}, சாதனம்: {1}",
+ "TaskDownloadMissingSubtitles": "விடுபட்டுபோன வசன வரிகளைப் பதிவிறக்கு",
+ "TaskRefreshChannels": "சேனல்களை புதுப்பி",
+ "TaskUpdatePlugins": "உட்செருகிகளை புதுப்பி",
+ "TaskRefreshLibrary": "மீடியா நூலகத்தை ஆராய்",
+ "TasksChannelsCategory": "இணைய சேனல்கள்",
+ "TasksApplicationCategory": "செயலி",
+ "TasksLibraryCategory": "நூலகம்",
+ "UserPolicyUpdatedWithName": "பயனர் கொள்கை {0} இற்கு புதுப்பிக்கப்பட்டுள்ளது",
+ "UserPasswordChangedWithName": "{0} பயனருக்கு கடவுச்சொல் மாற்றப்பட்டுள்ளது",
+ "UserLockedOutWithName": "பயனர் {0} முடக்கப்பட்டார்",
+ "UserDownloadingItemWithValues": "{0} ஆல் {1} பதிவிறக்கப்படுகிறது",
+ "UserDeletedWithName": "பயனர் {0} நீக்கப்பட்டார்",
+ "UserCreatedWithName": "பயனர் {0} உருவாக்கப்பட்டார்",
+ "User": "பயனர்",
+ "TvShows": "தொலைக்காட்சித் தொடர்கள்",
+ "Sync": "ஒத்திசைவு",
+ "StartupEmbyServerIsLoading": "ஜெல்லிஃபின் சேவையகம் துவங்குகிறது. சிறிது நேரம் கழித்து முயற்சிக்கவும்.",
+ "Songs": "பாட்டுகள்",
+ "Shows": "தொடர்கள்",
+ "ServerNameNeedsToBeRestarted": "{0} மறுதொடக்கம் செய்யப்பட வேண்டும்",
+ "ScheduledTaskStartedWithName": "{0} துவங்கியது",
+ "ScheduledTaskFailedWithName": "{0} தோல்வியடைந்தது",
+ "ProviderValue": "வழங்குநர்: {0}",
+ "PluginUpdatedWithName": "{0} புதுப்பிக்கப்பட்டது",
+ "PluginUninstalledWithName": "{0} நீக்கப்பட்டது",
+ "PluginInstalledWithName": "{0} நிறுவப்பட்டது",
+ "Plugin": "உட்செருகி",
+ "Playlists": "தொடர் பட்டியல்கள்",
+ "Photos": "புகைப்படங்கள்",
+ "NotificationOptionVideoPlaybackStopped": "நிகழ்பட ஒளிபரப்பு நிறுத்தப்பட்டது",
+ "NotificationOptionVideoPlayback": "நிகழ்பட ஒளிபரப்பு துவங்கியது",
+ "NotificationOptionUserLockedOut": "பயனர் கணக்கு முடக்கப்பட்டது",
+ "NotificationOptionServerRestartRequired": "சேவையக மறுதொடக்கம் தேவை",
+ "NotificationOptionNewLibraryContent": "புதிய உள்ளடக்கங்கள் சேர்க்கப்பட்டன",
+ "NotificationOptionInstallationFailed": "நிறுவல் தோல்வியடைந்தது",
+ "NotificationOptionAudioPlaybackStopped": "ஒலி இசைத்தல் நிறுத்தப்பட்டது",
+ "NotificationOptionAudioPlayback": "ஒலி இசைக்கத் துவங்கியுள்ளது",
+ "NotificationOptionApplicationUpdateInstalled": "செயலி புதுப்பிக்கப்பட்டது",
+ "NotificationOptionApplicationUpdateAvailable": "செயலியினை புதுப்பிக்கலாம்",
+ "NameSeasonUnknown": "பருவம் அறியப்படாதவை",
+ "NameSeasonNumber": "பருவம் {0}",
+ "NameInstallFailed": "{0} நிறுவல் தோல்வியடைந்தது",
+ "MusicVideos": "இசைப்படங்கள்",
+ "Music": "இசை",
+ "Movies": "திரைப்படங்கள்",
+ "Latest": "புதியன",
+ "LabelRunningTimeValue": "ஓடும் நேரம்: {0}",
+ "LabelIpAddressValue": "ஐபி முகவரி: {0}",
+ "ItemRemovedWithName": "{0} நூலகத்திலிருந்து அகற்றப்பட்டது",
+ "ItemAddedWithName": "{0} நூலகத்தில் சேர்க்கப்பட்டது",
+ "HeaderNextUp": "அடுத்ததாக",
+ "HeaderLiveTV": "நேரடித் தொலைக்காட்சி",
+ "HeaderFavoriteSongs": "பிடித்த பாட்டுகள்",
+ "HeaderFavoriteShows": "பிடித்த தொடர்கள்",
+ "HeaderFavoriteEpisodes": "பிடித்த அத்தியாயங்கள்",
+ "HeaderFavoriteArtists": "பிடித்த கலைஞர்கள்",
+ "HeaderFavoriteAlbums": "பிடித்த ஆல்பங்கள்",
+ "HeaderContinueWatching": "தொடர்ந்து பார்",
+ "HeaderAlbumArtists": "இசைக் கலைஞர்கள்",
+ "Genres": "வகைகள்",
+ "Favorites": "பிடித்தவை",
+ "ChapterNameValue": "அத்தியாயம் {0}",
+ "Channels": "சேனல்கள்",
+ "Books": "புத்தகங்கள்",
+ "AuthenticationSucceededWithUserName": "{0} வெற்றிகரமாக அங்கீகரிக்கப்பட்டது",
+ "Artists": "கலைஞர்கள்",
+ "Application": "செயலி",
+ "Albums": "ஆல்பங்கள்"
+}
diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs
index e2a634e1a..62a23118f 100644
--- a/Emby.Server.Implementations/Localization/LocalizationManager.cs
+++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs
@@ -25,7 +25,7 @@ namespace Emby.Server.Implementations.Localization
private readonly IServerConfigurationManager _configurationManager;
private readonly IJsonSerializer _jsonSerializer;
- private readonly ILogger _logger;
+ private readonly ILogger<LocalizationManager> _logger;
private readonly Dictionary<string, Dictionary<string, ParentalRating>> _allParentalRatings =
new Dictionary<string, Dictionary<string, ParentalRating>>(StringComparer.OrdinalIgnoreCase);
diff --git a/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs b/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs
index 677d68b4c..438bbe24a 100644
--- a/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs
+++ b/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -21,7 +23,7 @@ namespace Emby.Server.Implementations.MediaEncoder
{
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
private readonly IFileSystem _fileSystem;
- private readonly ILogger _logger;
+ private readonly ILogger<EncodingManager> _logger;
private readonly IMediaEncoder _encoder;
private readonly IChapterManager _chapterManager;
private readonly ILibraryManager _libraryManager;
diff --git a/Emby.Server.Implementations/Net/SocketFactory.cs b/Emby.Server.Implementations/Net/SocketFactory.cs
index e42ff8496..f347540c7 100644
--- a/Emby.Server.Implementations/Net/SocketFactory.cs
+++ b/Emby.Server.Implementations/Net/SocketFactory.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Net;
using System.Net.Sockets;
diff --git a/Emby.Server.Implementations/Net/UdpSocket.cs b/Emby.Server.Implementations/Net/UdpSocket.cs
index 211ca6784..848f82d85 100644
--- a/Emby.Server.Implementations/Net/UdpSocket.cs
+++ b/Emby.Server.Implementations/Net/UdpSocket.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Net;
using System.Net.Sockets;
diff --git a/Emby.Server.Implementations/Networking/NetworkManager.cs b/Emby.Server.Implementations/Networking/NetworkManager.cs
index b3e88b667..45864bb42 100644
--- a/Emby.Server.Implementations/Networking/NetworkManager.cs
+++ b/Emby.Server.Implementations/Networking/NetworkManager.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -13,7 +15,7 @@ namespace Emby.Server.Implementations.Networking
{
public class NetworkManager : INetworkManager
{
- private readonly ILogger _logger;
+ private readonly ILogger<NetworkManager> _logger;
private IPAddress[] _localIpAddresses;
private readonly object _localIpAddressSyncLock = new object();
diff --git a/Emby.Server.Implementations/Playlists/ManualPlaylistsFolder.cs b/Emby.Server.Implementations/Playlists/ManualPlaylistsFolder.cs
index cd9f7946e..358606b0d 100644
--- a/Emby.Server.Implementations/Playlists/ManualPlaylistsFolder.cs
+++ b/Emby.Server.Implementations/Playlists/ManualPlaylistsFolder.cs
@@ -1,6 +1,9 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Serialization;
+using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Playlists;
using MediaBrowser.Model.Querying;
@@ -42,7 +45,7 @@ namespace Emby.Server.Implementations.Playlists
}
query.Recursive = true;
- query.IncludeItemTypes = new string[] { "Playlist" };
+ query.IncludeItemTypes = new[] { "Playlist" };
query.Parent = null;
return LibraryManager.GetItemsResult(query);
}
diff --git a/Emby.Server.Implementations/Playlists/PlaylistManager.cs b/Emby.Server.Implementations/Playlists/PlaylistManager.cs
index 9b1510ac9..9feef5d8d 100644
--- a/Emby.Server.Implementations/Playlists/PlaylistManager.cs
+++ b/Emby.Server.Implementations/Playlists/PlaylistManager.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -5,6 +7,7 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
@@ -19,6 +22,8 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using PlaylistsNET.Content;
using PlaylistsNET.Models;
+using Genre = MediaBrowser.Controller.Entities.Genre;
+using MusicAlbum = MediaBrowser.Controller.Entities.Audio.MusicAlbum;
namespace Emby.Server.Implementations.Playlists
{
@@ -27,7 +32,7 @@ namespace Emby.Server.Implementations.Playlists
private readonly ILibraryManager _libraryManager;
private readonly IFileSystem _fileSystem;
private readonly ILibraryMonitor _iLibraryMonitor;
- private readonly ILogger _logger;
+ private readonly ILogger<PlaylistManager> _logger;
private readonly IUserManager _userManager;
private readonly IProviderManager _providerManager;
private readonly IConfiguration _appConfig;
@@ -153,10 +158,7 @@ namespace Emby.Server.Implementations.Playlists
});
}
- return new PlaylistCreationResult
- {
- Id = playlist.Id.ToString("N", CultureInfo.InvariantCulture)
- };
+ return new PlaylistCreationResult(playlist.Id.ToString("N", CultureInfo.InvariantCulture));
}
finally
{
diff --git a/Emby.Server.Implementations/ResourceFileManager.cs b/Emby.Server.Implementations/ResourceFileManager.cs
index 6eda2b503..22fc62293 100644
--- a/Emby.Server.Implementations/ResourceFileManager.cs
+++ b/Emby.Server.Implementations/ResourceFileManager.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.IO;
using MediaBrowser.Controller;
@@ -10,7 +12,7 @@ namespace Emby.Server.Implementations
public class ResourceFileManager : IResourceFileManager
{
private readonly IFileSystem _fileSystem;
- private readonly ILogger _logger;
+ private readonly ILogger<ResourceFileManager> _logger;
public ResourceFileManager(ILogger<ResourceFileManager> logger, IFileSystem fileSystem)
{
diff --git a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
index 5b188d962..e58c335a8 100644
--- a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Globalization;
using System.IO;
@@ -51,7 +53,6 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// </summary>
/// <value>The task manager.</value>
private ITaskManager TaskManager { get; set; }
- private readonly IFileSystem _fileSystem;
/// <summary>
/// Initializes a new instance of the <see cref="ScheduledTaskWorker" /> class.
@@ -72,24 +73,28 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// or
/// logger
/// </exception>
- public ScheduledTaskWorker(IScheduledTask scheduledTask, IApplicationPaths applicationPaths, ITaskManager taskManager, IJsonSerializer jsonSerializer, ILogger logger, IFileSystem fileSystem)
+ public ScheduledTaskWorker(IScheduledTask scheduledTask, IApplicationPaths applicationPaths, ITaskManager taskManager, IJsonSerializer jsonSerializer, ILogger logger)
{
if (scheduledTask == null)
{
throw new ArgumentNullException(nameof(scheduledTask));
}
+
if (applicationPaths == null)
{
throw new ArgumentNullException(nameof(applicationPaths));
}
+
if (taskManager == null)
{
throw new ArgumentNullException(nameof(taskManager));
}
+
if (jsonSerializer == null)
{
throw new ArgumentNullException(nameof(jsonSerializer));
}
+
if (logger == null)
{
throw new ArgumentNullException(nameof(logger));
@@ -100,7 +105,6 @@ namespace Emby.Server.Implementations.ScheduledTasks
TaskManager = taskManager;
JsonSerializer = jsonSerializer;
Logger = logger;
- _fileSystem = fileSystem;
InitTriggerEvents();
}
@@ -392,7 +396,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
((TaskManager)TaskManager).OnTaskExecuting(this);
- progress.ProgressChanged += progress_ProgressChanged;
+ progress.ProgressChanged += OnProgressChanged;
TaskCompletionStatus status;
CurrentExecutionStartTime = DateTime.UtcNow;
@@ -426,7 +430,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
var startTime = CurrentExecutionStartTime;
var endTime = DateTime.UtcNow;
- progress.ProgressChanged -= progress_ProgressChanged;
+ progress.ProgressChanged -= OnProgressChanged;
CurrentCancellationTokenSource.Dispose();
CurrentCancellationTokenSource = null;
CurrentProgress = null;
@@ -439,16 +443,13 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The e.</param>
- void progress_ProgressChanged(object sender, double e)
+ private void OnProgressChanged(object sender, double e)
{
e = Math.Min(e, 100);
CurrentProgress = e;
- TaskProgress?.Invoke(this, new GenericEventArgs<double>
- {
- Argument = e
- });
+ TaskProgress?.Invoke(this, new GenericEventArgs<double>(e));
}
/// <summary>
@@ -576,6 +577,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// <param name="startTime">The start time.</param>
/// <param name="endTime">The end time.</param>
/// <param name="status">The status.</param>
+ /// <param name="ex">The exception.</param>
private void OnTaskCompleted(DateTime startTime, DateTime endTime, TaskCompletionStatus status, Exception ex)
{
var elapsedTime = endTime - startTime;
diff --git a/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs b/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs
index 6ffa581a9..94220ac5d 100644
--- a/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@@ -34,7 +36,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
private readonly IJsonSerializer _jsonSerializer;
private readonly IApplicationPaths _applicationPaths;
- private readonly ILogger _logger;
+ private readonly ILogger<TaskManager> _logger;
private readonly IFileSystem _fileSystem;
/// <summary>
@@ -199,7 +201,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// <param name="tasks">The tasks.</param>
public void AddTasks(IEnumerable<IScheduledTask> tasks)
{
- var list = tasks.Select(t => new ScheduledTaskWorker(t, _applicationPaths, this, _jsonSerializer, _logger, _fileSystem));
+ var list = tasks.Select(t => new ScheduledTaskWorker(t, _applicationPaths, this, _jsonSerializer, _logger));
ScheduledTasks = ScheduledTasks.Concat(list).ToArray();
}
@@ -240,10 +242,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// <param name="task">The task.</param>
internal void OnTaskExecuting(IScheduledTaskWorker task)
{
- TaskExecuting?.Invoke(this, new GenericEventArgs<IScheduledTaskWorker>
- {
- Argument = task
- });
+ TaskExecuting?.Invoke(this, new GenericEventArgs<IScheduledTaskWorker>(task));
}
/// <summary>
@@ -253,11 +252,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// <param name="result">The result.</param>
internal void OnTaskCompleted(IScheduledTaskWorker task, TaskResult result)
{
- TaskCompleted?.Invoke(task, new TaskCompletionEventArgs
- {
- Result = result,
- Task = task
- });
+ TaskCompleted?.Invoke(task, new TaskCompletionEventArgs(task, result));
ExecuteQueuedTasks();
}
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs
index ea6a70615..9028222dd 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs
@@ -27,7 +27,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// <summary>
/// The _logger.
/// </summary>
- private readonly ILogger _logger;
+ private readonly ILogger<ChapterImagesTask> _logger;
/// <summary>
/// The _library manager.
@@ -54,7 +54,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
IFileSystem fileSystem,
ILocalizationManager localization)
{
- _logger = loggerFactory.CreateLogger(GetType().Name);
+ _logger = loggerFactory.CreateLogger<ChapterImagesTask>();
_libraryManager = libraryManager;
_itemRepo = itemRepo;
_appPaths = appPaths;
@@ -169,18 +169,25 @@ namespace Emby.Server.Implementations.ScheduledTasks
}
}
+ /// <inheritdoc />
public string Name => _localization.GetLocalizedString("TaskRefreshChapterImages");
+ /// <inheritdoc />
public string Description => _localization.GetLocalizedString("TaskRefreshChapterImagesDescription");
+ /// <inheritdoc />
public string Category => _localization.GetLocalizedString("TasksLibraryCategory");
+ /// <inheritdoc />
public string Key => "RefreshChapterImages";
+ /// <inheritdoc />
public bool IsHidden => false;
+ /// <inheritdoc />
public bool IsEnabled => true;
+ /// <inheritdoc />
public bool IsLogged => true;
}
}
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs
index 9df7c538b..966b549b2 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs
@@ -23,7 +23,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
/// <value>The application paths.</value>
private IApplicationPaths ApplicationPaths { get; set; }
- private readonly ILogger _logger;
+ private readonly ILogger<DeleteCacheFileTask> _logger;
private readonly IFileSystem _fileSystem;
private readonly ILocalizationManager _localization;
@@ -165,18 +165,25 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
}
}
+ /// <inheritdoc />
public string Name => _localization.GetLocalizedString("TaskCleanCache");
+ /// <inheritdoc />
public string Description => _localization.GetLocalizedString("TaskCleanCacheDescription");
+ /// <inheritdoc />
public string Category => _localization.GetLocalizedString("TasksMaintenanceCategory");
+ /// <inheritdoc />
public string Key => "DeleteCacheFiles";
+ /// <inheritdoc />
public bool IsHidden => false;
+ /// <inheritdoc />
public bool IsEnabled => true;
+ /// <inheritdoc />
public bool IsLogged => true;
}
}
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs
index 3140aa489..402b39a26 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs
@@ -28,6 +28,8 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
/// Initializes a new instance of the <see cref="DeleteLogFileTask" /> class.
/// </summary>
/// <param name="configurationManager">The configuration manager.</param>
+ /// <param name="fileSystem">The file system.</param>
+ /// <param name="localization">The localization manager.</param>
public DeleteLogFileTask(IConfigurationManager configurationManager, IFileSystem fileSystem, ILocalizationManager localization)
{
ConfigurationManager = configurationManager;
@@ -82,18 +84,25 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
return Task.CompletedTask;
}
+ /// <inheritdoc />
public string Name => _localization.GetLocalizedString("TaskCleanLogs");
+ /// <inheritdoc />
public string Description => string.Format(_localization.GetLocalizedString("TaskCleanLogsDescription"), ConfigurationManager.CommonConfiguration.LogFileRetentionDays);
+ /// <inheritdoc />
public string Category => _localization.GetLocalizedString("TasksMaintenanceCategory");
+ /// <inheritdoc />
public string Key => "CleanLogFiles";
+ /// <inheritdoc />
public bool IsHidden => false;
+ /// <inheritdoc />
public bool IsEnabled => true;
+ /// <inheritdoc />
public bool IsLogged => true;
}
}
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs
index 1d133dcda..53cf9a0a5 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs
@@ -17,7 +17,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
/// </summary>
public class DeleteTranscodeFileTask : IScheduledTask, IConfigurableScheduledTask
{
- private readonly ILogger _logger;
+ private readonly ILogger<DeleteTranscodeFileTask> _logger;
private readonly IConfigurationManager _configurationManager;
private readonly IFileSystem _fileSystem;
private readonly ILocalizationManager _localization;
@@ -132,18 +132,25 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
}
}
+ /// <inheritdoc />
public string Name => _localization.GetLocalizedString("TaskCleanTranscode");
+ /// <inheritdoc />
public string Description => _localization.GetLocalizedString("TaskCleanTranscodeDescription");
+ /// <inheritdoc />
public string Category => _localization.GetLocalizedString("TasksMaintenanceCategory");
+ /// <inheritdoc />
public string Key => "DeleteTranscodeFiles";
+ /// <inheritdoc />
public bool IsHidden => false;
+ /// <inheritdoc />
public bool IsEnabled => false;
+ /// <inheritdoc />
public bool IsLogged => true;
}
}
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs
index 63f867bf6..c384cf4bb 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs
@@ -1,8 +1,9 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
-using MediaBrowser.Controller;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Tasks;
using MediaBrowser.Model.Globalization;
@@ -18,19 +19,16 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// The library manager.
/// </summary>
private readonly ILibraryManager _libraryManager;
-
- private readonly IServerApplicationHost _appHost;
private readonly ILocalizationManager _localization;
/// <summary>
/// Initializes a new instance of the <see cref="PeopleValidationTask" /> class.
/// </summary>
/// <param name="libraryManager">The library manager.</param>
- /// <param name="appHost">The server application host</param>
- public PeopleValidationTask(ILibraryManager libraryManager, IServerApplicationHost appHost, ILocalizationManager localization)
+ /// <param name="localization">The localization manager.</param>
+ public PeopleValidationTask(ILibraryManager libraryManager, ILocalizationManager localization)
{
_libraryManager = libraryManager;
- _appHost = appHost;
_localization = localization;
}
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs
index 6a1afced7..7388086fb 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.IO;
@@ -20,7 +22,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// <summary>
/// The _logger.
/// </summary>
- private readonly ILogger _logger;
+ private readonly ILogger<PluginUpdateTask> _logger;
private readonly IInstallationManager _installationManager;
private readonly ILocalizationManager _localization;
@@ -80,11 +82,11 @@ namespace Emby.Server.Implementations.ScheduledTasks
}
catch (HttpException ex)
{
- _logger.LogError(ex, "Error downloading {0}", package.name);
+ _logger.LogError(ex, "Error downloading {0}", package.Name);
}
catch (IOException ex)
{
- _logger.LogError(ex, "Error updating {0}", package.name);
+ _logger.LogError(ex, "Error updating {0}", package.Name);
}
// Update progress
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs
index 74cb01444..e470adcf4 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs
@@ -1,9 +1,10 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Emby.Server.Implementations.Library;
-using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Tasks;
using MediaBrowser.Model.Globalization;
@@ -16,20 +17,19 @@ namespace Emby.Server.Implementations.ScheduledTasks
public class RefreshMediaLibraryTask : IScheduledTask
{
/// <summary>
- /// The _library manager
+ /// The _library manager.
/// </summary>
private readonly ILibraryManager _libraryManager;
- private readonly IServerConfigurationManager _config;
private readonly ILocalizationManager _localization;
/// <summary>
/// Initializes a new instance of the <see cref="RefreshMediaLibraryTask" /> class.
/// </summary>
/// <param name="libraryManager">The library manager.</param>
- public RefreshMediaLibraryTask(ILibraryManager libraryManager, IServerConfigurationManager config, ILocalizationManager localization)
+ /// <param name="localization">The localization manager.</param>
+ public RefreshMediaLibraryTask(ILibraryManager libraryManager, ILocalizationManager localization)
{
_libraryManager = libraryManager;
- _config = config;
_localization = localization;
}
@@ -61,18 +61,25 @@ namespace Emby.Server.Implementations.ScheduledTasks
return ((LibraryManager)_libraryManager).ValidateMediaLibraryInternal(progress, cancellationToken);
}
+ /// <inheritdoc />
public string Name => _localization.GetLocalizedString("TaskRefreshLibrary");
+ /// <inheritdoc />
public string Description => _localization.GetLocalizedString("TaskRefreshLibraryDescription");
+ /// <inheritdoc />
public string Category => _localization.GetLocalizedString("TasksLibraryCategory");
+ /// <inheritdoc />
public string Key => "RefreshLibrary";
+ /// <inheritdoc />
public bool IsHidden => false;
+ /// <inheritdoc />
public bool IsEnabled => true;
+ /// <inheritdoc />
public bool IsLogged => true;
}
}
diff --git a/Emby.Server.Implementations/ScheduledTasks/Triggers/DailyTrigger.cs b/Emby.Server.Implementations/ScheduledTasks/Triggers/DailyTrigger.cs
index ea278de0d..c7819d4c0 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Triggers/DailyTrigger.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Triggers/DailyTrigger.cs
@@ -31,6 +31,8 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// Stars waiting for the trigger action
/// </summary>
/// <param name="lastResult">The last result.</param>
+ /// <param name="logger">The logger.</param>
+ /// <param name="taskName">The name of the task.</param>
/// <param name="isApplicationStartup">if set to <c>true</c> [is application startup].</param>
public void Start(TaskResult lastResult, ILogger logger, string taskName, bool isApplicationStartup)
{
@@ -77,10 +79,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// </summary>
private void OnTriggered()
{
- if (Triggered != null)
- {
- Triggered(this, EventArgs.Empty);
- }
+ Triggered?.Invoke(this, EventArgs.Empty);
}
}
}
diff --git a/Emby.Server.Implementations/ScheduledTasks/Triggers/IntervalTrigger.cs b/Emby.Server.Implementations/ScheduledTasks/Triggers/IntervalTrigger.cs
index 3a34da3af..74cd4ef1e 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Triggers/IntervalTrigger.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Triggers/IntervalTrigger.cs
@@ -34,6 +34,8 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// Stars waiting for the trigger action
/// </summary>
/// <param name="lastResult">The last result.</param>
+ /// <param name="logger">The logger.</param>
+ /// <param name="taskName">The name of the task.</param>
/// <param name="isApplicationStartup">if set to <c>true</c> [is application startup].</param>
public void Start(TaskResult lastResult, ILogger logger, string taskName, bool isApplicationStartup)
{
diff --git a/Emby.Server.Implementations/ScheduledTasks/Triggers/StartupTrigger.cs b/Emby.Server.Implementations/ScheduledTasks/Triggers/StartupTrigger.cs
index 08ff4f55f..e171a9e9f 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Triggers/StartupTrigger.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Triggers/StartupTrigger.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Threading.Tasks;
using MediaBrowser.Model.Tasks;
@@ -6,7 +8,7 @@ using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.ScheduledTasks
{
/// <summary>
- /// Class StartupTaskTrigger
+ /// Class StartupTaskTrigger.
/// </summary>
public class StartupTrigger : ITaskTrigger
{
@@ -26,6 +28,8 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// Stars waiting for the trigger action
/// </summary>
/// <param name="lastResult">The last result.</param>
+ /// <param name="logger">The logger.</param>
+ /// <param name="taskName">The name of the task.</param>
/// <param name="isApplicationStartup">if set to <c>true</c> [is application startup].</param>
public async void Start(TaskResult lastResult, ILogger logger, string taskName, bool isApplicationStartup)
{
diff --git a/Emby.Server.Implementations/ScheduledTasks/Triggers/WeeklyTrigger.cs b/Emby.Server.Implementations/ScheduledTasks/Triggers/WeeklyTrigger.cs
index 2a6a7b13c..ad0b57af6 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Triggers/WeeklyTrigger.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Triggers/WeeklyTrigger.cs
@@ -37,6 +37,8 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// Stars waiting for the trigger action
/// </summary>
/// <param name="lastResult">The last result.</param>
+ /// <param name="logger">The logger.</param>
+ /// <param name="taskName">The name of the task.</param>
/// <param name="isApplicationStartup">if set to <c>true</c> [is application startup].</param>
public void Start(TaskResult lastResult, ILogger logger, string taskName, bool isApplicationStartup)
{
diff --git a/Emby.Server.Implementations/Security/AuthenticationRepository.cs b/Emby.Server.Implementations/Security/AuthenticationRepository.cs
index 4e4029f06..750890ec8 100644
--- a/Emby.Server.Implementations/Security/AuthenticationRepository.cs
+++ b/Emby.Server.Implementations/Security/AuthenticationRepository.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Globalization;
diff --git a/Emby.Server.Implementations/Serialization/JsonSerializer.cs b/Emby.Server.Implementations/Serialization/JsonSerializer.cs
index bcc814daf..5ec3a735a 100644
--- a/Emby.Server.Implementations/Serialization/JsonSerializer.cs
+++ b/Emby.Server.Implementations/Serialization/JsonSerializer.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Globalization;
using System.IO;
@@ -11,6 +13,9 @@ namespace Emby.Server.Implementations.Serialization
/// </summary>
public class JsonSerializer : IJsonSerializer
{
+ /// <summary>
+ /// Initializes a new instance of the <see cref="JsonSerializer" /> class.
+ /// </summary>
public JsonSerializer()
{
ServiceStack.Text.JsConfig.DateHandler = ServiceStack.Text.DateHandler.ISO8601;
diff --git a/Emby.Server.Implementations/Services/HttpResult.cs b/Emby.Server.Implementations/Services/HttpResult.cs
index 095193828..8ba86f756 100644
--- a/Emby.Server.Implementations/Services/HttpResult.cs
+++ b/Emby.Server.Implementations/Services/HttpResult.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
using System.IO;
using System.Net;
diff --git a/Emby.Server.Implementations/Services/RequestHelper.cs b/Emby.Server.Implementations/Services/RequestHelper.cs
index 2563cac99..1f9c7fc22 100644
--- a/Emby.Server.Implementations/Services/RequestHelper.cs
+++ b/Emby.Server.Implementations/Services/RequestHelper.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.IO;
using System.Threading.Tasks;
@@ -43,10 +45,7 @@ namespace Emby.Server.Implementations.Services
private static string GetContentTypeWithoutEncoding(string contentType)
{
- return contentType == null
- ? null
- : contentType.Split(';')[0].ToLowerInvariant().Trim();
+ return contentType?.Split(';')[0].ToLowerInvariant().Trim();
}
-
}
}
diff --git a/Emby.Server.Implementations/Services/ResponseHelper.cs b/Emby.Server.Implementations/Services/ResponseHelper.cs
index a566b18dd..f2b1d06f3 100644
--- a/Emby.Server.Implementations/Services/ResponseHelper.cs
+++ b/Emby.Server.Implementations/Services/ResponseHelper.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Globalization;
using System.IO;
@@ -43,8 +45,7 @@ namespace Emby.Server.Implementations.Services
response.StatusCode = httpResult.Status;
}
- var responseOptions = result as IHasHeaders;
- if (responseOptions != null)
+ if (result is IHasHeaders responseOptions)
{
foreach (var responseHeaders in responseOptions.Headers)
{
diff --git a/Emby.Server.Implementations/Services/ServiceController.cs b/Emby.Server.Implementations/Services/ServiceController.cs
index e24a95dbb..e688278b5 100644
--- a/Emby.Server.Implementations/Services/ServiceController.cs
+++ b/Emby.Server.Implementations/Services/ServiceController.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
@@ -13,7 +15,7 @@ namespace Emby.Server.Implementations.Services
public class ServiceController
{
- private readonly ILogger _logger;
+ private readonly ILogger<ServiceController> _logger;
/// <summary>
/// Initializes a new instance of the <see cref="ServiceController"/> class.
diff --git a/Emby.Server.Implementations/Services/ServiceExec.cs b/Emby.Server.Implementations/Services/ServiceExec.cs
index 9f5f97028..606f2a240 100644
--- a/Emby.Server.Implementations/Services/ServiceExec.cs
+++ b/Emby.Server.Implementations/Services/ServiceExec.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Linq;
diff --git a/Emby.Server.Implementations/Services/ServiceHandler.cs b/Emby.Server.Implementations/Services/ServiceHandler.cs
index 934560de3..7f44357e1 100644
--- a/Emby.Server.Implementations/Services/ServiceHandler.cs
+++ b/Emby.Server.Implementations/Services/ServiceHandler.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Reflection;
diff --git a/Emby.Server.Implementations/Services/ServiceMethod.cs b/Emby.Server.Implementations/Services/ServiceMethod.cs
index 5018bf4a2..59ee5908f 100644
--- a/Emby.Server.Implementations/Services/ServiceMethod.cs
+++ b/Emby.Server.Implementations/Services/ServiceMethod.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace Emby.Server.Implementations.Services
diff --git a/Emby.Server.Implementations/Services/ServicePath.cs b/Emby.Server.Implementations/Services/ServicePath.cs
index 27c4dcba0..278379a92 100644
--- a/Emby.Server.Implementations/Services/ServicePath.cs
+++ b/Emby.Server.Implementations/Services/ServicePath.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Globalization;
diff --git a/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs b/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs
index 56e23d549..ab22fe019 100644
--- a/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs
+++ b/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Reflection;
diff --git a/Emby.Server.Implementations/Services/SwaggerService.cs b/Emby.Server.Implementations/Services/SwaggerService.cs
index 5177251c3..16142a70d 100644
--- a/Emby.Server.Implementations/Services/SwaggerService.cs
+++ b/Emby.Server.Implementations/Services/SwaggerService.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Linq;
diff --git a/Emby.Server.Implementations/Services/UrlExtensions.cs b/Emby.Server.Implementations/Services/UrlExtensions.cs
index 483c63ade..e3b6aa197 100644
--- a/Emby.Server.Implementations/Services/UrlExtensions.cs
+++ b/Emby.Server.Implementations/Services/UrlExtensions.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using MediaBrowser.Common.Extensions;
diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs
index df98a35bc..07e443ef5 100644
--- a/Emby.Server.Implementations/Session/SessionManager.cs
+++ b/Emby.Server.Implementations/Session/SessionManager.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@@ -5,6 +7,8 @@ using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Enums;
using MediaBrowser.Common.Events;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller;
@@ -13,7 +17,6 @@ using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Security;
@@ -25,7 +28,10 @@ using MediaBrowser.Model.Events;
using MediaBrowser.Model.Library;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Session;
+using MediaBrowser.Model.SyncPlay;
+using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
+using Episode = MediaBrowser.Controller.Entities.TV.Episode;
namespace Emby.Server.Implementations.Session
{
@@ -42,7 +48,7 @@ namespace Emby.Server.Implementations.Session
/// <summary>
/// The logger.
/// </summary>
- private readonly ILogger _logger;
+ private readonly ILogger<SessionManager> _logger;
private readonly ILibraryManager _libraryManager;
private readonly IUserManager _userManager;
@@ -280,11 +286,18 @@ namespace Emby.Server.Implementations.Session
if (user != null)
{
var userLastActivityDate = user.LastActivityDate ?? DateTime.MinValue;
- user.LastActivityDate = activityDate;
if ((activityDate - userLastActivityDate).TotalSeconds > 60)
{
- _userManager.UpdateUser(user);
+ try
+ {
+ user.LastActivityDate = activityDate;
+ _userManager.UpdateUser(user);
+ }
+ catch (DbUpdateConcurrencyException e)
+ {
+ _logger.LogWarning(e, "Error updating user's last activity date.");
+ }
}
}
@@ -431,7 +444,13 @@ namespace Emby.Server.Implementations.Session
/// <param name="remoteEndPoint">The remote end point.</param>
/// <param name="user">The user.</param>
/// <returns>SessionInfo.</returns>
- private SessionInfo GetSessionInfo(string appName, string appVersion, string deviceId, string deviceName, string remoteEndPoint, User user)
+ private SessionInfo GetSessionInfo(
+ string appName,
+ string appVersion,
+ string deviceId,
+ string deviceName,
+ string remoteEndPoint,
+ User user)
{
CheckDisposed();
@@ -444,14 +463,13 @@ namespace Emby.Server.Implementations.Session
CheckDisposed();
- var sessionInfo = _activeConnections.GetOrAdd(key, k =>
- {
- return CreateSession(k, appName, appVersion, deviceId, deviceName, remoteEndPoint, user);
- });
+ var sessionInfo = _activeConnections.GetOrAdd(
+ key,
+ k => CreateSession(k, appName, appVersion, deviceId, deviceName, remoteEndPoint, user));
- sessionInfo.UserId = user == null ? Guid.Empty : user.Id;
- sessionInfo.UserName = user?.Name;
- sessionInfo.UserPrimaryImageTag = user == null ? null : GetImageCacheTag(user, ImageType.Primary);
+ sessionInfo.UserId = user?.Id ?? Guid.Empty;
+ sessionInfo.UserName = user?.Username;
+ sessionInfo.UserPrimaryImageTag = user?.ProfileImage == null ? null : GetImageCacheTag(user);
sessionInfo.RemoteEndPoint = remoteEndPoint;
sessionInfo.Client = appName;
@@ -470,7 +488,14 @@ namespace Emby.Server.Implementations.Session
return sessionInfo;
}
- private SessionInfo CreateSession(string key, string appName, string appVersion, string deviceId, string deviceName, string remoteEndPoint, User user)
+ private SessionInfo CreateSession(
+ string key,
+ string appName,
+ string appVersion,
+ string deviceId,
+ string deviceName,
+ string remoteEndPoint,
+ User user)
{
var sessionInfo = new SessionInfo(this, _logger)
{
@@ -480,11 +505,11 @@ namespace Emby.Server.Implementations.Session
Id = key.GetMD5().ToString("N", CultureInfo.InvariantCulture)
};
- var username = user?.Name;
+ var username = user?.Username;
sessionInfo.UserId = user?.Id ?? Guid.Empty;
sessionInfo.UserName = username;
- sessionInfo.UserPrimaryImageTag = user == null ? null : GetImageCacheTag(user, ImageType.Primary);
+ sessionInfo.UserPrimaryImageTag = user?.ProfileImage == null ? null : GetImageCacheTag(user);
sessionInfo.RemoteEndPoint = remoteEndPoint;
if (string.IsNullOrEmpty(deviceName))
@@ -532,10 +557,7 @@ namespace Emby.Server.Implementations.Session
private void StartIdleCheckTimer()
{
- if (_idleTimer == null)
- {
- _idleTimer = new Timer(CheckForIdlePlayback, null, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5));
- }
+ _idleTimer ??= new Timer(CheckForIdlePlayback, null, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5));
}
private void StopIdleCheckTimer()
@@ -783,7 +805,7 @@ namespace Emby.Server.Implementations.Session
{
var changed = false;
- if (user.Configuration.RememberAudioSelections)
+ if (user.RememberAudioSelections)
{
if (data.AudioStreamIndex != info.AudioStreamIndex)
{
@@ -800,7 +822,7 @@ namespace Emby.Server.Implementations.Session
}
}
- if (user.Configuration.RememberSubtitleSelections)
+ if (user.RememberSubtitleSelections)
{
if (data.SubtitleStreamIndex != info.SubtitleStreamIndex)
{
@@ -1111,13 +1133,13 @@ namespace Emby.Server.Implementations.Session
if (items.Any(i => i.GetPlayAccess(user) != PlayAccess.Full))
{
throw new ArgumentException(
- string.Format(CultureInfo.InvariantCulture, "{0} is not allowed to play media.", user.Name));
+ string.Format(CultureInfo.InvariantCulture, "{0} is not allowed to play media.", user.Username));
}
}
if (user != null
&& command.ItemIds.Length == 1
- && user.Configuration.EnableNextEpisodeAutoPlay
+ && user.EnableNextEpisodeAutoPlay
&& _libraryManager.GetItemById(command.ItemIds[0]) is Episode episode)
{
var series = episode.Series;
@@ -1153,6 +1175,22 @@ namespace Emby.Server.Implementations.Session
await SendMessageToSession(session, "Play", command, cancellationToken).ConfigureAwait(false);
}
+ /// <inheritdoc />
+ public async Task SendSyncPlayCommand(string sessionId, SendCommand command, CancellationToken cancellationToken)
+ {
+ CheckDisposed();
+ var session = GetSessionToRemoteControl(sessionId);
+ await SendMessageToSession(session, "SyncPlayCommand", command, cancellationToken).ConfigureAwait(false);
+ }
+
+ /// <inheritdoc />
+ public async Task SendSyncPlayGroupUpdate<T>(string sessionId, GroupUpdate<T> command, CancellationToken cancellationToken)
+ {
+ CheckDisposed();
+ var session = GetSessionToRemoteControl(sessionId);
+ await SendMessageToSession(session, "SyncPlayGroupUpdate", command, cancellationToken).ConfigureAwait(false);
+ }
+
private IEnumerable<BaseItem> TranslateItemForPlayback(Guid id, User user)
{
var item = _libraryManager.GetItemById(id);
@@ -1172,7 +1210,7 @@ namespace Emby.Server.Implementations.Session
DtoOptions = new DtoOptions(false)
{
EnableImages = false,
- Fields = new ItemFields[]
+ Fields = new[]
{
ItemFields.SortName
}
@@ -1334,7 +1372,7 @@ namespace Emby.Server.Implementations.Session
list.Add(new SessionUserInfo
{
UserId = userId,
- UserName = user.Name
+ UserName = user.Username
});
session.AdditionalUsers = list.ToArray();
@@ -1494,7 +1532,7 @@ namespace Emby.Server.Implementations.Session
DeviceName = deviceName,
UserId = user.Id,
AccessToken = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture),
- UserName = user.Name
+ UserName = user.Username
};
_logger.LogInformation("Creating new access token for user {0}", user.Id);
@@ -1691,15 +1729,15 @@ namespace Emby.Server.Implementations.Session
return info;
}
- private string GetImageCacheTag(BaseItem item, ImageType type)
+ private string GetImageCacheTag(User user)
{
try
{
- return _imageProcessor.GetImageCacheTag(item, type);
+ return _imageProcessor.GetImageCacheTag(user);
}
- catch (Exception ex)
+ catch (Exception e)
{
- _logger.LogError(ex, "Error getting image information for {Type}", type);
+ _logger.LogError(e, "Error getting image information for profile image");
return null;
}
}
@@ -1808,7 +1846,10 @@ namespace Emby.Server.Implementations.Session
{
CheckDisposed();
- var adminUserIds = _userManager.Users.Where(i => i.Policy.IsAdministrator).Select(i => i.Id).ToList();
+ var adminUserIds = _userManager.Users
+ .Where(i => i.HasPermission(PermissionKind.IsAdministrator))
+ .Select(i => i.Id)
+ .ToList();
return SendMessageToUserSessions(adminUserIds, name, data, cancellationToken);
}
diff --git a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs
index d4e4ba1f2..ef32c692c 100644
--- a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs
+++ b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs
@@ -1,8 +1,13 @@
using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.WebSockets;
+using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Events;
+using MediaBrowser.Model.Net;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
@@ -14,6 +19,21 @@ namespace Emby.Server.Implementations.Session
public sealed class SessionWebSocketListener : IWebSocketListener, IDisposable
{
/// <summary>
+ /// The timeout in seconds after which a WebSocket is considered to be lost.
+ /// </summary>
+ public const int WebSocketLostTimeout = 60;
+
+ /// <summary>
+ /// The keep-alive interval factor; controls how often the watcher will check on the status of the WebSockets.
+ /// </summary>
+ public const float IntervalFactor = 0.2f;
+
+ /// <summary>
+ /// The ForceKeepAlive factor; controls when a ForceKeepAlive is sent.
+ /// </summary>
+ public const float ForceKeepAliveFactor = 0.75f;
+
+ /// <summary>
/// The _session manager
/// </summary>
private readonly ISessionManager _sessionManager;
@@ -21,12 +41,32 @@ namespace Emby.Server.Implementations.Session
/// <summary>
/// The _logger
/// </summary>
- private readonly ILogger _logger;
+ private readonly ILogger<SessionWebSocketListener> _logger;
private readonly ILoggerFactory _loggerFactory;
private readonly IHttpServer _httpServer;
/// <summary>
+ /// The KeepAlive cancellation token.
+ /// </summary>
+ private CancellationTokenSource _keepAliveCancellationToken;
+
+ /// <summary>
+ /// Lock used for accesing the KeepAlive cancellation token.
+ /// </summary>
+ private readonly object _keepAliveLock = new object();
+
+ /// <summary>
+ /// The WebSocket watchlist.
+ /// </summary>
+ private readonly HashSet<IWebSocketConnection> _webSockets = new HashSet<IWebSocketConnection>();
+
+ /// <summary>
+ /// Lock used for accesing the WebSockets watchlist.
+ /// </summary>
+ private readonly object _webSocketsLock = new object();
+
+ /// <summary>
/// Initializes a new instance of the <see cref="SessionWebSocketListener" /> class.
/// </summary>
/// <param name="logger">The logger.</param>
@@ -47,12 +87,13 @@ namespace Emby.Server.Implementations.Session
httpServer.WebSocketConnected += OnServerManagerWebSocketConnected;
}
- private void OnServerManagerWebSocketConnected(object sender, GenericEventArgs<IWebSocketConnection> e)
+ private async void OnServerManagerWebSocketConnected(object sender, GenericEventArgs<IWebSocketConnection> e)
{
var session = GetSession(e.Argument.QueryString, e.Argument.RemoteEndPoint.ToString());
if (session != null)
{
EnsureController(session, e.Argument);
+ await KeepAliveWebSocket(e.Argument);
}
else
{
@@ -81,6 +122,7 @@ namespace Emby.Server.Implementations.Session
public void Dispose()
{
_httpServer.WebSocketConnected -= OnServerManagerWebSocketConnected;
+ StopKeepAlive();
}
/// <summary>
@@ -99,5 +141,206 @@ namespace Emby.Server.Implementations.Session
var controller = (WebSocketController)controllerInfo.Item1;
controller.AddWebSocket(connection);
}
+
+ /// <summary>
+ /// Called when a WebSocket is closed.
+ /// </summary>
+ /// <param name="sender">The WebSocket.</param>
+ /// <param name="e">The event arguments.</param>
+ private void OnWebSocketClosed(object sender, EventArgs e)
+ {
+ var webSocket = (IWebSocketConnection)sender;
+ _logger.LogDebug("WebSocket {0} is closed.", webSocket);
+ RemoveWebSocket(webSocket);
+ }
+
+ /// <summary>
+ /// Adds a WebSocket to the KeepAlive watchlist.
+ /// </summary>
+ /// <param name="webSocket">The WebSocket to monitor.</param>
+ private async Task KeepAliveWebSocket(IWebSocketConnection webSocket)
+ {
+ lock (_webSocketsLock)
+ {
+ if (!_webSockets.Add(webSocket))
+ {
+ _logger.LogWarning("Multiple attempts to keep alive single WebSocket {0}", webSocket);
+ return;
+ }
+ webSocket.Closed += OnWebSocketClosed;
+ webSocket.LastKeepAliveDate = DateTime.UtcNow;
+
+ StartKeepAlive();
+ }
+
+ // Notify WebSocket about timeout
+ try
+ {
+ await SendForceKeepAlive(webSocket);
+ }
+ catch (WebSocketException exception)
+ {
+ _logger.LogWarning(exception, "Cannot send ForceKeepAlive message to WebSocket {0}.", webSocket);
+ }
+ }
+
+ /// <summary>
+ /// Removes a WebSocket from the KeepAlive watchlist.
+ /// </summary>
+ /// <param name="webSocket">The WebSocket to remove.</param>
+ private void RemoveWebSocket(IWebSocketConnection webSocket)
+ {
+ lock (_webSocketsLock)
+ {
+ if (!_webSockets.Remove(webSocket))
+ {
+ _logger.LogWarning("WebSocket {0} not on watchlist.", webSocket);
+ }
+ else
+ {
+ webSocket.Closed -= OnWebSocketClosed;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Starts the KeepAlive watcher.
+ /// </summary>
+ private void StartKeepAlive()
+ {
+ lock (_keepAliveLock)
+ {
+ if (_keepAliveCancellationToken == null)
+ {
+ _keepAliveCancellationToken = new CancellationTokenSource();
+ // Start KeepAlive watcher
+ _ = RepeatAsyncCallbackEvery(
+ KeepAliveSockets,
+ TimeSpan.FromSeconds(WebSocketLostTimeout * IntervalFactor),
+ _keepAliveCancellationToken.Token);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Stops the KeepAlive watcher.
+ /// </summary>
+ private void StopKeepAlive()
+ {
+ lock (_keepAliveLock)
+ {
+ if (_keepAliveCancellationToken != null)
+ {
+ _keepAliveCancellationToken.Cancel();
+ _keepAliveCancellationToken = null;
+ }
+ }
+
+ lock (_webSocketsLock)
+ {
+ foreach (var webSocket in _webSockets)
+ {
+ webSocket.Closed -= OnWebSocketClosed;
+ }
+
+ _webSockets.Clear();
+ }
+ }
+
+ /// <summary>
+ /// Checks status of KeepAlive of WebSockets.
+ /// </summary>
+ private async Task KeepAliveSockets()
+ {
+ List<IWebSocketConnection> inactive;
+ List<IWebSocketConnection> lost;
+
+ lock (_webSocketsLock)
+ {
+ _logger.LogDebug("Watching {0} WebSockets.", _webSockets.Count);
+
+ inactive = _webSockets.Where(i =>
+ {
+ var elapsed = (DateTime.UtcNow - i.LastKeepAliveDate).TotalSeconds;
+ return (elapsed > WebSocketLostTimeout * ForceKeepAliveFactor) && (elapsed < WebSocketLostTimeout);
+ }).ToList();
+ lost = _webSockets.Where(i => (DateTime.UtcNow - i.LastKeepAliveDate).TotalSeconds >= WebSocketLostTimeout).ToList();
+ }
+
+ if (inactive.Any())
+ {
+ _logger.LogInformation("Sending ForceKeepAlive message to {0} inactive WebSockets.", inactive.Count);
+ }
+
+ foreach (var webSocket in inactive)
+ {
+ try
+ {
+ await SendForceKeepAlive(webSocket);
+ }
+ catch (WebSocketException exception)
+ {
+ _logger.LogInformation(exception, "Error sending ForceKeepAlive message to WebSocket.");
+ lost.Add(webSocket);
+ }
+ }
+
+ lock (_webSocketsLock)
+ {
+ if (lost.Any())
+ {
+ _logger.LogInformation("Lost {0} WebSockets.", lost.Count);
+ foreach (var webSocket in lost)
+ {
+ // TODO: handle session relative to the lost webSocket
+ RemoveWebSocket(webSocket);
+ }
+ }
+
+ if (!_webSockets.Any())
+ {
+ StopKeepAlive();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Sends a ForceKeepAlive message to a WebSocket.
+ /// </summary>
+ /// <param name="webSocket">The WebSocket.</param>
+ /// <returns>Task.</returns>
+ private Task SendForceKeepAlive(IWebSocketConnection webSocket)
+ {
+ return webSocket.SendAsync(new WebSocketMessage<int>
+ {
+ MessageType = "ForceKeepAlive",
+ Data = WebSocketLostTimeout
+ }, CancellationToken.None);
+ }
+
+ /// <summary>
+ /// Runs a given async callback once every specified interval time, until cancelled.
+ /// </summary>
+ /// <param name="callback">The async callback.</param>
+ /// <param name="interval">The interval time.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ private async Task RepeatAsyncCallbackEvery(Func<Task> callback, TimeSpan interval, CancellationToken cancellationToken)
+ {
+ while (!cancellationToken.IsCancellationRequested)
+ {
+ await callback();
+ Task task = Task.Delay(interval, cancellationToken);
+
+ try
+ {
+ await task;
+ }
+ catch (TaskCanceledException)
+ {
+ return;
+ }
+ }
+ }
}
}
diff --git a/Emby.Server.Implementations/Session/WebSocketController.cs b/Emby.Server.Implementations/Session/WebSocketController.cs
index a0274acd2..94604ca1e 100644
--- a/Emby.Server.Implementations/Session/WebSocketController.cs
+++ b/Emby.Server.Implementations/Session/WebSocketController.cs
@@ -17,7 +17,7 @@ namespace Emby.Server.Implementations.Session
{
public sealed class WebSocketController : ISessionController, IDisposable
{
- private readonly ILogger _logger;
+ private readonly ILogger<WebSocketController> _logger;
private readonly ISessionManager _sessionManager;
private readonly SessionInfo _session;
diff --git a/Emby.Server.Implementations/SocketSharp/HttpFile.cs b/Emby.Server.Implementations/SocketSharp/HttpFile.cs
deleted file mode 100644
index 120ac50d9..000000000
--- a/Emby.Server.Implementations/SocketSharp/HttpFile.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using System.IO;
-using MediaBrowser.Model.Services;
-
-namespace Emby.Server.Implementations.SocketSharp
-{
- public class HttpFile : IHttpFile
- {
- public string Name { get; set; }
-
- public string FileName { get; set; }
-
- public long ContentLength { get; set; }
-
- public string ContentType { get; set; }
-
- public Stream InputStream { get; set; }
- }
-}
diff --git a/Emby.Server.Implementations/SocketSharp/HttpPostedFile.cs b/Emby.Server.Implementations/SocketSharp/HttpPostedFile.cs
deleted file mode 100644
index 7479d8104..000000000
--- a/Emby.Server.Implementations/SocketSharp/HttpPostedFile.cs
+++ /dev/null
@@ -1,198 +0,0 @@
-using System;
-using System.IO;
-
-public sealed class HttpPostedFile : IDisposable
-{
- private string _name;
- private string _contentType;
- private Stream _stream;
- private bool _disposed = false;
-
- internal HttpPostedFile(string name, string content_type, Stream base_stream, long offset, long length)
- {
- _name = name;
- _contentType = content_type;
- _stream = new ReadSubStream(base_stream, offset, length);
- }
-
- public string ContentType => _contentType;
-
- public int ContentLength => (int)_stream.Length;
-
- public string FileName => _name;
-
- public Stream InputStream => _stream;
-
- /// <summary>
- /// Releases the unmanaged resources and disposes of the managed resources used.
- /// </summary>
- public void Dispose()
- {
- if (_disposed)
- {
- return;
- }
-
- _stream.Dispose();
- _stream = null;
-
- _name = null;
- _contentType = null;
-
- _disposed = true;
- }
-
- private class ReadSubStream : Stream
- {
- private Stream _stream;
- private long _offset;
- private long _end;
- private long _position;
-
- public ReadSubStream(Stream s, long offset, long length)
- {
- _stream = s;
- _offset = offset;
- _end = offset + length;
- _position = offset;
- }
-
- public override bool CanRead => true;
-
- public override bool CanSeek => true;
-
- public override bool CanWrite => false;
-
- public override long Length => _end - _offset;
-
- public override long Position
- {
- get => _position - _offset;
- set
- {
- if (value > Length)
- {
- throw new ArgumentOutOfRangeException(nameof(value));
- }
-
- _position = Seek(value, SeekOrigin.Begin);
- }
- }
-
- public override void Flush()
- {
- }
-
- public override int Read(byte[] buffer, int dest_offset, int count)
- {
- if (buffer == null)
- {
- throw new ArgumentNullException(nameof(buffer));
- }
-
- if (dest_offset < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(dest_offset), "< 0");
- }
-
- if (count < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(count), "< 0");
- }
-
- int len = buffer.Length;
- if (dest_offset > len)
- {
- throw new ArgumentException("destination offset is beyond array size", nameof(dest_offset));
- }
-
- // reordered to avoid possible integer overflow
- if (dest_offset > len - count)
- {
- throw new ArgumentException("Reading would overrun buffer", nameof(count));
- }
-
- if (count > _end - _position)
- {
- count = (int)(_end - _position);
- }
-
- if (count <= 0)
- {
- return 0;
- }
-
- _stream.Position = _position;
- int result = _stream.Read(buffer, dest_offset, count);
- if (result > 0)
- {
- _position += result;
- }
- else
- {
- _position = _end;
- }
-
- return result;
- }
-
- public override int ReadByte()
- {
- if (_position >= _end)
- {
- return -1;
- }
-
- _stream.Position = _position;
- int result = _stream.ReadByte();
- if (result < 0)
- {
- _position = _end;
- }
- else
- {
- _position++;
- }
-
- return result;
- }
-
- public override long Seek(long d, SeekOrigin origin)
- {
- long real;
- switch (origin)
- {
- case SeekOrigin.Begin:
- real = _offset + d;
- break;
- case SeekOrigin.End:
- real = _end + d;
- break;
- case SeekOrigin.Current:
- real = _position + d;
- break;
- default:
- throw new ArgumentException("Unknown SeekOrigin value", nameof(origin));
- }
-
- long virt = real - _offset;
- if (virt < 0 || virt > Length)
- {
- throw new ArgumentException("Invalid position", nameof(d));
- }
-
- _position = _stream.Seek(real, SeekOrigin.Begin);
- return _position;
- }
-
- public override void SetLength(long value)
- {
- throw new NotSupportedException();
- }
-
- public override void Write(byte[] buffer, int offset, int count)
- {
- throw new NotSupportedException();
- }
- }
-}
diff --git a/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs b/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs
index ee5131c1f..ae1a8d0b7 100644
--- a/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs
+++ b/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.IO;
@@ -24,7 +26,7 @@ namespace Emby.Server.Implementations.SocketSharp
private Dictionary<string, object> _items;
private string _responseContentType;
- public WebSocketSharpRequest(HttpRequest httpRequest, HttpResponse httpResponse, string operationName, ILogger logger)
+ public WebSocketSharpRequest(HttpRequest httpRequest, HttpResponse httpResponse, string operationName)
{
this.OperationName = operationName;
this.Request = httpRequest;
@@ -209,7 +211,7 @@ namespace Emby.Server.Implementations.SocketSharp
private static string GetQueryStringContentType(HttpRequest httpReq)
{
ReadOnlySpan<char> format = httpReq.Query["format"].ToString();
- if (format == null)
+ if (format == ReadOnlySpan<char>.Empty)
{
const int FormatMaxLength = 4;
ReadOnlySpan<char> pi = httpReq.Path.ToString();
diff --git a/Emby.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs b/Emby.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs
index 16507466f..67e31f7f6 100644
--- a/Emby.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs
+++ b/Emby.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
diff --git a/Emby.Server.Implementations/Sorting/CommunityRatingComparer.cs b/Emby.Server.Implementations/Sorting/CommunityRatingComparer.cs
index 87d3ae2d6..980954ba0 100644
--- a/Emby.Server.Implementations/Sorting/CommunityRatingComparer.cs
+++ b/Emby.Server.Implementations/Sorting/CommunityRatingComparer.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Sorting;
@@ -8,6 +10,12 @@ namespace Emby.Server.Implementations.Sorting
public class CommunityRatingComparer : IBaseItemComparer
{
/// <summary>
+ /// Gets the name.
+ /// </summary>
+ /// <value>The name.</value>
+ public string Name => ItemSortBy.CommunityRating;
+
+ /// <summary>
/// Compares the specified x.
/// </summary>
/// <param name="x">The x.</param>
@@ -16,18 +24,16 @@ namespace Emby.Server.Implementations.Sorting
public int Compare(BaseItem x, BaseItem y)
{
if (x == null)
+ {
throw new ArgumentNullException(nameof(x));
+ }
if (y == null)
+ {
throw new ArgumentNullException(nameof(y));
+ }
return (x.CommunityRating ?? 0).CompareTo(y.CommunityRating ?? 0);
}
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name => ItemSortBy.CommunityRating;
}
}
diff --git a/Emby.Server.Implementations/Sorting/DateLastMediaAddedComparer.cs b/Emby.Server.Implementations/Sorting/DateLastMediaAddedComparer.cs
index 623675157..03ff19d21 100644
--- a/Emby.Server.Implementations/Sorting/DateLastMediaAddedComparer.cs
+++ b/Emby.Server.Implementations/Sorting/DateLastMediaAddedComparer.cs
@@ -1,4 +1,7 @@
+#pragma warning disable CS1591
+
using System;
+using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Sorting;
@@ -27,6 +30,12 @@ namespace Emby.Server.Implementations.Sorting
public IUserDataManager UserDataRepository { get; set; }
/// <summary>
+ /// Gets the name.
+ /// </summary>
+ /// <value>The name.</value>
+ public string Name => ItemSortBy.DateLastContentAdded;
+
+ /// <summary>
/// Compares the specified x.
/// </summary>
/// <param name="x">The x.</param>
@@ -44,9 +53,7 @@ namespace Emby.Server.Implementations.Sorting
/// <returns>DateTime.</returns>
private static DateTime GetDate(BaseItem x)
{
- var folder = x as Folder;
-
- if (folder != null)
+ if (x is Folder folder)
{
if (folder.DateLastMediaAdded.HasValue)
{
@@ -56,11 +63,5 @@ namespace Emby.Server.Implementations.Sorting
return DateTime.MinValue;
}
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name => ItemSortBy.DateLastContentAdded;
}
}
diff --git a/Emby.Server.Implementations/Sorting/DatePlayedComparer.cs b/Emby.Server.Implementations/Sorting/DatePlayedComparer.cs
index 73f59f8cd..5e527ea25 100644
--- a/Emby.Server.Implementations/Sorting/DatePlayedComparer.cs
+++ b/Emby.Server.Implementations/Sorting/DatePlayedComparer.cs
@@ -1,4 +1,5 @@
using System;
+using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Sorting;
diff --git a/Emby.Server.Implementations/Sorting/IsFavoriteOrLikeComparer.cs b/Emby.Server.Implementations/Sorting/IsFavoriteOrLikeComparer.cs
index 66de05a6a..0c4e82d01 100644
--- a/Emby.Server.Implementations/Sorting/IsFavoriteOrLikeComparer.cs
+++ b/Emby.Server.Implementations/Sorting/IsFavoriteOrLikeComparer.cs
@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+
+using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Sorting;
@@ -14,6 +17,24 @@ namespace Emby.Server.Implementations.Sorting
public User User { get; set; }
/// <summary>
+ /// Gets the name.
+ /// </summary>
+ /// <value>The name.</value>
+ public string Name => ItemSortBy.IsFavoriteOrLiked;
+
+ /// <summary>
+ /// Gets or sets the user data repository.
+ /// </summary>
+ /// <value>The user data repository.</value>
+ public IUserDataManager UserDataRepository { get; set; }
+
+ /// <summary>
+ /// Gets or sets the user manager.
+ /// </summary>
+ /// <value>The user manager.</value>
+ public IUserManager UserManager { get; set; }
+
+ /// <summary>
/// Compares the specified x.
/// </summary>
/// <param name="x">The x.</param>
@@ -33,23 +54,5 @@ namespace Emby.Server.Implementations.Sorting
{
return x.IsFavoriteOrLiked(User) ? 0 : 1;
}
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name => ItemSortBy.IsFavoriteOrLiked;
-
- /// <summary>
- /// Gets or sets the user data repository.
- /// </summary>
- /// <value>The user data repository.</value>
- public IUserDataManager UserDataRepository { get; set; }
-
- /// <summary>
- /// Gets or sets the user manager.
- /// </summary>
- /// <value>The user manager.</value>
- public IUserManager UserManager { get; set; }
}
}
diff --git a/Emby.Server.Implementations/Sorting/IsFolderComparer.cs b/Emby.Server.Implementations/Sorting/IsFolderComparer.cs
index dfaa144cd..a35192eff 100644
--- a/Emby.Server.Implementations/Sorting/IsFolderComparer.cs
+++ b/Emby.Server.Implementations/Sorting/IsFolderComparer.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Querying;
@@ -7,6 +9,12 @@ namespace Emby.Server.Implementations.Sorting
public class IsFolderComparer : IBaseItemComparer
{
/// <summary>
+ /// Gets the name.
+ /// </summary>
+ /// <value>The name.</value>
+ public string Name => ItemSortBy.IsFolder;
+
+ /// <summary>
/// Compares the specified x.
/// </summary>
/// <param name="x">The x.</param>
@@ -26,11 +34,5 @@ namespace Emby.Server.Implementations.Sorting
{
return x.IsFolder ? 0 : 1;
}
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name => ItemSortBy.IsFolder;
}
}
diff --git a/Emby.Server.Implementations/Sorting/IsPlayedComparer.cs b/Emby.Server.Implementations/Sorting/IsPlayedComparer.cs
index da3f3dd25..d95948406 100644
--- a/Emby.Server.Implementations/Sorting/IsPlayedComparer.cs
+++ b/Emby.Server.Implementations/Sorting/IsPlayedComparer.cs
@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+
+using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Sorting;
@@ -14,6 +17,24 @@ namespace Emby.Server.Implementations.Sorting
public User User { get; set; }
/// <summary>
+ /// Gets the name.
+ /// </summary>
+ /// <value>The name.</value>
+ public string Name => ItemSortBy.IsUnplayed;
+
+ /// <summary>
+ /// Gets or sets the user data repository.
+ /// </summary>
+ /// <value>The user data repository.</value>
+ public IUserDataManager UserDataRepository { get; set; }
+
+ /// <summary>
+ /// Gets or sets the user manager.
+ /// </summary>
+ /// <value>The user manager.</value>
+ public IUserManager UserManager { get; set; }
+
+ /// <summary>
/// Compares the specified x.
/// </summary>
/// <param name="x">The x.</param>
@@ -33,23 +54,5 @@ namespace Emby.Server.Implementations.Sorting
{
return x.IsPlayed(User) ? 0 : 1;
}
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name => ItemSortBy.IsUnplayed;
-
- /// <summary>
- /// Gets or sets the user data repository.
- /// </summary>
- /// <value>The user data repository.</value>
- public IUserDataManager UserDataRepository { get; set; }
-
- /// <summary>
- /// Gets or sets the user manager.
- /// </summary>
- /// <value>The user manager.</value>
- public IUserManager UserManager { get; set; }
}
}
diff --git a/Emby.Server.Implementations/Sorting/IsUnplayedComparer.cs b/Emby.Server.Implementations/Sorting/IsUnplayedComparer.cs
index d99d0eff2..1632c5a7a 100644
--- a/Emby.Server.Implementations/Sorting/IsUnplayedComparer.cs
+++ b/Emby.Server.Implementations/Sorting/IsUnplayedComparer.cs
@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+
+using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Sorting;
@@ -14,6 +17,24 @@ namespace Emby.Server.Implementations.Sorting
public User User { get; set; }
/// <summary>
+ /// Gets the name.
+ /// </summary>
+ /// <value>The name.</value>
+ public string Name => ItemSortBy.IsUnplayed;
+
+ /// <summary>
+ /// Gets or sets the user data repository.
+ /// </summary>
+ /// <value>The user data repository.</value>
+ public IUserDataManager UserDataRepository { get; set; }
+
+ /// <summary>
+ /// Gets or sets the user manager.
+ /// </summary>
+ /// <value>The user manager.</value>
+ public IUserManager UserManager { get; set; }
+
+ /// <summary>
/// Compares the specified x.
/// </summary>
/// <param name="x">The x.</param>
@@ -33,23 +54,5 @@ namespace Emby.Server.Implementations.Sorting
{
return x.IsUnplayed(User) ? 0 : 1;
}
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name => ItemSortBy.IsUnplayed;
-
- /// <summary>
- /// Gets or sets the user data repository.
- /// </summary>
- /// <value>The user data repository.</value>
- public IUserDataManager UserDataRepository { get; set; }
-
- /// <summary>
- /// Gets or sets the user manager.
- /// </summary>
- /// <value>The user manager.</value>
- public IUserManager UserManager { get; set; }
}
}
diff --git a/Emby.Server.Implementations/Sorting/OfficialRatingComparer.cs b/Emby.Server.Implementations/Sorting/OfficialRatingComparer.cs
index 7afbd9ff7..76bb798b5 100644
--- a/Emby.Server.Implementations/Sorting/OfficialRatingComparer.cs
+++ b/Emby.Server.Implementations/Sorting/OfficialRatingComparer.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Sorting;
@@ -16,6 +18,12 @@ namespace Emby.Server.Implementations.Sorting
}
/// <summary>
+ /// Gets the name.
+ /// </summary>
+ /// <value>The name.</value>
+ public string Name => ItemSortBy.OfficialRating;
+
+ /// <summary>
/// Compares the specified x.
/// </summary>
/// <param name="x">The x.</param>
@@ -38,11 +46,5 @@ namespace Emby.Server.Implementations.Sorting
return levelX.CompareTo(levelY);
}
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name => ItemSortBy.OfficialRating;
}
}
diff --git a/Emby.Server.Implementations/Sorting/PlayCountComparer.cs b/Emby.Server.Implementations/Sorting/PlayCountComparer.cs
index eb74ce1bd..afbaaf6ab 100644
--- a/Emby.Server.Implementations/Sorting/PlayCountComparer.cs
+++ b/Emby.Server.Implementations/Sorting/PlayCountComparer.cs
@@ -1,3 +1,4 @@
+using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Sorting;
diff --git a/Emby.Server.Implementations/Sorting/SeriesSortNameComparer.cs b/Emby.Server.Implementations/Sorting/SeriesSortNameComparer.cs
index 504b6d283..b9205ee07 100644
--- a/Emby.Server.Implementations/Sorting/SeriesSortNameComparer.cs
+++ b/Emby.Server.Implementations/Sorting/SeriesSortNameComparer.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Sorting;
@@ -8,6 +10,12 @@ namespace Emby.Server.Implementations.Sorting
public class SeriesSortNameComparer : IBaseItemComparer
{
/// <summary>
+ /// Gets the name.
+ /// </summary>
+ /// <value>The name.</value>
+ public string Name => ItemSortBy.SeriesSortName;
+
+ /// <summary>
/// Compares the specified x.
/// </summary>
/// <param name="x">The x.</param>
@@ -18,12 +26,6 @@ namespace Emby.Server.Implementations.Sorting
return string.Compare(GetValue(x), GetValue(y), StringComparison.CurrentCultureIgnoreCase);
}
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name => ItemSortBy.SeriesSortName;
-
private static string GetValue(BaseItem item)
{
var hasSeries = item as IHasSeries;
diff --git a/Emby.Server.Implementations/Sorting/StartDateComparer.cs b/Emby.Server.Implementations/Sorting/StartDateComparer.cs
index aa040fa15..558a3d351 100644
--- a/Emby.Server.Implementations/Sorting/StartDateComparer.cs
+++ b/Emby.Server.Implementations/Sorting/StartDateComparer.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.LiveTv;
@@ -9,6 +11,12 @@ namespace Emby.Server.Implementations.Sorting
public class StartDateComparer : IBaseItemComparer
{
/// <summary>
+ /// Gets the name.
+ /// </summary>
+ /// <value>The name.</value>
+ public string Name => ItemSortBy.StartDate;
+
+ /// <summary>
/// Compares the specified x.
/// </summary>
/// <param name="x">The x.</param>
@@ -26,19 +34,12 @@ namespace Emby.Server.Implementations.Sorting
/// <returns>DateTime.</returns>
private static DateTime GetDate(BaseItem x)
{
- var hasStartDate = x as LiveTvProgram;
-
- if (hasStartDate != null)
+ if (x is LiveTvProgram hasStartDate)
{
return hasStartDate.StartDate;
}
+
return DateTime.MinValue;
}
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name => ItemSortBy.StartDate;
}
}
diff --git a/Emby.Server.Implementations/Sorting/StudioComparer.cs b/Emby.Server.Implementations/Sorting/StudioComparer.cs
index c9ac765c1..5766dc542 100644
--- a/Emby.Server.Implementations/Sorting/StudioComparer.cs
+++ b/Emby.Server.Implementations/Sorting/StudioComparer.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Linq;
using MediaBrowser.Controller.Entities;
diff --git a/Emby.Server.Implementations/SyncPlay/SyncPlayController.cs b/Emby.Server.Implementations/SyncPlay/SyncPlayController.cs
new file mode 100644
index 000000000..d0812a13f
--- /dev/null
+++ b/Emby.Server.Implementations/SyncPlay/SyncPlayController.cs
@@ -0,0 +1,522 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Controller.Session;
+using MediaBrowser.Controller.SyncPlay;
+using MediaBrowser.Model.Session;
+using MediaBrowser.Model.SyncPlay;
+
+namespace Emby.Server.Implementations.SyncPlay
+{
+ /// <summary>
+ /// Class SyncPlayController.
+ /// </summary>
+ /// <remarks>
+ /// Class is not thread-safe, external locking is required when accessing methods.
+ /// </remarks>
+ public class SyncPlayController : ISyncPlayController
+ {
+ /// <summary>
+ /// Used to filter the sessions of a group.
+ /// </summary>
+ private enum BroadcastType
+ {
+ /// <summary>
+ /// All sessions will receive the message.
+ /// </summary>
+ AllGroup = 0,
+ /// <summary>
+ /// Only the specified session will receive the message.
+ /// </summary>
+ CurrentSession = 1,
+ /// <summary>
+ /// All sessions, except the current one, will receive the message.
+ /// </summary>
+ AllExceptCurrentSession = 2,
+ /// <summary>
+ /// Only sessions that are not buffering will receive the message.
+ /// </summary>
+ AllReady = 3
+ }
+
+ /// <summary>
+ /// The session manager.
+ /// </summary>
+ private readonly ISessionManager _sessionManager;
+
+ /// <summary>
+ /// The SyncPlay manager.
+ /// </summary>
+ private readonly ISyncPlayManager _syncPlayManager;
+
+ /// <summary>
+ /// The group to manage.
+ /// </summary>
+ private readonly GroupInfo _group = new GroupInfo();
+
+ /// <inheritdoc />
+ public Guid GetGroupId() => _group.GroupId;
+
+ /// <inheritdoc />
+ public Guid GetPlayingItemId() => _group.PlayingItem.Id;
+
+ /// <inheritdoc />
+ public bool IsGroupEmpty() => _group.IsEmpty();
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SyncPlayController" /> class.
+ /// </summary>
+ /// <param name="sessionManager">The session manager.</param>
+ /// <param name="syncPlayManager">The SyncPlay manager.</param>
+ public SyncPlayController(
+ ISessionManager sessionManager,
+ ISyncPlayManager syncPlayManager)
+ {
+ _sessionManager = sessionManager;
+ _syncPlayManager = syncPlayManager;
+ }
+
+ /// <summary>
+ /// Converts DateTime to UTC string.
+ /// </summary>
+ /// <param name="date">The date to convert.</param>
+ /// <value>The UTC string.</value>
+ private string DateToUTCString(DateTime date)
+ {
+ return date.ToUniversalTime().ToString("o");
+ }
+
+ /// <summary>
+ /// Filters sessions of this group.
+ /// </summary>
+ /// <param name="from">The current session.</param>
+ /// <param name="type">The filtering type.</param>
+ /// <value>The array of sessions matching the filter.</value>
+ private SessionInfo[] FilterSessions(SessionInfo from, BroadcastType type)
+ {
+ switch (type)
+ {
+ case BroadcastType.CurrentSession:
+ return new SessionInfo[] { from };
+ case BroadcastType.AllGroup:
+ return _group.Participants.Values.Select(
+ session => session.Session
+ ).ToArray();
+ case BroadcastType.AllExceptCurrentSession:
+ return _group.Participants.Values.Select(
+ session => session.Session
+ ).Where(
+ session => !session.Id.Equals(from.Id)
+ ).ToArray();
+ case BroadcastType.AllReady:
+ return _group.Participants.Values.Where(
+ session => !session.IsBuffering
+ ).Select(
+ session => session.Session
+ ).ToArray();
+ default:
+ return Array.Empty<SessionInfo>();
+ }
+ }
+
+ /// <summary>
+ /// Sends a GroupUpdate message to the interested sessions.
+ /// </summary>
+ /// <param name="from">The current session.</param>
+ /// <param name="type">The filtering type.</param>
+ /// <param name="message">The message to send.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <value>The task.</value>
+ private Task SendGroupUpdate<T>(SessionInfo from, BroadcastType type, GroupUpdate<T> message, CancellationToken cancellationToken)
+ {
+ IEnumerable<Task> GetTasks()
+ {
+ SessionInfo[] sessions = FilterSessions(from, type);
+ foreach (var session in sessions)
+ {
+ yield return _sessionManager.SendSyncPlayGroupUpdate(session.Id.ToString(), message, cancellationToken);
+ }
+ }
+
+ return Task.WhenAll(GetTasks());
+ }
+
+ /// <summary>
+ /// Sends a playback command to the interested sessions.
+ /// </summary>
+ /// <param name="from">The current session.</param>
+ /// <param name="type">The filtering type.</param>
+ /// <param name="message">The message to send.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <value>The task.</value>
+ private Task SendCommand(SessionInfo from, BroadcastType type, SendCommand message, CancellationToken cancellationToken)
+ {
+ IEnumerable<Task> GetTasks()
+ {
+ SessionInfo[] sessions = FilterSessions(from, type);
+ foreach (var session in sessions)
+ {
+ yield return _sessionManager.SendSyncPlayCommand(session.Id.ToString(), message, cancellationToken);
+ }
+ }
+
+ return Task.WhenAll(GetTasks());
+ }
+
+ /// <summary>
+ /// Builds a new playback command with some default values.
+ /// </summary>
+ /// <param name="type">The command type.</param>
+ /// <value>The SendCommand.</value>
+ private SendCommand NewSyncPlayCommand(SendCommandType type)
+ {
+ return new SendCommand()
+ {
+ GroupId = _group.GroupId.ToString(),
+ Command = type,
+ PositionTicks = _group.PositionTicks,
+ When = DateToUTCString(_group.LastActivity),
+ EmittedAt = DateToUTCString(DateTime.UtcNow)
+ };
+ }
+
+ /// <summary>
+ /// Builds a new group update message.
+ /// </summary>
+ /// <param name="type">The update type.</param>
+ /// <param name="data">The data to send.</param>
+ /// <value>The GroupUpdate.</value>
+ private GroupUpdate<T> NewSyncPlayGroupUpdate<T>(GroupUpdateType type, T data)
+ {
+ return new GroupUpdate<T>()
+ {
+ GroupId = _group.GroupId.ToString(),
+ Type = type,
+ Data = data
+ };
+ }
+
+ /// <inheritdoc />
+ public void InitGroup(SessionInfo session, CancellationToken cancellationToken)
+ {
+ _group.AddSession(session);
+ _syncPlayManager.AddSessionToGroup(session, this);
+
+ _group.PlayingItem = session.FullNowPlayingItem;
+ _group.IsPaused = true;
+ _group.PositionTicks = session.PlayState.PositionTicks ?? 0;
+ _group.LastActivity = DateTime.UtcNow;
+
+ var updateSession = NewSyncPlayGroupUpdate(GroupUpdateType.GroupJoined, DateToUTCString(DateTime.UtcNow));
+ SendGroupUpdate(session, BroadcastType.CurrentSession, updateSession, cancellationToken);
+ var pauseCommand = NewSyncPlayCommand(SendCommandType.Pause);
+ SendCommand(session, BroadcastType.CurrentSession, pauseCommand, cancellationToken);
+ }
+
+ /// <inheritdoc />
+ public void SessionJoin(SessionInfo session, JoinGroupRequest request, CancellationToken cancellationToken)
+ {
+ if (session.NowPlayingItem?.Id == _group.PlayingItem.Id && request.PlayingItemId == _group.PlayingItem.Id)
+ {
+ _group.AddSession(session);
+ _syncPlayManager.AddSessionToGroup(session, this);
+
+ var updateSession = NewSyncPlayGroupUpdate(GroupUpdateType.GroupJoined, DateToUTCString(DateTime.UtcNow));
+ SendGroupUpdate(session, BroadcastType.CurrentSession, updateSession, cancellationToken);
+
+ var updateOthers = NewSyncPlayGroupUpdate(GroupUpdateType.UserJoined, session.UserName);
+ SendGroupUpdate(session, BroadcastType.AllExceptCurrentSession, updateOthers, cancellationToken);
+
+ // Client join and play, syncing will happen client side
+ if (!_group.IsPaused)
+ {
+ var playCommand = NewSyncPlayCommand(SendCommandType.Play);
+ SendCommand(session, BroadcastType.CurrentSession, playCommand, cancellationToken);
+ }
+ else
+ {
+ var pauseCommand = NewSyncPlayCommand(SendCommandType.Pause);
+ SendCommand(session, BroadcastType.CurrentSession, pauseCommand, cancellationToken);
+ }
+ }
+ else
+ {
+ var playRequest = new PlayRequest();
+ playRequest.ItemIds = new Guid[] { _group.PlayingItem.Id };
+ playRequest.StartPositionTicks = _group.PositionTicks;
+ var update = NewSyncPlayGroupUpdate(GroupUpdateType.PrepareSession, playRequest);
+ SendGroupUpdate(session, BroadcastType.CurrentSession, update, cancellationToken);
+ }
+ }
+
+ /// <inheritdoc />
+ public void SessionLeave(SessionInfo session, CancellationToken cancellationToken)
+ {
+ _group.RemoveSession(session);
+ _syncPlayManager.RemoveSessionFromGroup(session, this);
+
+ var updateSession = NewSyncPlayGroupUpdate(GroupUpdateType.GroupLeft, _group.PositionTicks);
+ SendGroupUpdate(session, BroadcastType.CurrentSession, updateSession, cancellationToken);
+
+ var updateOthers = NewSyncPlayGroupUpdate(GroupUpdateType.UserLeft, session.UserName);
+ SendGroupUpdate(session, BroadcastType.AllExceptCurrentSession, updateOthers, cancellationToken);
+ }
+
+ /// <inheritdoc />
+ public void HandleRequest(SessionInfo session, PlaybackRequest request, CancellationToken cancellationToken)
+ {
+ // The server's job is to mantain a consistent state to which clients refer to,
+ // as also to notify clients of state changes.
+ // The actual syncing of media playback happens client side.
+ // Clients are aware of the server's time and use it to sync.
+ switch (request.Type)
+ {
+ case PlaybackRequestType.Play:
+ HandlePlayRequest(session, request, cancellationToken);
+ break;
+ case PlaybackRequestType.Pause:
+ HandlePauseRequest(session, request, cancellationToken);
+ break;
+ case PlaybackRequestType.Seek:
+ HandleSeekRequest(session, request, cancellationToken);
+ break;
+ case PlaybackRequestType.Buffering:
+ HandleBufferingRequest(session, request, cancellationToken);
+ break;
+ case PlaybackRequestType.BufferingDone:
+ HandleBufferingDoneRequest(session, request, cancellationToken);
+ break;
+ case PlaybackRequestType.UpdatePing:
+ HandlePingUpdateRequest(session, request);
+ break;
+ }
+ }
+
+ /// <summary>
+ /// Handles a play action requested by a session.
+ /// </summary>
+ /// <param name="session">The session.</param>
+ /// <param name="request">The play action.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ private void HandlePlayRequest(SessionInfo session, PlaybackRequest request, CancellationToken cancellationToken)
+ {
+ if (_group.IsPaused)
+ {
+ // Pick a suitable time that accounts for latency
+ var delay = _group.GetHighestPing() * 2;
+ delay = delay < _group.DefaulPing ? _group.DefaulPing : delay;
+
+ // Unpause group and set starting point in future
+ // Clients will start playback at LastActivity (datetime) from PositionTicks (playback position)
+ // The added delay does not guarantee, of course, that the command will be received in time
+ // Playback synchronization will mainly happen client side
+ _group.IsPaused = false;
+ _group.LastActivity = DateTime.UtcNow.AddMilliseconds(
+ delay
+ );
+
+ var command = NewSyncPlayCommand(SendCommandType.Play);
+ SendCommand(session, BroadcastType.AllGroup, command, cancellationToken);
+ }
+ else
+ {
+ // Client got lost, sending current state
+ var command = NewSyncPlayCommand(SendCommandType.Play);
+ SendCommand(session, BroadcastType.CurrentSession, command, cancellationToken);
+ }
+ }
+
+ /// <summary>
+ /// Handles a pause action requested by a session.
+ /// </summary>
+ /// <param name="session">The session.</param>
+ /// <param name="request">The pause action.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ private void HandlePauseRequest(SessionInfo session, PlaybackRequest request, CancellationToken cancellationToken)
+ {
+ if (!_group.IsPaused)
+ {
+ // Pause group and compute the media playback position
+ _group.IsPaused = true;
+ var currentTime = DateTime.UtcNow;
+ var elapsedTime = currentTime - _group.LastActivity;
+ _group.LastActivity = currentTime;
+ // Seek only if playback actually started
+ // (a pause request may be issued during the delay added to account for latency)
+ _group.PositionTicks += elapsedTime.Ticks > 0 ? elapsedTime.Ticks : 0;
+
+ var command = NewSyncPlayCommand(SendCommandType.Pause);
+ SendCommand(session, BroadcastType.AllGroup, command, cancellationToken);
+ }
+ else
+ {
+ // Client got lost, sending current state
+ var command = NewSyncPlayCommand(SendCommandType.Pause);
+ SendCommand(session, BroadcastType.CurrentSession, command, cancellationToken);
+ }
+ }
+
+ /// <summary>
+ /// Handles a seek action requested by a session.
+ /// </summary>
+ /// <param name="session">The session.</param>
+ /// <param name="request">The seek action.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ private void HandleSeekRequest(SessionInfo session, PlaybackRequest request, CancellationToken cancellationToken)
+ {
+ // Sanitize PositionTicks
+ var ticks = SanitizePositionTicks(request.PositionTicks);
+
+ // Pause and seek
+ _group.IsPaused = true;
+ _group.PositionTicks = ticks;
+ _group.LastActivity = DateTime.UtcNow;
+
+ var command = NewSyncPlayCommand(SendCommandType.Seek);
+ SendCommand(session, BroadcastType.AllGroup, command, cancellationToken);
+ }
+
+ /// <summary>
+ /// Handles a buffering action requested by a session.
+ /// </summary>
+ /// <param name="session">The session.</param>
+ /// <param name="request">The buffering action.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ private void HandleBufferingRequest(SessionInfo session, PlaybackRequest request, CancellationToken cancellationToken)
+ {
+ if (!_group.IsPaused)
+ {
+ // Pause group and compute the media playback position
+ _group.IsPaused = true;
+ var currentTime = DateTime.UtcNow;
+ var elapsedTime = currentTime - _group.LastActivity;
+ _group.LastActivity = currentTime;
+ _group.PositionTicks += elapsedTime.Ticks > 0 ? elapsedTime.Ticks : 0;
+
+ _group.SetBuffering(session, true);
+
+ // Send pause command to all non-buffering sessions
+ var command = NewSyncPlayCommand(SendCommandType.Pause);
+ SendCommand(session, BroadcastType.AllReady, command, cancellationToken);
+
+ var updateOthers = NewSyncPlayGroupUpdate(GroupUpdateType.GroupWait, session.UserName);
+ SendGroupUpdate(session, BroadcastType.AllExceptCurrentSession, updateOthers, cancellationToken);
+ }
+ else
+ {
+ // Client got lost, sending current state
+ var command = NewSyncPlayCommand(SendCommandType.Pause);
+ SendCommand(session, BroadcastType.CurrentSession, command, cancellationToken);
+ }
+ }
+
+ /// <summary>
+ /// Handles a buffering-done action requested by a session.
+ /// </summary>
+ /// <param name="session">The session.</param>
+ /// <param name="request">The buffering-done action.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ private void HandleBufferingDoneRequest(SessionInfo session, PlaybackRequest request, CancellationToken cancellationToken)
+ {
+ if (_group.IsPaused)
+ {
+ _group.SetBuffering(session, false);
+
+ var requestTicks = SanitizePositionTicks(request.PositionTicks);
+
+ var when = request.When ?? DateTime.UtcNow;
+ var currentTime = DateTime.UtcNow;
+ var elapsedTime = currentTime - when;
+ var clientPosition = TimeSpan.FromTicks(requestTicks) + elapsedTime;
+ var delay = _group.PositionTicks - clientPosition.Ticks;
+
+ if (_group.IsBuffering())
+ {
+ // Others are still buffering, tell this client to pause when ready
+ var command = NewSyncPlayCommand(SendCommandType.Pause);
+ var pauseAtTime = currentTime.AddMilliseconds(delay);
+ command.When = DateToUTCString(pauseAtTime);
+ SendCommand(session, BroadcastType.CurrentSession, command, cancellationToken);
+ }
+ else
+ {
+ // Let other clients resume as soon as the buffering client catches up
+ _group.IsPaused = false;
+
+ if (delay > _group.GetHighestPing() * 2)
+ {
+ // Client that was buffering is recovering, notifying others to resume
+ _group.LastActivity = currentTime.AddMilliseconds(
+ delay
+ );
+ var command = NewSyncPlayCommand(SendCommandType.Play);
+ SendCommand(session, BroadcastType.AllExceptCurrentSession, command, cancellationToken);
+ }
+ else
+ {
+ // Client, that was buffering, resumed playback but did not update others in time
+ delay = _group.GetHighestPing() * 2;
+ delay = delay < _group.DefaulPing ? _group.DefaulPing : delay;
+
+ _group.LastActivity = currentTime.AddMilliseconds(
+ delay
+ );
+
+ var command = NewSyncPlayCommand(SendCommandType.Play);
+ SendCommand(session, BroadcastType.AllGroup, command, cancellationToken);
+ }
+ }
+ }
+ else
+ {
+ // Group was not waiting, make sure client has latest state
+ var command = NewSyncPlayCommand(SendCommandType.Play);
+ SendCommand(session, BroadcastType.CurrentSession, command, cancellationToken);
+ }
+ }
+
+ /// <summary>
+ /// Sanitizes the PositionTicks, considers the current playing item when available.
+ /// </summary>
+ /// <param name="positionTicks">The PositionTicks.</param>
+ /// <value>The sanitized PositionTicks.</value>
+ private long SanitizePositionTicks(long? positionTicks)
+ {
+ var ticks = positionTicks ?? 0;
+ ticks = ticks >= 0 ? ticks : 0;
+ if (_group.PlayingItem != null)
+ {
+ var runTimeTicks = _group.PlayingItem.RunTimeTicks ?? 0;
+ ticks = ticks > runTimeTicks ? runTimeTicks : ticks;
+ }
+
+ return ticks;
+ }
+
+ /// <summary>
+ /// Updates ping of a session.
+ /// </summary>
+ /// <param name="session">The session.</param>
+ /// <param name="request">The update.</param>
+ private void HandlePingUpdateRequest(SessionInfo session, PlaybackRequest request)
+ {
+ // Collected pings are used to account for network latency when unpausing playback
+ _group.UpdatePing(session, request.Ping ?? _group.DefaulPing);
+ }
+
+ /// <inheritdoc />
+ public GroupInfoView GetInfo()
+ {
+ return new GroupInfoView()
+ {
+ GroupId = GetGroupId().ToString(),
+ PlayingItemName = _group.PlayingItem.Name,
+ PlayingItemId = _group.PlayingItem.Id.ToString(),
+ PositionTicks = _group.PositionTicks,
+ Participants = _group.Participants.Values.Select(session => session.Session.UserName).Distinct().ToList()
+ };
+ }
+ }
+}
diff --git a/Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs b/Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs
new file mode 100644
index 000000000..ecc3eeb39
--- /dev/null
+++ b/Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs
@@ -0,0 +1,380 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Threading;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Enums;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Session;
+using MediaBrowser.Controller.SyncPlay;
+using MediaBrowser.Model.SyncPlay;
+using Microsoft.Extensions.Logging;
+
+namespace Emby.Server.Implementations.SyncPlay
+{
+ /// <summary>
+ /// Class SyncPlayManager.
+ /// </summary>
+ public class SyncPlayManager : ISyncPlayManager, IDisposable
+ {
+ /// <summary>
+ /// The logger.
+ /// </summary>
+ private readonly ILogger<SyncPlayManager> _logger;
+
+ /// <summary>
+ /// The user manager.
+ /// </summary>
+ private readonly IUserManager _userManager;
+
+ /// <summary>
+ /// The session manager.
+ /// </summary>
+ private readonly ISessionManager _sessionManager;
+
+ /// <summary>
+ /// The library manager.
+ /// </summary>
+ private readonly ILibraryManager _libraryManager;
+
+ /// <summary>
+ /// The map between sessions and groups.
+ /// </summary>
+ private readonly Dictionary<string, ISyncPlayController> _sessionToGroupMap =
+ new Dictionary<string, ISyncPlayController>(StringComparer.OrdinalIgnoreCase);
+
+ /// <summary>
+ /// The groups.
+ /// </summary>
+ private readonly Dictionary<Guid, ISyncPlayController> _groups =
+ new Dictionary<Guid, ISyncPlayController>();
+
+ /// <summary>
+ /// Lock used for accesing any group.
+ /// </summary>
+ private readonly object _groupsLock = new object();
+
+ private bool _disposed = false;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SyncPlayManager" /> class.
+ /// </summary>
+ /// <param name="logger">The logger.</param>
+ /// <param name="userManager">The user manager.</param>
+ /// <param name="sessionManager">The session manager.</param>
+ /// <param name="libraryManager">The library manager.</param>
+ public SyncPlayManager(
+ ILogger<SyncPlayManager> logger,
+ IUserManager userManager,
+ ISessionManager sessionManager,
+ ILibraryManager libraryManager)
+ {
+ _logger = logger;
+ _userManager = userManager;
+ _sessionManager = sessionManager;
+ _libraryManager = libraryManager;
+
+ _sessionManager.SessionEnded += OnSessionManagerSessionEnded;
+ _sessionManager.PlaybackStopped += OnSessionManagerPlaybackStopped;
+ }
+
+ /// <summary>
+ /// Gets all groups.
+ /// </summary>
+ /// <value>All groups.</value>
+ public IEnumerable<ISyncPlayController> Groups => _groups.Values;
+
+ /// <inheritdoc />
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ /// <summary>
+ /// Releases unmanaged and optionally managed resources.
+ /// </summary>
+ /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
+ protected virtual void Dispose(bool disposing)
+ {
+ if (_disposed)
+ {
+ return;
+ }
+
+ _sessionManager.SessionEnded -= OnSessionManagerSessionEnded;
+ _sessionManager.PlaybackStopped -= OnSessionManagerPlaybackStopped;
+
+ _disposed = true;
+ }
+
+ private void OnSessionManagerSessionEnded(object sender, SessionEventArgs e)
+ {
+ var session = e.SessionInfo;
+ if (!IsSessionInGroup(session))
+ {
+ return;
+ }
+
+ LeaveGroup(session, CancellationToken.None);
+ }
+
+ private void OnSessionManagerPlaybackStopped(object sender, PlaybackStopEventArgs e)
+ {
+ var session = e.Session;
+ if (!IsSessionInGroup(session))
+ {
+ return;
+ }
+
+ LeaveGroup(session, CancellationToken.None);
+ }
+
+ private bool IsSessionInGroup(SessionInfo session)
+ {
+ return _sessionToGroupMap.ContainsKey(session.Id);
+ }
+
+ private bool HasAccessToItem(User user, Guid itemId)
+ {
+ var item = _libraryManager.GetItemById(itemId);
+
+ // Check ParentalRating access
+ var hasParentalRatingAccess = !user.MaxParentalAgeRating.HasValue
+ || item.InheritedParentalRatingValue <= user.MaxParentalAgeRating;
+
+ if (!user.HasPermission(PermissionKind.EnableAllFolders) && hasParentalRatingAccess)
+ {
+ var collections = _libraryManager.GetCollectionFolders(item).Select(
+ folder => folder.Id.ToString("N", CultureInfo.InvariantCulture));
+
+ return collections.Intersect(user.GetPreference(PreferenceKind.EnabledFolders)).Any();
+ }
+
+ return hasParentalRatingAccess;
+ }
+
+ private Guid? GetSessionGroup(SessionInfo session)
+ {
+ _sessionToGroupMap.TryGetValue(session.Id, out var group);
+ return group?.GetGroupId();
+ }
+
+ /// <inheritdoc />
+ public void NewGroup(SessionInfo session, CancellationToken cancellationToken)
+ {
+ var user = _userManager.GetUserById(session.UserId);
+
+ if (user.SyncPlayAccess != SyncPlayAccess.CreateAndJoinGroups)
+ {
+ _logger.LogWarning("NewGroup: {0} does not have permission to create groups.", session.Id);
+
+ var error = new GroupUpdate<string>()
+ {
+ Type = GroupUpdateType.CreateGroupDenied
+ };
+ _sessionManager.SendSyncPlayGroupUpdate(session.Id, error, CancellationToken.None);
+ return;
+ }
+
+ lock (_groupsLock)
+ {
+ if (IsSessionInGroup(session))
+ {
+ LeaveGroup(session, cancellationToken);
+ }
+
+ var group = new SyncPlayController(_sessionManager, this);
+ _groups[group.GetGroupId()] = group;
+
+ group.InitGroup(session, cancellationToken);
+ }
+ }
+
+ /// <inheritdoc />
+ public void JoinGroup(SessionInfo session, Guid groupId, JoinGroupRequest request, CancellationToken cancellationToken)
+ {
+ var user = _userManager.GetUserById(session.UserId);
+
+ if (user.SyncPlayAccess == SyncPlayAccess.None)
+ {
+ _logger.LogWarning("JoinGroup: {0} does not have access to SyncPlay.", session.Id);
+
+ var error = new GroupUpdate<string>()
+ {
+ Type = GroupUpdateType.JoinGroupDenied
+ };
+ _sessionManager.SendSyncPlayGroupUpdate(session.Id, error, CancellationToken.None);
+ return;
+ }
+
+ lock (_groupsLock)
+ {
+ ISyncPlayController group;
+ _groups.TryGetValue(groupId, out group);
+
+ if (group == null)
+ {
+ _logger.LogWarning("JoinGroup: {0} tried to join group {0} that does not exist.", session.Id, groupId);
+
+ var error = new GroupUpdate<string>()
+ {
+ Type = GroupUpdateType.GroupDoesNotExist
+ };
+ _sessionManager.SendSyncPlayGroupUpdate(session.Id, error, CancellationToken.None);
+ return;
+ }
+
+ if (!HasAccessToItem(user, group.GetPlayingItemId()))
+ {
+ _logger.LogWarning("JoinGroup: {0} does not have access to {1}.", session.Id, group.GetPlayingItemId());
+
+ var error = new GroupUpdate<string>()
+ {
+ GroupId = group.GetGroupId().ToString(),
+ Type = GroupUpdateType.LibraryAccessDenied
+ };
+ _sessionManager.SendSyncPlayGroupUpdate(session.Id, error, CancellationToken.None);
+ return;
+ }
+
+ if (IsSessionInGroup(session))
+ {
+ if (GetSessionGroup(session).Equals(groupId))
+ {
+ return;
+ }
+
+ LeaveGroup(session, cancellationToken);
+ }
+
+ group.SessionJoin(session, request, cancellationToken);
+ }
+ }
+
+ /// <inheritdoc />
+ public void LeaveGroup(SessionInfo session, CancellationToken cancellationToken)
+ {
+ // TODO: determine what happens to users that are in a group and get their permissions revoked
+ lock (_groupsLock)
+ {
+ _sessionToGroupMap.TryGetValue(session.Id, out var group);
+
+ if (group == null)
+ {
+ _logger.LogWarning("LeaveGroup: {0} does not belong to any group.", session.Id);
+
+ var error = new GroupUpdate<string>()
+ {
+ Type = GroupUpdateType.NotInGroup
+ };
+ _sessionManager.SendSyncPlayGroupUpdate(session.Id, error, CancellationToken.None);
+ return;
+ }
+
+ group.SessionLeave(session, cancellationToken);
+
+ if (group.IsGroupEmpty())
+ {
+ _logger.LogInformation("LeaveGroup: removing empty group {0}.", group.GetGroupId());
+ _groups.Remove(group.GetGroupId(), out _);
+ }
+ }
+ }
+
+ /// <inheritdoc />
+ public List<GroupInfoView> ListGroups(SessionInfo session, Guid filterItemId)
+ {
+ var user = _userManager.GetUserById(session.UserId);
+
+ if (user.SyncPlayAccess == SyncPlayAccess.None)
+ {
+ return new List<GroupInfoView>();
+ }
+
+ // Filter by item if requested
+ if (!filterItemId.Equals(Guid.Empty))
+ {
+ return _groups.Values.Where(
+ group => group.GetPlayingItemId().Equals(filterItemId) && HasAccessToItem(user, group.GetPlayingItemId())
+ ).Select(
+ group => group.GetInfo()
+ ).ToList();
+ }
+ // Otherwise show all available groups
+ else
+ {
+ return _groups.Values.Where(
+ group => HasAccessToItem(user, group.GetPlayingItemId())
+ ).Select(
+ group => group.GetInfo()
+ ).ToList();
+ }
+ }
+
+ /// <inheritdoc />
+ public void HandleRequest(SessionInfo session, PlaybackRequest request, CancellationToken cancellationToken)
+ {
+ var user = _userManager.GetUserById(session.UserId);
+
+ if (user.SyncPlayAccess == SyncPlayAccess.None)
+ {
+ _logger.LogWarning("HandleRequest: {0} does not have access to SyncPlay.", session.Id);
+
+ var error = new GroupUpdate<string>()
+ {
+ Type = GroupUpdateType.JoinGroupDenied
+ };
+ _sessionManager.SendSyncPlayGroupUpdate(session.Id, error, CancellationToken.None);
+ return;
+ }
+
+ lock (_groupsLock)
+ {
+ _sessionToGroupMap.TryGetValue(session.Id, out var group);
+
+ if (group == null)
+ {
+ _logger.LogWarning("HandleRequest: {0} does not belong to any group.", session.Id);
+
+ var error = new GroupUpdate<string>()
+ {
+ Type = GroupUpdateType.NotInGroup
+ };
+ _sessionManager.SendSyncPlayGroupUpdate(session.Id, error, CancellationToken.None);
+ return;
+ }
+
+ group.HandleRequest(session, request, cancellationToken);
+ }
+ }
+
+ /// <inheritdoc />
+ public void AddSessionToGroup(SessionInfo session, ISyncPlayController group)
+ {
+ if (IsSessionInGroup(session))
+ {
+ throw new InvalidOperationException("Session in other group already!");
+ }
+
+ _sessionToGroupMap[session.Id] = group;
+ }
+
+ /// <inheritdoc />
+ public void RemoveSessionFromGroup(SessionInfo session, ISyncPlayController group)
+ {
+ if (!IsSessionInGroup(session))
+ {
+ throw new InvalidOperationException("Session not in any group!");
+ }
+
+ _sessionToGroupMap.Remove(session.Id, out var tempGroup);
+
+ if (!tempGroup.GetGroupId().Equals(group.GetGroupId()))
+ {
+ throw new InvalidOperationException("Session was in wrong group!");
+ }
+ }
+ }
+}
diff --git a/Emby.Server.Implementations/TV/TVSeriesManager.cs b/Emby.Server.Implementations/TV/TVSeriesManager.cs
index 4c2f24e6f..6999668d1 100644
--- a/Emby.Server.Implementations/TV/TVSeriesManager.cs
+++ b/Emby.Server.Implementations/TV/TVSeriesManager.cs
@@ -1,15 +1,20 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.TV;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
+using Episode = MediaBrowser.Controller.Entities.TV.Episode;
+using Series = MediaBrowser.Controller.Entities.TV.Series;
namespace Emby.Server.Implementations.TV
{
@@ -18,14 +23,12 @@ namespace Emby.Server.Implementations.TV
private readonly IUserManager _userManager;
private readonly IUserDataManager _userDataManager;
private readonly ILibraryManager _libraryManager;
- private readonly IServerConfigurationManager _config;
- public TVSeriesManager(IUserManager userManager, IUserDataManager userDataManager, ILibraryManager libraryManager, IServerConfigurationManager config)
+ public TVSeriesManager(IUserManager userManager, IUserDataManager userDataManager, ILibraryManager libraryManager)
{
_userManager = userManager;
_userDataManager = userDataManager;
_libraryManager = libraryManager;
- _config = config;
}
public QueryResult<BaseItem> GetNextUp(NextUpQuery request, DtoOptions dtoOptions)
@@ -74,7 +77,8 @@ namespace Emby.Server.Implementations.TV
{
parents = _libraryManager.GetUserRootFolder().GetChildren(user, true)
.Where(i => i is Folder)
- .Where(i => !user.Configuration.LatestItemsExcludes.Contains(i.Id.ToString("N", CultureInfo.InvariantCulture)))
+ .Where(i => !user.GetPreference(PreferenceKind.LatestItemExcludes)
+ .Contains(i.Id.ToString("N", CultureInfo.InvariantCulture)))
.ToArray();
}
@@ -192,7 +196,7 @@ namespace Emby.Server.Implementations.TV
{
AncestorWithPresentationUniqueKey = null,
SeriesPresentationUniqueKey = seriesKey,
- IncludeItemTypes = new[] { typeof(Episode).Name },
+ IncludeItemTypes = new[] { nameof(Episode) },
OrderBy = new[] { new ValueTuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Descending) },
IsPlayed = true,
Limit = 1,
@@ -205,7 +209,6 @@ namespace Emby.Server.Implementations.TV
},
EnableImages = false
}
-
}).FirstOrDefault();
Func<Episode> getEpisode = () =>
@@ -220,7 +223,7 @@ namespace Emby.Server.Implementations.TV
IsPlayed = false,
IsVirtualItem = false,
ParentIndexNumberNotEquals = 0,
- MinSortName = lastWatchedEpisode == null ? null : lastWatchedEpisode.SortName,
+ MinSortName = lastWatchedEpisode?.SortName,
DtoOptions = dtoOptions
}).Cast<Episode>().FirstOrDefault();
diff --git a/Emby.Server.Implementations/Udp/UdpServer.cs b/Emby.Server.Implementations/Udp/UdpServer.cs
index c91d137a7..a26f714b1 100644
--- a/Emby.Server.Implementations/Udp/UdpServer.cs
+++ b/Emby.Server.Implementations/Udp/UdpServer.cs
@@ -7,6 +7,7 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller;
using MediaBrowser.Model.ApiClient;
+using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Udp
@@ -21,6 +22,12 @@ namespace Emby.Server.Implementations.Udp
/// </summary>
private readonly ILogger _logger;
private readonly IServerApplicationHost _appHost;
+ private readonly IConfiguration _config;
+
+ /// <summary>
+ /// Address Override Configuration Key.
+ /// </summary>
+ public const string AddressOverrideConfigKey = "PublishedServerUrl";
private Socket _udpSocket;
private IPEndPoint _endpoint;
@@ -31,15 +38,18 @@ namespace Emby.Server.Implementations.Udp
/// <summary>
/// Initializes a new instance of the <see cref="UdpServer" /> class.
/// </summary>
- public UdpServer(ILogger logger, IServerApplicationHost appHost)
+ public UdpServer(ILogger logger, IServerApplicationHost appHost, IConfiguration configuration)
{
_logger = logger;
_appHost = appHost;
+ _config = configuration;
}
private async Task RespondToV2Message(string messageText, EndPoint endpoint, CancellationToken cancellationToken)
{
- var localUrl = await _appHost.GetLocalApiUrl(cancellationToken).ConfigureAwait(false);
+ string localUrl = !string.IsNullOrEmpty(_config[AddressOverrideConfigKey])
+ ? _config[AddressOverrideConfigKey]
+ : await _appHost.GetLocalApiUrl(cancellationToken).ConfigureAwait(false);
if (!string.IsNullOrEmpty(localUrl))
{
@@ -105,7 +115,7 @@ namespace Emby.Server.Implementations.Udp
}
catch (SocketException ex)
{
- _logger.LogError(ex, "Failed to receive data drom socket");
+ _logger.LogError(ex, "Failed to receive data from socket");
}
catch (OperationCanceledException)
{
diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs
index 0b2309889..80326fddf 100644
--- a/Emby.Server.Implementations/Updates/InstallationManager.cs
+++ b/Emby.Server.Implementations/Updates/InstallationManager.cs
@@ -1,11 +1,12 @@
+#pragma warning disable CS1591
+
using System;
+using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using System.Net;
using System.Net.Http;
-using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Security.Cryptography;
using System.Threading;
@@ -38,7 +39,7 @@ namespace Emby.Server.Implementations.Updates
/// <summary>
/// The logger.
/// </summary>
- private readonly ILogger _logger;
+ private readonly ILogger<InstallationManager> _logger;
private readonly IApplicationPaths _appPaths;
private readonly IHttpClient _httpClient;
private readonly IJsonSerializer _jsonSerializer;
@@ -97,25 +98,25 @@ namespace Emby.Server.Implementations.Updates
}
/// <inheritdoc />
- public event EventHandler<InstallationEventArgs> PackageInstalling;
+ public event EventHandler<InstallationInfo> PackageInstalling;
/// <inheritdoc />
- public event EventHandler<InstallationEventArgs> PackageInstallationCompleted;
+ public event EventHandler<InstallationInfo> PackageInstallationCompleted;
/// <inheritdoc />
public event EventHandler<InstallationFailedEventArgs> PackageInstallationFailed;
/// <inheritdoc />
- public event EventHandler<InstallationEventArgs> PackageInstallationCancelled;
+ public event EventHandler<InstallationInfo> PackageInstallationCancelled;
/// <inheritdoc />
- public event EventHandler<GenericEventArgs<IPlugin>> PluginUninstalled;
+ public event EventHandler<IPlugin> PluginUninstalled;
/// <inheritdoc />
- public event EventHandler<GenericEventArgs<(IPlugin, VersionInfo)>> PluginUpdated;
+ public event EventHandler<InstallationInfo> PluginUpdated;
/// <inheritdoc />
- public event EventHandler<GenericEventArgs<VersionInfo>> PluginInstalled;
+ public event EventHandler<InstallationInfo> PluginInstalled;
/// <inheritdoc />
public IEnumerable<InstallationInfo> CompletedInstallations => _completedInstallationsInternal;
@@ -183,24 +184,7 @@ namespace Emby.Server.Implementations.Updates
}
/// <inheritdoc />
- public IEnumerable<VersionInfo> GetCompatibleVersions(
- IEnumerable<VersionInfo> availableVersions,
- Version minVersion = null)
- {
- var appVer = _applicationHost.ApplicationVersion;
- availableVersions = availableVersions
- .Where(x => Version.Parse(x.targetAbi) <= appVer);
-
- if (minVersion != null)
- {
- availableVersions = availableVersions.Where(x => x.version >= minVersion);
- }
-
- return availableVersions.OrderByDescending(x => x.version);
- }
-
- /// <inheritdoc />
- public IEnumerable<VersionInfo> GetCompatibleVersions(
+ public IEnumerable<InstallationInfo> GetCompatibleVersions(
IEnumerable<PackageInfo> availablePackages,
string name = null,
Guid guid = default,
@@ -211,28 +195,46 @@ namespace Emby.Server.Implementations.Updates
// Package not found in repository
if (package == null)
{
- return Enumerable.Empty<VersionInfo>();
+ yield break;
}
- return GetCompatibleVersions(
- package.versions,
- minVersion);
+ var appVer = _applicationHost.ApplicationVersion;
+ var availableVersions = package.versions
+ .Where(x => Version.Parse(x.targetAbi) <= appVer);
+
+ if (minVersion != null)
+ {
+ availableVersions = availableVersions.Where(x => new Version(x.version) >= minVersion);
+ }
+
+ foreach (var v in availableVersions.OrderByDescending(x => x.version))
+ {
+ yield return new InstallationInfo
+ {
+ Changelog = v.changelog,
+ Guid = new Guid(package.guid),
+ Name = package.name,
+ Version = new Version(v.version),
+ SourceUrl = v.sourceUrl,
+ Checksum = v.checksum
+ };
+ }
}
/// <inheritdoc />
- public async Task<IEnumerable<VersionInfo>> GetAvailablePluginUpdates(CancellationToken cancellationToken = default)
+ public async Task<IEnumerable<InstallationInfo>> GetAvailablePluginUpdates(CancellationToken cancellationToken = default)
{
var catalog = await GetAvailablePackages(cancellationToken).ConfigureAwait(false);
return GetAvailablePluginUpdates(catalog);
}
- private IEnumerable<VersionInfo> GetAvailablePluginUpdates(IReadOnlyList<PackageInfo> pluginCatalog)
+ private IEnumerable<InstallationInfo> GetAvailablePluginUpdates(IReadOnlyList<PackageInfo> pluginCatalog)
{
foreach (var plugin in _applicationHost.Plugins)
{
var compatibleversions = GetCompatibleVersions(pluginCatalog, plugin.Name, plugin.Id, plugin.Version);
- var version = compatibleversions.FirstOrDefault(y => y.version > plugin.Version);
- if (version != null && !CompletedInstallations.Any(x => string.Equals(x.Guid, version.guid, StringComparison.OrdinalIgnoreCase)))
+ var version = compatibleversions.FirstOrDefault(y => y.Version > plugin.Version);
+ if (version != null && CompletedInstallations.All(x => x.Guid != version.Guid))
{
yield return version;
}
@@ -240,23 +242,16 @@ namespace Emby.Server.Implementations.Updates
}
/// <inheritdoc />
- public async Task InstallPackage(VersionInfo package, CancellationToken cancellationToken)
+ public async Task InstallPackage(InstallationInfo package, CancellationToken cancellationToken)
{
if (package == null)
{
throw new ArgumentNullException(nameof(package));
}
- var installationInfo = new InstallationInfo
- {
- Guid = package.guid,
- Name = package.name,
- Version = package.version.ToString()
- };
-
var innerCancellationTokenSource = new CancellationTokenSource();
- var tuple = (installationInfo, innerCancellationTokenSource);
+ var tuple = (package, innerCancellationTokenSource);
// Add it to the in-progress list
lock (_currentInstallationsLock)
@@ -266,13 +261,7 @@ namespace Emby.Server.Implementations.Updates
var linkedToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, innerCancellationTokenSource.Token).Token;
- var installationEventArgs = new InstallationEventArgs
- {
- InstallationInfo = installationInfo,
- VersionInfo = package
- };
-
- PackageInstalling?.Invoke(this, installationEventArgs);
+ PackageInstalling?.Invoke(this, package);
try
{
@@ -283,9 +272,9 @@ namespace Emby.Server.Implementations.Updates
_currentInstallations.Remove(tuple);
}
- _completedInstallationsInternal.Add(installationInfo);
+ _completedInstallationsInternal.Add(package);
- PackageInstallationCompleted?.Invoke(this, installationEventArgs);
+ PackageInstallationCompleted?.Invoke(this, package);
}
catch (OperationCanceledException)
{
@@ -294,9 +283,9 @@ namespace Emby.Server.Implementations.Updates
_currentInstallations.Remove(tuple);
}
- _logger.LogInformation("Package installation cancelled: {0} {1}", package.name, package.version);
+ _logger.LogInformation("Package installation cancelled: {0} {1}", package.Name, package.Version);
- PackageInstallationCancelled?.Invoke(this, installationEventArgs);
+ PackageInstallationCancelled?.Invoke(this, package);
throw;
}
@@ -311,7 +300,7 @@ namespace Emby.Server.Implementations.Updates
PackageInstallationFailed?.Invoke(this, new InstallationFailedEventArgs
{
- InstallationInfo = installationInfo,
+ InstallationInfo = package,
Exception = ex
});
@@ -330,11 +319,11 @@ namespace Emby.Server.Implementations.Updates
/// <param name="package">The package.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns><see cref="Task" />.</returns>
- private async Task InstallPackageInternal(VersionInfo package, CancellationToken cancellationToken)
+ private async Task InstallPackageInternal(InstallationInfo package, CancellationToken cancellationToken)
{
// Set last update time if we were installed before
- IPlugin plugin = _applicationHost.Plugins.FirstOrDefault(p => string.Equals(p.Id.ToString(), package.guid, StringComparison.OrdinalIgnoreCase))
- ?? _applicationHost.Plugins.FirstOrDefault(p => p.Name.Equals(package.name, StringComparison.OrdinalIgnoreCase));
+ IPlugin plugin = _applicationHost.Plugins.FirstOrDefault(p => p.Id == package.Guid)
+ ?? _applicationHost.Plugins.FirstOrDefault(p => p.Name.Equals(package.Name, StringComparison.OrdinalIgnoreCase));
// Do the install
await PerformPackageInstallation(package, cancellationToken).ConfigureAwait(false);
@@ -342,38 +331,38 @@ namespace Emby.Server.Implementations.Updates
// Do plugin-specific processing
if (plugin == null)
{
- _logger.LogInformation("New plugin installed: {0} {1} {2}", package.name, package.version);
+ _logger.LogInformation("New plugin installed: {0} {1}", package.Name, package.Version);
- PluginInstalled?.Invoke(this, new GenericEventArgs<VersionInfo>(package));
+ PluginInstalled?.Invoke(this, package);
}
else
{
- _logger.LogInformation("Plugin updated: {0} {1} {2}", package.name, package.version);
+ _logger.LogInformation("Plugin updated: {0} {1}", package.Name, package.Version);
- PluginUpdated?.Invoke(this, new GenericEventArgs<(IPlugin, VersionInfo)>((plugin, package)));
+ PluginUpdated?.Invoke(this, package);
}
_applicationHost.NotifyPendingRestart();
}
- private async Task PerformPackageInstallation(VersionInfo package, CancellationToken cancellationToken)
+ private async Task PerformPackageInstallation(InstallationInfo package, CancellationToken cancellationToken)
{
- var extension = Path.GetExtension(package.filename);
+ var extension = Path.GetExtension(package.SourceUrl);
if (!string.Equals(extension, ".zip", StringComparison.OrdinalIgnoreCase))
{
- _logger.LogError("Only zip packages are supported. {Filename} is not a zip archive.", package.filename);
+ _logger.LogError("Only zip packages are supported. {SourceUrl} is not a zip archive.", package.SourceUrl);
return;
}
// Always override the passed-in target (which is a file) and figure it out again
- string targetDir = Path.Combine(_appPaths.PluginsPath, package.name);
+ string targetDir = Path.Combine(_appPaths.PluginsPath, package.Name);
// CA5351: Do Not Use Broken Cryptographic Algorithms
#pragma warning disable CA5351
using (var res = await _httpClient.SendAsync(
new HttpRequestOptions
{
- Url = package.sourceUrl,
+ Url = package.SourceUrl,
CancellationToken = cancellationToken,
// We need it to be buffered for setting the position
BufferContent = true
@@ -385,12 +374,12 @@ namespace Emby.Server.Implementations.Updates
cancellationToken.ThrowIfCancellationRequested();
var hash = Hex.Encode(md5.ComputeHash(stream));
- if (!string.Equals(package.checksum, hash, StringComparison.OrdinalIgnoreCase))
+ if (!string.Equals(package.Checksum, hash, StringComparison.OrdinalIgnoreCase))
{
_logger.LogError(
"The checksums didn't match while installing {Package}, expected: {Expected}, got: {Received}",
- package.name,
- package.checksum,
+ package.Name,
+ package.Checksum,
hash);
throw new InvalidDataException("The checksum of the received data doesn't match.");
}
@@ -456,7 +445,7 @@ namespace Emby.Server.Implementations.Updates
_config.SaveConfiguration();
}
- PluginUninstalled?.Invoke(this, new GenericEventArgs<IPlugin> { Argument = plugin });
+ PluginUninstalled?.Invoke(this, plugin);
_applicationHost.NotifyPendingRestart();
}
@@ -466,7 +455,7 @@ namespace Emby.Server.Implementations.Updates
{
lock (_currentInstallationsLock)
{
- var install = _currentInstallations.Find(x => x.info.Guid == id.ToString());
+ var install = _currentInstallations.Find(x => x.info.Guid == id);
if (install == default((InstallationInfo, CancellationTokenSource)))
{
return false;
@@ -486,9 +475,9 @@ namespace Emby.Server.Implementations.Updates
}
/// <summary>
- /// Releases unmanaged and - optionally - managed resources.
+ /// Releases unmanaged and optionally managed resources.
/// </summary>
- /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
+ /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources or <c>false</c> to release only unmanaged resources.</param>
protected virtual void Dispose(bool dispose)
{
if (dispose)
diff --git a/Jellyfin.Api/Auth/CustomAuthenticationHandler.cs b/Jellyfin.Api/Auth/CustomAuthenticationHandler.cs
index aab1141ee..767ba9fd4 100644
--- a/Jellyfin.Api/Auth/CustomAuthenticationHandler.cs
+++ b/Jellyfin.Api/Auth/CustomAuthenticationHandler.cs
@@ -3,6 +3,7 @@ using System.Security.Claims;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Jellyfin.Api.Constants;
+using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Net;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Logging;
@@ -49,10 +50,10 @@ namespace Jellyfin.Api.Auth
var claims = new[]
{
- new Claim(ClaimTypes.Name, user.Name),
+ new Claim(ClaimTypes.Name, user.Username),
new Claim(
ClaimTypes.Role,
- value: user.Policy.IsAdministrator ? UserRoles.Administrator : UserRoles.User)
+ value: user.HasPermission(PermissionKind.IsAdministrator) ? UserRoles.Administrator : UserRoles.User)
};
var identity = new ClaimsIdentity(claims, Scheme.Name);
var principal = new ClaimsPrincipal(identity);
diff --git a/Jellyfin.Api/Controllers/StartupController.cs b/Jellyfin.Api/Controllers/StartupController.cs
index afc9b8f3d..6ec0a4e26 100644
--- a/Jellyfin.Api/Controllers/StartupController.cs
+++ b/Jellyfin.Api/Controllers/StartupController.cs
@@ -95,10 +95,12 @@ namespace Jellyfin.Api.Controllers
[HttpGet("User")]
public StartupUserDto GetFirstUser()
{
+ // TODO: Remove this method when startup wizard no longer requires an existing user.
+ _userManager.Initialize();
var user = _userManager.Users.First();
return new StartupUserDto
{
- Name = user.Name,
+ Name = user.Username,
Password = user.Password
};
}
@@ -113,9 +115,9 @@ namespace Jellyfin.Api.Controllers
{
var user = _userManager.Users.First();
- user.Name = startupUserDto.Name;
+ user.Username = startupUserDto.Name;
- _userManager.UpdateUser(user);
+ await _userManager.UpdateUserAsync(user).ConfigureAwait(false);
if (!string.IsNullOrEmpty(startupUserDto.Password))
{
diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj
index 25d5d0c89..f3016bb3e 100644
--- a/Jellyfin.Api/Jellyfin.Api.csproj
+++ b/Jellyfin.Api/Jellyfin.Api.csproj
@@ -9,11 +9,12 @@
<TargetFramework>netstandard2.1</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+ <Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication" Version="2.2.0" />
- <PackageReference Include="Microsoft.AspNetCore.Authorization" Version="3.1.4" />
+ <PackageReference Include="Microsoft.AspNetCore.Authorization" Version="3.1.5" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.0.0" />
</ItemGroup>
diff --git a/Jellyfin.Api/Models/StartupDtos/StartupConfigurationDto.cs b/Jellyfin.Api/Models/StartupDtos/StartupConfigurationDto.cs
index d048dad0a..5a83a030d 100644
--- a/Jellyfin.Api/Models/StartupDtos/StartupConfigurationDto.cs
+++ b/Jellyfin.Api/Models/StartupDtos/StartupConfigurationDto.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace Jellyfin.Api.Models.StartupDtos
{
/// <summary>
diff --git a/Jellyfin.Api/Models/StartupDtos/StartupUserDto.cs b/Jellyfin.Api/Models/StartupDtos/StartupUserDto.cs
index 3a9348037..0dbb245ec 100644
--- a/Jellyfin.Api/Models/StartupDtos/StartupUserDto.cs
+++ b/Jellyfin.Api/Models/StartupDtos/StartupUserDto.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace Jellyfin.Api.Models.StartupDtos
{
/// <summary>
diff --git a/Jellyfin.Data/DayOfWeekHelper.cs b/Jellyfin.Data/DayOfWeekHelper.cs
new file mode 100644
index 000000000..32a41368d
--- /dev/null
+++ b/Jellyfin.Data/DayOfWeekHelper.cs
@@ -0,0 +1,65 @@
+using System;
+using System.Collections.Generic;
+using Jellyfin.Data.Enums;
+
+namespace Jellyfin.Data
+{
+ public static class DayOfWeekHelper
+ {
+ public static List<DayOfWeek> GetDaysOfWeek(DynamicDayOfWeek day)
+ {
+ var days = new List<DayOfWeek>(7);
+
+ if (day == DynamicDayOfWeek.Sunday
+ || day == DynamicDayOfWeek.Weekend
+ || day == DynamicDayOfWeek.Everyday)
+ {
+ days.Add(DayOfWeek.Sunday);
+ }
+
+ if (day == DynamicDayOfWeek.Monday
+ || day == DynamicDayOfWeek.Weekday
+ || day == DynamicDayOfWeek.Everyday)
+ {
+ days.Add(DayOfWeek.Monday);
+ }
+
+ if (day == DynamicDayOfWeek.Tuesday
+ || day == DynamicDayOfWeek.Weekday
+ || day == DynamicDayOfWeek.Everyday)
+ {
+ days.Add(DayOfWeek.Tuesday);
+ }
+
+ if (day == DynamicDayOfWeek.Wednesday
+ || day == DynamicDayOfWeek.Weekday
+ || day == DynamicDayOfWeek.Everyday)
+ {
+ days.Add(DayOfWeek.Wednesday);
+ }
+
+ if (day == DynamicDayOfWeek.Thursday
+ || day == DynamicDayOfWeek.Weekday
+ || day == DynamicDayOfWeek.Everyday)
+ {
+ days.Add(DayOfWeek.Thursday);
+ }
+
+ if (day == DynamicDayOfWeek.Friday
+ || day == DynamicDayOfWeek.Weekday
+ || day == DynamicDayOfWeek.Everyday)
+ {
+ days.Add(DayOfWeek.Friday);
+ }
+
+ if (day == DynamicDayOfWeek.Saturday
+ || day == DynamicDayOfWeek.Weekend
+ || day == DynamicDayOfWeek.Everyday)
+ {
+ days.Add(DayOfWeek.Saturday);
+ }
+
+ return days;
+ }
+ }
+}
diff --git a/Jellyfin.Data/Entities/AccessSchedule.cs b/Jellyfin.Data/Entities/AccessSchedule.cs
new file mode 100644
index 000000000..7d1b76a3f
--- /dev/null
+++ b/Jellyfin.Data/Entities/AccessSchedule.cs
@@ -0,0 +1,91 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using System.Text.Json.Serialization;
+using System.Xml.Serialization;
+using Jellyfin.Data.Enums;
+
+namespace Jellyfin.Data.Entities
+{
+ /// <summary>
+ /// An entity representing a user's access schedule.
+ /// </summary>
+ public class AccessSchedule
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="AccessSchedule"/> class.
+ /// </summary>
+ /// <param name="dayOfWeek">The day of the week.</param>
+ /// <param name="startHour">The start hour.</param>
+ /// <param name="endHour">The end hour.</param>
+ /// <param name="userId">The associated user's id.</param>
+ public AccessSchedule(DynamicDayOfWeek dayOfWeek, double startHour, double endHour, Guid userId)
+ {
+ UserId = userId;
+ DayOfWeek = dayOfWeek;
+ StartHour = startHour;
+ EndHour = endHour;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="AccessSchedule"/> class.
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </summary>
+ protected AccessSchedule()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the id of this instance.
+ /// </summary>
+ /// <remarks>
+ /// Identity, Indexed, Required.
+ /// </remarks>
+ [XmlIgnore]
+ [Key]
+ [Required]
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public int Id { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets the id of the associated user.
+ /// </summary>
+ [XmlIgnore]
+ [Required]
+ public Guid UserId { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets the day of week.
+ /// </summary>
+ /// <value>The day of week.</value>
+ [Required]
+ public DynamicDayOfWeek DayOfWeek { get; set; }
+
+ /// <summary>
+ /// Gets or sets the start hour.
+ /// </summary>
+ /// <value>The start hour.</value>
+ [Required]
+ public double StartHour { get; set; }
+
+ /// <summary>
+ /// Gets or sets the end hour.
+ /// </summary>
+ /// <value>The end hour.</value>
+ [Required]
+ public double EndHour { get; set; }
+
+ /// <summary>
+ /// Static create function (for use in LINQ queries, etc.)
+ /// </summary>
+ /// <param name="dayOfWeek">The day of the week.</param>
+ /// <param name="startHour">The start hour.</param>
+ /// <param name="endHour">The end hour.</param>
+ /// <param name="userId">The associated user's id.</param>
+ /// <returns>The newly created instance.</returns>
+ public static AccessSchedule Create(DynamicDayOfWeek dayOfWeek, double startHour, double endHour, Guid userId)
+ {
+ return new AccessSchedule(dayOfWeek, startHour, endHour, userId);
+ }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Group.cs b/Jellyfin.Data/Entities/Group.cs
index 54f9f4905..5cbb126f9 100644
--- a/Jellyfin.Data/Entities/Group.cs
+++ b/Jellyfin.Data/Entities/Group.cs
@@ -2,19 +2,32 @@ using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
+using System.Linq;
+using Jellyfin.Data.Enums;
namespace Jellyfin.Data.Entities
{
- public partial class Group
+ /// <summary>
+ /// An entity representing a group.
+ /// </summary>
+ public partial class Group : IHasPermissions, ISavingChanges
{
- partial void Init();
-
/// <summary>
- /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// Initializes a new instance of the <see cref="Group"/> class.
+ /// Public constructor with required data.
/// </summary>
- protected Group()
+ /// <param name="name">The name of the group.</param>
+ public Group(string name)
{
- GroupPermissions = new HashSet<Permission>();
+ if (string.IsNullOrEmpty(name))
+ {
+ throw new ArgumentNullException(nameof(name));
+ }
+
+ Name = name;
+ Id = Guid.NewGuid();
+
+ Permissions = new HashSet<Permission>();
ProviderMappings = new HashSet<ProviderMapping>();
Preferences = new HashSet<Preference>();
@@ -22,66 +35,45 @@ namespace Jellyfin.Data.Entities
}
/// <summary>
- /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
- /// </summary>
- public static Group CreateGroupUnsafe()
- {
- return new Group();
- }
-
- /// <summary>
- /// Public constructor with required data
+ /// Initializes a new instance of the <see cref="Group"/> class.
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
/// </summary>
- /// <param name="name"></param>
- /// <param name="_user0"></param>
- public Group(string name, User _user0)
+ protected Group()
{
- if (string.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name));
- this.Name = name;
-
- if (_user0 == null) throw new ArgumentNullException(nameof(_user0));
- _user0.Groups.Add(this);
-
- this.GroupPermissions = new HashSet<Permission>();
- this.ProviderMappings = new HashSet<ProviderMapping>();
- this.Preferences = new HashSet<Preference>();
-
Init();
}
- /// <summary>
- /// Static create function (for use in LINQ queries, etc.)
- /// </summary>
- /// <param name="name"></param>
- /// <param name="_user0"></param>
- public static Group Create(string name, User _user0)
- {
- return new Group(name, _user0);
- }
-
/*************************************************************************
* Properties
*************************************************************************/
/// <summary>
- /// Identity, Indexed, Required
+ /// Gets or sets the id of this group.
/// </summary>
+ /// <remarks>
+ /// Identity, Indexed, Required.
+ /// </remarks>
[Key]
[Required]
- [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id { get; protected set; }
+ public Guid Id { get; protected set; }
/// <summary>
- /// Required, Max length = 255
+ /// Gets or sets the group's name.
/// </summary>
+ /// <remarks>
+ /// Required, Max length = 255.
+ /// </remarks>
[Required]
[MaxLength(255)]
[StringLength(255)]
public string Name { get; set; }
/// <summary>
- /// Required, ConcurrenyToken
+ /// Gets or sets the row version.
/// </summary>
+ /// <remarks>
+ /// Required, Concurrency Token.
+ /// </remarks>
[ConcurrencyCheck]
[Required]
public uint RowVersion { get; set; }
@@ -96,7 +88,7 @@ namespace Jellyfin.Data.Entities
*************************************************************************/
[ForeignKey("Permission_GroupPermissions_Id")]
- public virtual ICollection<Permission> GroupPermissions { get; protected set; }
+ public virtual ICollection<Permission> Permissions { get; protected set; }
[ForeignKey("ProviderMapping_ProviderMappings_Id")]
public virtual ICollection<ProviderMapping> ProviderMappings { get; protected set; }
@@ -104,6 +96,27 @@ namespace Jellyfin.Data.Entities
[ForeignKey("Preference_Preferences_Id")]
public virtual ICollection<Preference> Preferences { get; protected set; }
+ /// <summary>
+ /// Static create function (for use in LINQ queries, etc.)
+ /// </summary>
+ /// <param name="name">The name of this group</param>
+ public static Group Create(string name)
+ {
+ return new Group(name);
+ }
+
+ /// <inheritdoc/>
+ public bool HasPermission(PermissionKind kind)
+ {
+ return Permissions.First(p => p.Kind == kind).Value;
+ }
+
+ /// <inheritdoc/>
+ public void SetPermission(PermissionKind kind, bool value)
+ {
+ Permissions.First(p => p.Kind == kind).Value = value;
+ }
+
+ partial void Init();
}
}
-
diff --git a/Jellyfin.Data/Entities/ImageInfo.cs b/Jellyfin.Data/Entities/ImageInfo.cs
new file mode 100644
index 000000000..64e36a791
--- /dev/null
+++ b/Jellyfin.Data/Entities/ImageInfo.cs
@@ -0,0 +1,30 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace Jellyfin.Data.Entities
+{
+ public class ImageInfo
+ {
+ public ImageInfo(string path)
+ {
+ Path = path;
+ LastModified = DateTime.UtcNow;
+ }
+
+ [Key]
+ [Required]
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public int Id { get; protected set; }
+
+ public Guid? UserId { get; protected set; }
+
+ [Required]
+ [MaxLength(512)]
+ [StringLength(512)]
+ public string Path { get; set; }
+
+ [Required]
+ public DateTime LastModified { get; set; }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Permission.cs b/Jellyfin.Data/Entities/Permission.cs
index 0b5b52cbd..b675e911d 100644
--- a/Jellyfin.Data/Entities/Permission.cs
+++ b/Jellyfin.Data/Entities/Permission.cs
@@ -1,64 +1,35 @@
-using System;
-using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
-using System.Runtime.CompilerServices;
+using Jellyfin.Data.Enums;
namespace Jellyfin.Data.Entities
{
- public partial class Permission
+ /// <summary>
+ /// An entity representing whether the associated user has a specific permission.
+ /// </summary>
+ public partial class Permission : ISavingChanges
{
- partial void Init();
-
- /// <summary>
- /// Default constructor. Protected due to required properties, but present because EF needs it.
- /// </summary>
- protected Permission()
- {
- Init();
- }
-
/// <summary>
- /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
+ /// Initializes a new instance of the <see cref="Permission"/> class.
+ /// Public constructor with required data.
/// </summary>
- public static Permission CreatePermissionUnsafe()
+ /// <param name="kind">The permission kind.</param>
+ /// <param name="value">The value of this permission.</param>
+ public Permission(PermissionKind kind, bool value)
{
- return new Permission();
- }
-
- /// <summary>
- /// Public constructor with required data
- /// </summary>
- /// <param name="kind"></param>
- /// <param name="value"></param>
- /// <param name="_user0"></param>
- /// <param name="_group1"></param>
- public Permission(Enums.PermissionKind kind, bool value, User _user0, Group _group1)
- {
- this.Kind = kind;
-
- this.Value = value;
-
- if (_user0 == null) throw new ArgumentNullException(nameof(_user0));
- _user0.Permissions.Add(this);
-
- if (_group1 == null) throw new ArgumentNullException(nameof(_group1));
- _group1.GroupPermissions.Add(this);
-
+ Kind = kind;
+ Value = value;
Init();
}
/// <summary>
- /// Static create function (for use in LINQ queries, etc.)
+ /// Initializes a new instance of the <see cref="Permission"/> class.
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
/// </summary>
- /// <param name="kind"></param>
- /// <param name="value"></param>
- /// <param name="_user0"></param>
- /// <param name="_group1"></param>
- public static Permission Create(Enums.PermissionKind kind, bool value, User _user0, Group _group1)
+ protected Permission()
{
- return new Permission(kind, value, _user0, _group1);
+ Init();
}
/*************************************************************************
@@ -66,79 +37,61 @@ namespace Jellyfin.Data.Entities
*************************************************************************/
/// <summary>
- /// Identity, Indexed, Required
+ /// Gets or sets the id of this permission.
/// </summary>
+ /// <remarks>
+ /// Identity, Indexed, Required.
+ /// </remarks>
[Key]
[Required]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; protected set; }
/// <summary>
- /// Backing field for Kind
- /// </summary>
- protected Enums.PermissionKind _Kind;
- /// <summary>
- /// When provided in a partial class, allows value of Kind to be changed before setting.
- /// </summary>
- partial void SetKind(Enums.PermissionKind oldValue, ref Enums.PermissionKind newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Kind to be changed before returning.
- /// </summary>
- partial void GetKind(ref Enums.PermissionKind result);
-
- /// <summary>
- /// Required
+ /// Gets or sets the type of this permission.
/// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
[Required]
- public Enums.PermissionKind Kind
- {
- get
- {
- Enums.PermissionKind value = _Kind;
- GetKind(ref value);
- return (_Kind = value);
- }
- set
- {
- Enums.PermissionKind oldValue = _Kind;
- SetKind(oldValue, ref value);
- if (oldValue != value)
- {
- _Kind = value;
- OnPropertyChanged();
- }
- }
- }
+ public PermissionKind Kind { get; protected set; }
/// <summary>
- /// Required
+ /// Gets or sets a value indicating whether the associated user has this permission.
/// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
[Required]
public bool Value { get; set; }
/// <summary>
- /// Required, ConcurrenyToken
+ /// Gets or sets the row version.
/// </summary>
+ /// <remarks>
+ /// Required, ConcurrencyToken.
+ /// </remarks>
[ConcurrencyCheck]
[Required]
public uint RowVersion { get; set; }
- public void OnSavingChanges()
+ /// <summary>
+ /// Static create function (for use in LINQ queries, etc.)
+ /// </summary>
+ /// <param name="kind">The permission kind.</param>
+ /// <param name="value">The value of this permission.</param>
+ /// <returns>The newly created instance.</returns>
+ public static Permission Create(PermissionKind kind, bool value)
{
- RowVersion++;
+ return new Permission(kind, value);
}
- /*************************************************************************
- * Navigation properties
- *************************************************************************/
-
- public virtual event PropertyChangedEventHandler PropertyChanged;
-
- protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
+ /// <inheritdoc/>
+ public void OnSavingChanges()
{
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ RowVersion++;
}
+ partial void Init();
}
}
-
diff --git a/Jellyfin.Data/Entities/Preference.cs b/Jellyfin.Data/Entities/Preference.cs
index 505f52e6b..0ca9d7eff 100644
--- a/Jellyfin.Data/Entities/Preference.cs
+++ b/Jellyfin.Data/Entities/Preference.cs
@@ -1,63 +1,33 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
+using Jellyfin.Data.Enums;
namespace Jellyfin.Data.Entities
{
- public partial class Preference
+ /// <summary>
+ /// An entity representing a preference attached to a user or group.
+ /// </summary>
+ public class Preference : ISavingChanges
{
- partial void Init();
-
- /// <summary>
- /// Default constructor. Protected due to required properties, but present because EF needs it.
- /// </summary>
- protected Preference()
- {
- Init();
- }
-
/// <summary>
- /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
+ /// Initializes a new instance of the <see cref="Preference"/> class.
+ /// Public constructor with required data.
/// </summary>
- public static Preference CreatePreferenceUnsafe()
+ /// <param name="kind">The preference kind.</param>
+ /// <param name="value">The value.</param>
+ public Preference(PreferenceKind kind, string value)
{
- return new Preference();
- }
-
- /// <summary>
- /// Public constructor with required data
- /// </summary>
- /// <param name="kind"></param>
- /// <param name="value"></param>
- /// <param name="_user0"></param>
- /// <param name="_group1"></param>
- public Preference(Enums.PreferenceKind kind, string value, User _user0, Group _group1)
- {
- this.Kind = kind;
-
- if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(value));
- this.Value = value;
-
- if (_user0 == null) throw new ArgumentNullException(nameof(_user0));
- _user0.Preferences.Add(this);
-
- if (_group1 == null) throw new ArgumentNullException(nameof(_group1));
- _group1.Preferences.Add(this);
-
-
- Init();
+ Kind = kind;
+ Value = value ?? throw new ArgumentNullException(nameof(value));
}
/// <summary>
- /// Static create function (for use in LINQ queries, etc.)
+ /// Initializes a new instance of the <see cref="Preference"/> class.
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
/// </summary>
- /// <param name="kind"></param>
- /// <param name="value"></param>
- /// <param name="_user0"></param>
- /// <param name="_group1"></param>
- public static Preference Create(Enums.PreferenceKind kind, string value, User _user0, Group _group1)
+ protected Preference()
{
- return new Preference(kind, value, _user0, _group1);
}
/*************************************************************************
@@ -65,43 +35,61 @@ namespace Jellyfin.Data.Entities
*************************************************************************/
/// <summary>
- /// Identity, Indexed, Required
+ /// Gets or sets the id of this preference.
/// </summary>
+ /// <remarks>
+ /// Identity, Indexed, Required.
+ /// </remarks>
[Key]
[Required]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; protected set; }
/// <summary>
- /// Required
+ /// Gets or sets the type of this preference.
/// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
[Required]
- public Enums.PreferenceKind Kind { get; set; }
+ public PreferenceKind Kind { get; protected set; }
/// <summary>
- /// Required, Max length = 65535
+ /// Gets or sets the value of this preference.
/// </summary>
+ /// <remarks>
+ /// Required, Max length = 65535.
+ /// </remarks>
[Required]
[MaxLength(65535)]
[StringLength(65535)]
public string Value { get; set; }
/// <summary>
- /// Required, ConcurrenyToken
+ /// Gets or sets the row version.
/// </summary>
+ /// <remarks>
+ /// Required, ConcurrencyToken.
+ /// </remarks>
[ConcurrencyCheck]
[Required]
public uint RowVersion { get; set; }
+ /// <summary>
+ /// Static create function (for use in LINQ queries, etc.)
+ /// </summary>
+ /// <param name="kind">The preference kind.</param>
+ /// <param name="value">The value.</param>
+ /// <returns>The new instance.</returns>
+ public static Preference Create(PreferenceKind kind, string value)
+ {
+ return new Preference(kind, value);
+ }
+
+ /// <inheritdoc/>
public void OnSavingChanges()
{
RowVersion++;
}
-
- /*************************************************************************
- * Navigation properties
- *************************************************************************/
-
}
}
-
diff --git a/Jellyfin.Data/Entities/ProviderMapping.cs b/Jellyfin.Data/Entities/ProviderMapping.cs
index 6197bd97b..e479341ad 100644
--- a/Jellyfin.Data/Entities/ProviderMapping.cs
+++ b/Jellyfin.Data/Entities/ProviderMapping.cs
@@ -43,12 +43,6 @@ namespace Jellyfin.Data.Entities
if (string.IsNullOrEmpty(providerdata)) throw new ArgumentNullException(nameof(providerdata));
this.ProviderData = providerdata;
- if (_user0 == null) throw new ArgumentNullException(nameof(_user0));
- _user0.ProviderMappings.Add(this);
-
- if (_group1 == null) throw new ArgumentNullException(nameof(_group1));
- _group1.ProviderMappings.Add(this);
-
Init();
}
diff --git a/Jellyfin.Data/Entities/Series.cs b/Jellyfin.Data/Entities/Series.cs
index 097b9958e..4f25c38b7 100644
--- a/Jellyfin.Data/Entities/Series.cs
+++ b/Jellyfin.Data/Entities/Series.cs
@@ -20,14 +20,6 @@ namespace Jellyfin.Data.Entities
}
/// <summary>
- /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
- /// </summary>
- public static Series CreateSeriesUnsafe()
- {
- return new Series();
- }
-
- /// <summary>
/// Public constructor with required data
/// </summary>
/// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param>
@@ -57,27 +49,27 @@ namespace Jellyfin.Data.Entities
/// <summary>
/// Backing field for AirsDayOfWeek
/// </summary>
- protected Enums.Weekday? _AirsDayOfWeek;
+ protected DayOfWeek? _AirsDayOfWeek;
/// <summary>
/// When provided in a partial class, allows value of AirsDayOfWeek to be changed before setting.
/// </summary>
- partial void SetAirsDayOfWeek(Enums.Weekday? oldValue, ref Enums.Weekday? newValue);
+ partial void SetAirsDayOfWeek(DayOfWeek? oldValue, ref DayOfWeek? newValue);
/// <summary>
/// When provided in a partial class, allows value of AirsDayOfWeek to be changed before returning.
/// </summary>
- partial void GetAirsDayOfWeek(ref Enums.Weekday? result);
+ partial void GetAirsDayOfWeek(ref DayOfWeek? result);
- public Enums.Weekday? AirsDayOfWeek
+ public DayOfWeek? AirsDayOfWeek
{
get
{
- Enums.Weekday? value = _AirsDayOfWeek;
+ DayOfWeek? value = _AirsDayOfWeek;
GetAirsDayOfWeek(ref value);
return (_AirsDayOfWeek = value);
}
set
{
- Enums.Weekday? oldValue = _AirsDayOfWeek;
+ DayOfWeek? oldValue = _AirsDayOfWeek;
SetAirsDayOfWeek(oldValue, ref value);
if (oldValue != value)
{
diff --git a/Jellyfin.Data/Entities/User.cs b/Jellyfin.Data/Entities/User.cs
index a81d5215b..b89b0a8f4 100644
--- a/Jellyfin.Data/Entities/User.cs
+++ b/Jellyfin.Data/Entities/User.cs
@@ -2,234 +2,506 @@ using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
+using System.Globalization;
+using System.Linq;
+using System.Text.Json.Serialization;
+using Jellyfin.Data.Enums;
namespace Jellyfin.Data.Entities
{
- public partial class User
+ /// <summary>
+ /// An entity representing a user.
+ /// </summary>
+ public partial class User : IHasPermissions, ISavingChanges
{
- partial void Init();
+ /// <summary>
+ /// The values being delimited here are Guids, so commas work as they do not appear in Guids.
+ /// </summary>
+ private const char Delimiter = ',';
/// <summary>
- /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// Initializes a new instance of the <see cref="User"/> class.
+ /// Public constructor with required data.
/// </summary>
- protected User()
+ /// <param name="username">The username for the new user.</param>
+ /// <param name="authenticationProviderId">The Id of the user's authentication provider.</param>
+ /// <param name="passwordResetProviderId">The Id of the user's password reset provider.</param>
+ public User(string username, string authenticationProviderId, string passwordResetProviderId)
{
- Groups = new HashSet<Group>();
+ if (string.IsNullOrEmpty(username))
+ {
+ throw new ArgumentNullException(nameof(username));
+ }
+
+ if (string.IsNullOrEmpty(authenticationProviderId))
+ {
+ throw new ArgumentNullException(nameof(authenticationProviderId));
+ }
+
+ if (string.IsNullOrEmpty(passwordResetProviderId))
+ {
+ throw new ArgumentNullException(nameof(passwordResetProviderId));
+ }
+
+ Username = username;
+ AuthenticationProviderId = authenticationProviderId;
+ PasswordResetProviderId = passwordResetProviderId;
+
+ AccessSchedules = new HashSet<AccessSchedule>();
+ // Groups = new HashSet<Group>();
Permissions = new HashSet<Permission>();
- ProviderMappings = new HashSet<ProviderMapping>();
Preferences = new HashSet<Preference>();
-
+ // ProviderMappings = new HashSet<ProviderMapping>();
+
+ // Set default values
+ Id = Guid.NewGuid();
+ InvalidLoginAttemptCount = 0;
+ EnableUserPreferenceAccess = true;
+ MustUpdatePassword = false;
+ DisplayMissingEpisodes = false;
+ DisplayCollectionsView = false;
+ HidePlayedInLatest = true;
+ RememberAudioSelections = true;
+ RememberSubtitleSelections = true;
+ EnableNextEpisodeAutoPlay = true;
+ EnableAutoLogin = false;
+ PlayDefaultAudioTrack = true;
+ SubtitleMode = SubtitlePlaybackMode.Default;
+ SyncPlayAccess = SyncPlayAccess.CreateAndJoinGroups;
+
+ AddDefaultPermissions();
+ AddDefaultPreferences();
Init();
}
/// <summary>
- /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
- /// </summary>
- public static User CreateUserUnsafe()
- {
- return new User();
- }
-
- /// <summary>
- /// Public constructor with required data
+ /// Initializes a new instance of the <see cref="User"/> class.
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
/// </summary>
- /// <param name="username"></param>
- /// <param name="mustupdatepassword"></param>
- /// <param name="audiolanguagepreference"></param>
- /// <param name="authenticationproviderid"></param>
- /// <param name="invalidloginattemptcount"></param>
- /// <param name="subtitlemode"></param>
- /// <param name="playdefaultaudiotrack"></param>
- public User(string username, bool mustupdatepassword, string audiolanguagepreference, string authenticationproviderid, int invalidloginattemptcount, string subtitlemode, bool playdefaultaudiotrack)
+ protected User()
{
- if (string.IsNullOrEmpty(username)) throw new ArgumentNullException(nameof(username));
- this.Username = username;
-
- this.MustUpdatePassword = mustupdatepassword;
-
- if (string.IsNullOrEmpty(audiolanguagepreference)) throw new ArgumentNullException(nameof(audiolanguagepreference));
- this.AudioLanguagePreference = audiolanguagepreference;
-
- if (string.IsNullOrEmpty(authenticationproviderid)) throw new ArgumentNullException(nameof(authenticationproviderid));
- this.AuthenticationProviderId = authenticationproviderid;
-
- this.InvalidLoginAttemptCount = invalidloginattemptcount;
-
- if (string.IsNullOrEmpty(subtitlemode)) throw new ArgumentNullException(nameof(subtitlemode));
- this.SubtitleMode = subtitlemode;
-
- this.PlayDefaultAudioTrack = playdefaultaudiotrack;
-
- this.Groups = new HashSet<Group>();
- this.Permissions = new HashSet<Permission>();
- this.ProviderMappings = new HashSet<ProviderMapping>();
- this.Preferences = new HashSet<Preference>();
-
Init();
}
- /// <summary>
- /// Static create function (for use in LINQ queries, etc.)
- /// </summary>
- /// <param name="username"></param>
- /// <param name="mustupdatepassword"></param>
- /// <param name="audiolanguagepreference"></param>
- /// <param name="authenticationproviderid"></param>
- /// <param name="invalidloginattemptcount"></param>
- /// <param name="subtitlemode"></param>
- /// <param name="playdefaultaudiotrack"></param>
- public static User Create(string username, bool mustupdatepassword, string audiolanguagepreference, string authenticationproviderid, int invalidloginattemptcount, string subtitlemode, bool playdefaultaudiotrack)
- {
- return new User(username, mustupdatepassword, audiolanguagepreference, authenticationproviderid, invalidloginattemptcount, subtitlemode, playdefaultaudiotrack);
- }
-
/*************************************************************************
* Properties
*************************************************************************/
/// <summary>
- /// Identity, Indexed, Required
+ /// Gets or sets the Id of the user.
/// </summary>
+ /// <remarks>
+ /// Identity, Indexed, Required.
+ /// </remarks>
[Key]
[Required]
- [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id { get; protected set; }
+ [JsonIgnore]
+ public Guid Id { get; set; }
/// <summary>
- /// Required, Max length = 255
+ /// Gets or sets the user's name.
/// </summary>
+ /// <remarks>
+ /// Required, Max length = 255.
+ /// </remarks>
[Required]
[MaxLength(255)]
[StringLength(255)]
public string Username { get; set; }
/// <summary>
- /// Max length = 65535
+ /// Gets or sets the user's password, or <c>null</c> if none is set.
/// </summary>
+ /// <remarks>
+ /// Max length = 65535.
+ /// </remarks>
[MaxLength(65535)]
[StringLength(65535)]
public string Password { get; set; }
/// <summary>
- /// Required
+ /// Gets or sets the user's easy password, or <c>null</c> if none is set.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 65535.
+ /// </remarks>
+ [MaxLength(65535)]
+ [StringLength(65535)]
+ public string EasyPassword { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether the user must update their password.
/// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
[Required]
public bool MustUpdatePassword { get; set; }
/// <summary>
- /// Required, Max length = 255
+ /// Gets or sets the audio language preference.
/// </summary>
- [Required]
+ /// <remarks>
+ /// Max length = 255.
+ /// </remarks>
[MaxLength(255)]
[StringLength(255)]
public string AudioLanguagePreference { get; set; }
/// <summary>
- /// Required, Max length = 255
+ /// Gets or sets the authentication provider id.
/// </summary>
+ /// <remarks>
+ /// Required, Max length = 255.
+ /// </remarks>
[Required]
[MaxLength(255)]
[StringLength(255)]
public string AuthenticationProviderId { get; set; }
/// <summary>
- /// Max length = 65535
+ /// Gets or sets the password reset provider id.
/// </summary>
- [MaxLength(65535)]
- [StringLength(65535)]
- public string GroupedFolders { get; set; }
+ /// <remarks>
+ /// Required, Max length = 255.
+ /// </remarks>
+ [Required]
+ [MaxLength(255)]
+ [StringLength(255)]
+ public string PasswordResetProviderId { get; set; }
/// <summary>
- /// Required
+ /// Gets or sets the invalid login attempt count.
/// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
[Required]
public int InvalidLoginAttemptCount { get; set; }
/// <summary>
- /// Max length = 65535
+ /// Gets or sets the last activity date.
/// </summary>
- [MaxLength(65535)]
- [StringLength(65535)]
- public string LatestItemExcludes { get; set; }
-
- public int? LoginAttemptsBeforeLockout { get; set; }
+ public DateTime? LastActivityDate { get; set; }
/// <summary>
- /// Max length = 65535
+ /// Gets or sets the last login date.
/// </summary>
- [MaxLength(65535)]
- [StringLength(65535)]
- public string MyMediaExcludes { get; set; }
+ public DateTime? LastLoginDate { get; set; }
/// <summary>
- /// Max length = 65535
+ /// Gets or sets the number of login attempts the user can make before they are locked out.
/// </summary>
- [MaxLength(65535)]
- [StringLength(65535)]
- public string OrderedViews { get; set; }
+ public int? LoginAttemptsBeforeLockout { get; set; }
/// <summary>
- /// Required, Max length = 255
+ /// Gets or sets the subtitle mode.
/// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
[Required]
- [MaxLength(255)]
- [StringLength(255)]
- public string SubtitleMode { get; set; }
+ public SubtitlePlaybackMode SubtitleMode { get; set; }
/// <summary>
- /// Required
+ /// Gets or sets a value indicating whether the default audio track should be played.
/// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
[Required]
public bool PlayDefaultAudioTrack { get; set; }
/// <summary>
- /// Max length = 255
+ /// Gets or sets the subtitle language preference.
/// </summary>
+ /// <remarks>
+ /// Max length = 255.
+ /// </remarks>
[MaxLength(255)]
[StringLength(255)]
- public string SubtitleLanguagePrefernce { get; set; }
+ public string SubtitleLanguagePreference { get; set; }
- public bool? DisplayMissingEpisodes { get; set; }
+ /// <summary>
+ /// Gets or sets a value indicating whether missing episodes should be displayed.
+ /// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
+ [Required]
+ public bool DisplayMissingEpisodes { get; set; }
- public bool? DisplayCollectionsView { get; set; }
+ /// <summary>
+ /// Gets or sets a value indicating whether to display the collections view.
+ /// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
+ [Required]
+ public bool DisplayCollectionsView { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether the user has a local password.
+ /// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
+ [Required]
+ public bool EnableLocalPassword { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether the server should hide played content in "Latest".
+ /// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
+ [Required]
+ public bool HidePlayedInLatest { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether to remember audio selections on played content.
+ /// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
+ [Required]
+ public bool RememberAudioSelections { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether to remember subtitle selections on played content.
+ /// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
+ [Required]
+ public bool RememberSubtitleSelections { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether to enable auto-play for the next episode.
+ /// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
+ [Required]
+ public bool EnableNextEpisodeAutoPlay { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether the user should auto-login.
+ /// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
+ [Required]
+ public bool EnableAutoLogin { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether the user can change their preferences.
+ /// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
+ [Required]
+ public bool EnableUserPreferenceAccess { get; set; }
- public bool? HidePlayedInLatest { get; set; }
+ /// <summary>
+ /// Gets or sets the maximum parental age rating.
+ /// </summary>
+ public int? MaxParentalAgeRating { get; set; }
- public bool? RememberAudioSelections { get; set; }
+ /// <summary>
+ /// Gets or sets the remote client bitrate limit.
+ /// </summary>
+ public int? RemoteClientBitrateLimit { get; set; }
- public bool? RememberSubtitleSelections { get; set; }
+ /// <summary>
+ /// Gets or sets the internal id.
+ /// This is a temporary stopgap for until the library db is migrated.
+ /// This corresponds to the value of the index of this user in the library db.
+ /// </summary>
+ [Required]
+ public long InternalId { get; set; }
- public bool? EnableNextEpisodeAutoPlay { get; set; }
+ /// <summary>
+ /// Gets or sets the user's profile image. Can be <c>null</c>.
+ /// </summary>
+ // [ForeignKey("UserId")]
+ public virtual ImageInfo ProfileImage { get; set; }
- public bool? EnableUserPreferenceAccess { get; set; }
+ [Required]
+ public SyncPlayAccess SyncPlayAccess { get; set; }
/// <summary>
- /// Required, ConcurrenyToken
+ /// Gets or sets the row version.
/// </summary>
+ /// <remarks>
+ /// Required, Concurrency Token.
+ /// </remarks>
[ConcurrencyCheck]
[Required]
public uint RowVersion { get; set; }
- public void OnSavingChanges()
- {
- RowVersion++;
- }
-
/*************************************************************************
* Navigation properties
*************************************************************************/
- [ForeignKey("Group_Groups_Id")]
+
+ /// <summary>
+ /// Gets or sets the list of access schedules this user has.
+ /// </summary>
+ public virtual ICollection<AccessSchedule> AccessSchedules { get; protected set; }
+
+ /*
+ /// <summary>
+ /// Gets or sets the list of groups this user is a member of.
+ /// </summary>
+ [ForeignKey("Group_Groups_Guid")]
public virtual ICollection<Group> Groups { get; protected set; }
+ */
- [ForeignKey("Permission_Permissions_Id")]
+ /// <summary>
+ /// Gets or sets the list of permissions this user has.
+ /// </summary>
+ [ForeignKey("Permission_Permissions_Guid")]
public virtual ICollection<Permission> Permissions { get; protected set; }
+ /*
+ /// <summary>
+ /// Gets or sets the list of provider mappings this user has.
+ /// </summary>
[ForeignKey("ProviderMapping_ProviderMappings_Id")]
public virtual ICollection<ProviderMapping> ProviderMappings { get; protected set; }
+ */
- [ForeignKey("Preference_Preferences_Id")]
+ /// <summary>
+ /// Gets or sets the list of preferences this user has.
+ /// </summary>
+ [ForeignKey("Preference_Preferences_Guid")]
public virtual ICollection<Preference> Preferences { get; protected set; }
+ /// <summary>
+ /// Static create function (for use in LINQ queries, etc.)
+ /// </summary>
+ /// <param name="username">The username for the created user.</param>
+ /// <param name="authenticationProviderId">The Id of the user's authentication provider.</param>
+ /// <param name="passwordResetProviderId">The Id of the user's password reset provider.</param>
+ /// <returns>The created instance.</returns>
+ public static User Create(string username, string authenticationProviderId, string passwordResetProviderId)
+ {
+ return new User(username, authenticationProviderId, passwordResetProviderId);
+ }
+
+ /// <inheritdoc/>
+ public void OnSavingChanges()
+ {
+ RowVersion++;
+ }
+
+ /// <summary>
+ /// Checks whether the user has the specified permission.
+ /// </summary>
+ /// <param name="kind">The permission kind.</param>
+ /// <returns><c>True</c> if the user has the specified permission.</returns>
+ public bool HasPermission(PermissionKind kind)
+ {
+ return Permissions.First(p => p.Kind == kind).Value;
+ }
+
+ /// <summary>
+ /// Sets the given permission kind to the provided value.
+ /// </summary>
+ /// <param name="kind">The permission kind.</param>
+ /// <param name="value">The value to set.</param>
+ public void SetPermission(PermissionKind kind, bool value)
+ {
+ Permissions.First(p => p.Kind == kind).Value = value;
+ }
+
+ /// <summary>
+ /// Gets the user's preferences for the given preference kind.
+ /// </summary>
+ /// <param name="preference">The preference kind.</param>
+ /// <returns>A string array containing the user's preferences.</returns>
+ public string[] GetPreference(PreferenceKind preference)
+ {
+ var val = Preferences.First(p => p.Kind == preference).Value;
+
+ return Equals(val, string.Empty) ? Array.Empty<string>() : val.Split(Delimiter);
+ }
+
+ /// <summary>
+ /// Sets the specified preference to the given value.
+ /// </summary>
+ /// <param name="preference">The preference kind.</param>
+ /// <param name="values">The values.</param>
+ public void SetPreference(PreferenceKind preference, string[] values)
+ {
+ Preferences.First(p => p.Kind == preference).Value
+ = string.Join(Delimiter.ToString(CultureInfo.InvariantCulture), values);
+ }
+
+ /// <summary>
+ /// Checks whether this user is currently allowed to use the server.
+ /// </summary>
+ /// <returns><c>True</c> if the current time is within an access schedule, or there are no access schedules.</returns>
+ public bool IsParentalScheduleAllowed()
+ {
+ return AccessSchedules.Count == 0
+ || AccessSchedules.Any(i => IsParentalScheduleAllowed(i, DateTime.UtcNow));
+ }
+
+ /// <summary>
+ /// Checks whether the provided folder is in this user's grouped folders.
+ /// </summary>
+ /// <param name="id">The Guid of the folder.</param>
+ /// <returns><c>True</c> if the folder is in the user's grouped folders.</returns>
+ public bool IsFolderGrouped(Guid id)
+ {
+ return GetPreference(PreferenceKind.GroupedFolders).Any(i => new Guid(i) == id);
+ }
+
+ private static bool IsParentalScheduleAllowed(AccessSchedule schedule, DateTime date)
+ {
+ var localTime = date.ToLocalTime();
+ var hour = localTime.TimeOfDay.TotalHours;
+
+ return DayOfWeekHelper.GetDaysOfWeek(schedule.DayOfWeek).Contains(localTime.DayOfWeek)
+ && hour >= schedule.StartHour
+ && hour <= schedule.EndHour;
+ }
+
+ // TODO: make these user configurable?
+ private void AddDefaultPermissions()
+ {
+ Permissions.Add(new Permission(PermissionKind.IsAdministrator, false));
+ Permissions.Add(new Permission(PermissionKind.IsDisabled, false));
+ Permissions.Add(new Permission(PermissionKind.IsHidden, true));
+ Permissions.Add(new Permission(PermissionKind.EnableAllChannels, true));
+ Permissions.Add(new Permission(PermissionKind.EnableAllDevices, true));
+ Permissions.Add(new Permission(PermissionKind.EnableAllFolders, true));
+ Permissions.Add(new Permission(PermissionKind.EnableContentDeletion, false));
+ Permissions.Add(new Permission(PermissionKind.EnableContentDownloading, true));
+ Permissions.Add(new Permission(PermissionKind.EnableMediaConversion, true));
+ Permissions.Add(new Permission(PermissionKind.EnableMediaPlayback, true));
+ Permissions.Add(new Permission(PermissionKind.EnablePlaybackRemuxing, true));
+ Permissions.Add(new Permission(PermissionKind.EnablePublicSharing, true));
+ Permissions.Add(new Permission(PermissionKind.EnableRemoteAccess, true));
+ Permissions.Add(new Permission(PermissionKind.EnableSyncTranscoding, true));
+ Permissions.Add(new Permission(PermissionKind.EnableAudioPlaybackTranscoding, true));
+ Permissions.Add(new Permission(PermissionKind.EnableLiveTvAccess, true));
+ Permissions.Add(new Permission(PermissionKind.EnableLiveTvManagement, true));
+ Permissions.Add(new Permission(PermissionKind.EnableSharedDeviceControl, true));
+ Permissions.Add(new Permission(PermissionKind.EnableVideoPlaybackTranscoding, true));
+ Permissions.Add(new Permission(PermissionKind.ForceRemoteSourceTranscoding, false));
+ Permissions.Add(new Permission(PermissionKind.EnableRemoteControlOfOtherUsers, false));
+ }
+
+ private void AddDefaultPreferences()
+ {
+ foreach (var val in Enum.GetValues(typeof(PreferenceKind)).Cast<PreferenceKind>())
+ {
+ Preferences.Add(new Preference(val, string.Empty));
+ }
+ }
+
+ partial void Init();
}
}
-
diff --git a/MediaBrowser.Model/Configuration/DynamicDayOfWeek.cs b/Jellyfin.Data/Enums/DynamicDayOfWeek.cs
index 71b16cfba..a33cd9d1c 100644
--- a/MediaBrowser.Model/Configuration/DynamicDayOfWeek.cs
+++ b/Jellyfin.Data/Enums/DynamicDayOfWeek.cs
@@ -1,6 +1,6 @@
#pragma warning disable CS1591
-namespace MediaBrowser.Model.Configuration
+namespace Jellyfin.Data.Enums
{
public enum DynamicDayOfWeek
{
diff --git a/Jellyfin.Data/Enums/PermissionKind.cs b/Jellyfin.Data/Enums/PermissionKind.cs
index 1506471e8..7d5200874 100644
--- a/Jellyfin.Data/Enums/PermissionKind.cs
+++ b/Jellyfin.Data/Enums/PermissionKind.cs
@@ -1,26 +1,113 @@
namespace Jellyfin.Data.Enums
{
+ /// <summary>
+ /// The types of user permissions.
+ /// </summary>
public enum PermissionKind
{
- IsAdministrator,
- IsHidden,
- IsDisabled,
- BlockUnrateditems,
- EnbleSharedDeviceControl,
- EnableRemoteAccess,
- EnableLiveTvManagement,
- EnableLiveTvAccess,
- EnableMediaPlayback,
- EnableAudioPlaybackTranscoding,
- EnableVideoPlaybackTranscoding,
- EnableContentDeletion,
- EnableContentDownloading,
- EnableSyncTranscoding,
- EnableMediaConversion,
- EnableAllDevices,
- EnableAllChannels,
- EnableAllFolders,
- EnablePublicSharing,
- AccessSchedules
+ /// <summary>
+ /// Whether the user is an administrator.
+ /// </summary>
+ IsAdministrator = 0,
+
+ /// <summary>
+ /// Whether the user is hidden.
+ /// </summary>
+ IsHidden = 1,
+
+ /// <summary>
+ /// Whether the user is disabled.
+ /// </summary>
+ IsDisabled = 2,
+
+ /// <summary>
+ /// Whether the user can control shared devices.
+ /// </summary>
+ EnableSharedDeviceControl = 3,
+
+ /// <summary>
+ /// Whether the user can access the server remotely.
+ /// </summary>
+ EnableRemoteAccess = 4,
+
+ /// <summary>
+ /// Whether the user can manage live tv.
+ /// </summary>
+ EnableLiveTvManagement = 5,
+
+ /// <summary>
+ /// Whether the user can access live tv.
+ /// </summary>
+ EnableLiveTvAccess = 6,
+
+ /// <summary>
+ /// Whether the user can play media.
+ /// </summary>
+ EnableMediaPlayback = 7,
+
+ /// <summary>
+ /// Whether the server should transcode audio for the user if requested.
+ /// </summary>
+ EnableAudioPlaybackTranscoding = 8,
+
+ /// <summary>
+ /// Whether the server should transcode video for the user if requested.
+ /// </summary>
+ EnableVideoPlaybackTranscoding = 9,
+
+ /// <summary>
+ /// Whether the user can delete content.
+ /// </summary>
+ EnableContentDeletion = 10,
+
+ /// <summary>
+ /// Whether the user can download content.
+ /// </summary>
+ EnableContentDownloading = 11,
+
+ /// <summary>
+ /// Whether to enable sync transcoding for the user.
+ /// </summary>
+ EnableSyncTranscoding = 12,
+
+ /// <summary>
+ /// Whether the user can do media conversion.
+ /// </summary>
+ EnableMediaConversion = 13,
+
+ /// <summary>
+ /// Whether the user has access to all devices.
+ /// </summary>
+ EnableAllDevices = 14,
+
+ /// <summary>
+ /// Whether the user has access to all channels.
+ /// </summary>
+ EnableAllChannels = 15,
+
+ /// <summary>
+ /// Whether the user has access to all folders.
+ /// </summary>
+ EnableAllFolders = 16,
+
+ /// <summary>
+ /// Whether to enable public sharing for the user.
+ /// </summary>
+ EnablePublicSharing = 17,
+
+ /// <summary>
+ /// Whether the user can remotely control other users.
+ /// </summary>
+ EnableRemoteControlOfOtherUsers = 18,
+
+ /// <summary>
+ /// Whether the user is permitted to do playback remuxing.
+ /// </summary>
+ EnablePlaybackRemuxing = 19,
+
+ /// <summary>
+ /// Whether the server should force transcoding on remote connections for the user.
+ /// </summary>
+ ForceRemoteSourceTranscoding = 20
}
}
diff --git a/Jellyfin.Data/Enums/PreferenceKind.cs b/Jellyfin.Data/Enums/PreferenceKind.cs
index cd2cb791a..de8eecc73 100644
--- a/Jellyfin.Data/Enums/PreferenceKind.cs
+++ b/Jellyfin.Data/Enums/PreferenceKind.cs
@@ -1,13 +1,68 @@
namespace Jellyfin.Data.Enums
{
+ /// <summary>
+ /// The types of user preferences.
+ /// </summary>
public enum PreferenceKind
{
- MaxParentalRating,
- BlockedTags,
- RemoteClientBitrateLimit,
- EnabledDevices,
- EnabledChannels,
- EnabledFolders,
- EnableContentDeletionFromFolders
+ /// <summary>
+ /// A list of blocked tags.
+ /// </summary>
+ BlockedTags = 0,
+
+ /// <summary>
+ /// A list of blocked channels.
+ /// </summary>
+ BlockedChannels = 1,
+
+ /// <summary>
+ /// A list of blocked media folders.
+ /// </summary>
+ BlockedMediaFolders = 2,
+
+ /// <summary>
+ /// A list of enabled devices.
+ /// </summary>
+ EnabledDevices = 3,
+
+ /// <summary>
+ /// A list of enabled channels
+ /// </summary>
+ EnabledChannels = 4,
+
+ /// <summary>
+ /// A list of enabled folders.
+ /// </summary>
+ EnabledFolders = 5,
+
+ /// <summary>
+ /// A list of folders to allow content deletion from.
+ /// </summary>
+ EnableContentDeletionFromFolders = 6,
+
+ /// <summary>
+ /// A list of latest items to exclude.
+ /// </summary>
+ LatestItemExcludes = 7,
+
+ /// <summary>
+ /// A list of media to exclude.
+ /// </summary>
+ MyMediaExcludes = 8,
+
+ /// <summary>
+ /// A list of grouped folders.
+ /// </summary>
+ GroupedFolders = 9,
+
+ /// <summary>
+ /// A list of unrated items to block.
+ /// </summary>
+ BlockUnratedItems = 10,
+
+ /// <summary>
+ /// A list of ordered views.
+ /// </summary>
+ OrderedViews = 11
}
}
diff --git a/MediaBrowser.Model/Configuration/SubtitlePlaybackMode.cs b/Jellyfin.Data/Enums/SubtitlePlaybackMode.cs
index f0aa2b98c..c8fc21159 100644
--- a/MediaBrowser.Model/Configuration/SubtitlePlaybackMode.cs
+++ b/Jellyfin.Data/Enums/SubtitlePlaybackMode.cs
@@ -1,6 +1,6 @@
-#pragma warning disable CS1591
+#pragma warning disable CS1591
-namespace MediaBrowser.Model.Configuration
+namespace Jellyfin.Data.Enums
{
public enum SubtitlePlaybackMode
{
diff --git a/Jellyfin.Data/Enums/SyncPlayAccess.cs b/Jellyfin.Data/Enums/SyncPlayAccess.cs
new file mode 100644
index 000000000..8c13b37a1
--- /dev/null
+++ b/Jellyfin.Data/Enums/SyncPlayAccess.cs
@@ -0,0 +1,23 @@
+namespace Jellyfin.Data.Enums
+{
+ /// <summary>
+ /// Enum SyncPlayAccess.
+ /// </summary>
+ public enum SyncPlayAccess
+ {
+ /// <summary>
+ /// User can create groups and join them.
+ /// </summary>
+ CreateAndJoinGroups = 0,
+
+ /// <summary>
+ /// User can only join already existing groups.
+ /// </summary>
+ JoinGroups = 1,
+
+ /// <summary>
+ /// SyncPlay is disabled for the user.
+ /// </summary>
+ None = 2
+ }
+}
diff --git a/MediaBrowser.Model/Configuration/UnratedItem.cs b/Jellyfin.Data/Enums/UnratedItem.cs
index e1d1a363d..5259e7739 100644
--- a/MediaBrowser.Model/Configuration/UnratedItem.cs
+++ b/Jellyfin.Data/Enums/UnratedItem.cs
@@ -1,6 +1,6 @@
#pragma warning disable CS1591
-namespace MediaBrowser.Model.Configuration
+namespace Jellyfin.Data.Enums
{
public enum UnratedItem
{
diff --git a/Jellyfin.Data/Enums/Weekday.cs b/Jellyfin.Data/Enums/Weekday.cs
deleted file mode 100644
index b80a03a33..000000000
--- a/Jellyfin.Data/Enums/Weekday.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-namespace Jellyfin.Data.Enums
-{
- public enum Weekday
- {
- Sunday,
- Monday,
- Tuesday,
- Wednesday,
- Thursday,
- Friday,
- Saturday
- }
-}
diff --git a/Jellyfin.Data/IHasPermissions.cs b/Jellyfin.Data/IHasPermissions.cs
new file mode 100644
index 000000000..3be72259a
--- /dev/null
+++ b/Jellyfin.Data/IHasPermissions.cs
@@ -0,0 +1,31 @@
+using System.Collections.Generic;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Enums;
+
+namespace Jellyfin.Data
+{
+ /// <summary>
+ /// An abstraction representing an entity that has permissions.
+ /// </summary>
+ public interface IHasPermissions
+ {
+ /// <summary>
+ /// Gets a collection containing this entity's permissions.
+ /// </summary>
+ ICollection<Permission> Permissions { get; }
+
+ /// <summary>
+ /// Checks whether this entity has the specified permission kind.
+ /// </summary>
+ /// <param name="kind">The kind of permission.</param>
+ /// <returns><c>true</c> if this entity has the specified permission, <c>false</c> otherwise.</returns>
+ bool HasPermission(PermissionKind kind);
+
+ /// <summary>
+ /// Sets the specified permission to the provided value.
+ /// </summary>
+ /// <param name="kind">The kind of permission.</param>
+ /// <param name="value">The value to set.</param>
+ void SetPermission(PermissionKind kind, bool value);
+ }
+}
diff --git a/Jellyfin.Data/Jellyfin.Data.csproj b/Jellyfin.Data/Jellyfin.Data.csproj
index 9157c3ead..282ea511c 100644
--- a/Jellyfin.Data/Jellyfin.Data.csproj
+++ b/Jellyfin.Data/Jellyfin.Data.csproj
@@ -19,8 +19,9 @@
</ItemGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="3.1.4" />
- <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.4" />
+ <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="3.1.5" />
+ <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.5" />
+ <PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="3.1.5" />
</ItemGroup>
</Project>
diff --git a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj
index a6e1f490a..6db514b2e 100644
--- a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj
+++ b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj
@@ -18,8 +18,10 @@
</ItemGroup>
<ItemGroup>
- <PackageReference Include="SkiaSharp" Version="1.68.1" />
- <PackageReference Include="SkiaSharp.NativeAssets.Linux" Version="1.68.1" />
+ <PackageReference Include="BlurHashSharp" Version="1.0.1" />
+ <PackageReference Include="BlurHashSharp.SkiaSharp" Version="1.0.0" />
+ <PackageReference Include="SkiaSharp" Version="1.68.3" />
+ <PackageReference Include="SkiaSharp.NativeAssets.Linux" Version="1.68.3" />
<PackageReference Include="Jellyfin.SkiaSharp.NativeAssets.LinuxArm" Version="1.68.1" />
</ItemGroup>
diff --git a/Jellyfin.Drawing.Skia/SkiaEncoder.cs b/Jellyfin.Drawing.Skia/SkiaEncoder.cs
index 5c7462ee2..ba9a5809f 100644
--- a/Jellyfin.Drawing.Skia/SkiaEncoder.cs
+++ b/Jellyfin.Drawing.Skia/SkiaEncoder.cs
@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
+using BlurHashSharp.SkiaSharp;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Extensions;
@@ -20,7 +21,7 @@ namespace Jellyfin.Drawing.Skia
private static readonly HashSet<string> _transparentImageTypes
= new HashSet<string>(StringComparer.OrdinalIgnoreCase) { ".png", ".gif", ".webp" };
- private readonly ILogger _logger;
+ private readonly ILogger<SkiaEncoder> _logger;
private readonly IApplicationPaths _appPaths;
/// <summary>
@@ -52,9 +53,7 @@ namespace Jellyfin.Drawing.Skia
"jpeg",
"jpg",
"png",
-
"dng",
-
"webp",
"gif",
"bmp",
@@ -63,10 +62,8 @@ namespace Jellyfin.Drawing.Skia
"ktx",
"pkm",
"wbmp",
-
- // TODO
- // Are all of these supported? https://github.com/google/skia/blob/master/infra/bots/recipes/test.py#L454
-
+ // TODO: check if these are supported on multiple platforms
+ // https://github.com/google/skia/blob/master/infra/bots/recipes/test.py#L454
// working on windows at least
"cr2",
"nef",
@@ -229,6 +226,20 @@ namespace Jellyfin.Drawing.Skia
}
}
+ /// <inheritdoc />
+ /// <exception cref="ArgumentNullException">The path is null.</exception>
+ /// <exception cref="FileNotFoundException">The path is not valid.</exception>
+ /// <exception cref="SkiaCodecException">The file at the specified path could not be used to generate a codec.</exception>
+ public string GetImageBlurHash(int xComp, int yComp, string path)
+ {
+ if (path == null)
+ {
+ throw new ArgumentNullException(nameof(path));
+ }
+
+ return BlurHashEncoder.Encode(xComp, yComp, path);
+ }
+
private static bool HasDiacritics(string text)
=> !string.Equals(text, text.RemoveDiacritics(), StringComparison.Ordinal);
@@ -257,7 +268,7 @@ namespace Jellyfin.Drawing.Skia
return path;
}
- var tempPath = Path.Combine(_appPaths.TempDirectory, Guid.NewGuid() + Path.GetExtension(path) ?? string.Empty);
+ var tempPath = Path.Combine(_appPaths.TempDirectory, Guid.NewGuid() + Path.GetExtension(path));
Directory.CreateDirectory(Path.GetDirectoryName(tempPath));
File.Copy(path, tempPath, true);
diff --git a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj
index 8486fc2df..dcac1b34b 100644
--- a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj
+++ b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj
@@ -21,16 +21,14 @@
<ItemGroup>
<Compile Include="..\SharedVersion.cs" />
- <Compile Remove="Migrations\20200430214405_InitialSchema.cs" />
- <Compile Remove="Migrations\20200430214405_InitialSchema.Designer.cs" />
</ItemGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.4">
+ <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
- <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.4">
+ <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
diff --git a/Jellyfin.Server.Implementations/JellyfinDb.cs b/Jellyfin.Server.Implementations/JellyfinDb.cs
index ec09a619f..f574ebc66 100644
--- a/Jellyfin.Server.Implementations/JellyfinDb.cs
+++ b/Jellyfin.Server.Implementations/JellyfinDb.cs
@@ -1,9 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1201 // Constuctors should not follow properties
-#pragma warning disable SA1516 // Elements should be followed by a blank line
-#pragma warning disable SA1623 // Property's documentation should begin with gets or sets
-#pragma warning disable SA1629 // Documentation should end with a period
-#pragma warning disable SA1648 // Inheritdoc should be used with inheriting class
using System.Linq;
using Jellyfin.Data;
@@ -15,7 +10,30 @@ namespace Jellyfin.Server.Implementations
/// <inheritdoc/>
public partial class JellyfinDb : DbContext
{
+ /// <summary>
+ /// Initializes a new instance of the <see cref="JellyfinDb"/> class.
+ /// </summary>
+ /// <param name="options">The database context options.</param>
+ public JellyfinDb(DbContextOptions<JellyfinDb> options) : base(options)
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the default connection string.
+ /// </summary>
+ public static string ConnectionString { get; set; } = @"Data Source=jellyfin.db";
+
+ public virtual DbSet<AccessSchedule> AccessSchedules { get; set; }
+
public virtual DbSet<ActivityLog> ActivityLogs { get; set; }
+
+ public virtual DbSet<ImageInfo> ImageInfos { get; set; }
+
+ public virtual DbSet<Permission> Permissions { get; set; }
+
+ public virtual DbSet<Preference> Preferences { get; set; }
+
+ public virtual DbSet<User> Users { get; set; }
/*public virtual DbSet<Artwork> Artwork { get; set; }
public virtual DbSet<Book> Books { get; set; }
public virtual DbSet<BookMetadata> BookMetadata { get; set; }
@@ -42,12 +60,10 @@ namespace Jellyfin.Server.Implementations
public virtual DbSet<MovieMetadata> MovieMetadata { get; set; }
public virtual DbSet<MusicAlbum> MusicAlbums { get; set; }
public virtual DbSet<MusicAlbumMetadata> MusicAlbumMetadata { get; set; }
- public virtual DbSet<Permission> Permissions { get; set; }
public virtual DbSet<Person> People { get; set; }
public virtual DbSet<PersonRole> PersonRoles { get; set; }
public virtual DbSet<Photo> Photo { get; set; }
public virtual DbSet<PhotoMetadata> PhotoMetadata { get; set; }
- public virtual DbSet<Preference> Preferences { get; set; }
public virtual DbSet<ProviderMapping> ProviderMappings { get; set; }
public virtual DbSet<Rating> Ratings { get; set; }
@@ -62,20 +78,21 @@ namespace Jellyfin.Server.Implementations
public virtual DbSet<Series> Series { get; set; }
public virtual DbSet<SeriesMetadata> SeriesMetadata { get; set; }
public virtual DbSet<Track> Tracks { get; set; }
- public virtual DbSet<TrackMetadata> TrackMetadata { get; set; }
- public virtual DbSet<User> Users { get; set; } */
-
- /// <summary>
- /// Gets or sets the default connection string.
- /// </summary>
- public static string ConnectionString { get; set; } = @"Data Source=jellyfin.db";
+ public virtual DbSet<TrackMetadata> TrackMetadata { get; set; }*/
- /// <inheritdoc />
- public JellyfinDb(DbContextOptions<JellyfinDb> options) : base(options)
+ /// <inheritdoc/>
+ public override int SaveChanges()
{
- }
+ foreach (var saveEntity in ChangeTracker.Entries()
+ .Where(e => e.State == EntityState.Modified)
+ .Select(entry => entry.Entity)
+ .OfType<ISavingChanges>())
+ {
+ saveEntity.OnSavingChanges();
+ }
- partial void CustomInit(DbContextOptionsBuilder optionsBuilder);
+ return base.SaveChanges();
+ }
/// <inheritdoc />
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
@@ -83,9 +100,6 @@ namespace Jellyfin.Server.Implementations
CustomInit(optionsBuilder);
}
- partial void OnModelCreatingImpl(ModelBuilder modelBuilder);
- partial void OnModelCreatedImpl(ModelBuilder modelBuilder);
-
/// <inheritdoc />
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
@@ -105,16 +119,10 @@ namespace Jellyfin.Server.Implementations
OnModelCreatedImpl(modelBuilder);
}
- public override int SaveChanges()
- {
- foreach (var saveEntity in ChangeTracker.Entries()
- .Where(e => e.State == EntityState.Modified)
- .OfType<ISavingChanges>())
- {
- saveEntity.OnSavingChanges();
- }
+ partial void CustomInit(DbContextOptionsBuilder optionsBuilder);
- return base.SaveChanges();
- }
+ partial void OnModelCreatingImpl(ModelBuilder modelBuilder);
+
+ partial void OnModelCreatedImpl(ModelBuilder modelBuilder);
}
}
diff --git a/Jellyfin.Server.Implementations/JellyfinDbProvider.cs b/Jellyfin.Server.Implementations/JellyfinDbProvider.cs
index eab531d38..8f5c19900 100644
--- a/Jellyfin.Server.Implementations/JellyfinDbProvider.cs
+++ b/Jellyfin.Server.Implementations/JellyfinDbProvider.cs
@@ -18,7 +18,7 @@ namespace Jellyfin.Server.Implementations
public JellyfinDbProvider(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
- serviceProvider.GetService<JellyfinDb>().Database.Migrate();
+ serviceProvider.GetRequiredService<JellyfinDb>().Database.Migrate();
}
/// <summary>
diff --git a/Jellyfin.Server.Implementations/Migrations/20200613202153_AddUsers.Designer.cs b/Jellyfin.Server.Implementations/Migrations/20200613202153_AddUsers.Designer.cs
new file mode 100644
index 000000000..6342ce9cf
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Migrations/20200613202153_AddUsers.Designer.cs
@@ -0,0 +1,312 @@
+#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("20200613202153_AddUsers")]
+ partial class AddUsers
+ {
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasDefaultSchema("jellyfin")
+ .HasAnnotation("ProductVersion", "3.1.4");
+
+ 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")
+ .HasColumnType("TEXT")
+ .HasMaxLength(256);
+
+ b.Property<int>("LogSeverity")
+ .HasColumnType("INTEGER");
+
+ b.Property<string>("Name")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasMaxLength(512);
+
+ b.Property<string>("Overview")
+ .HasColumnType("TEXT")
+ .HasMaxLength(512);
+
+ b.Property<uint>("RowVersion")
+ .IsConcurrencyToken()
+ .HasColumnType("INTEGER");
+
+ b.Property<string>("ShortOverview")
+ .HasColumnType("TEXT")
+ .HasMaxLength(512);
+
+ b.Property<string>("Type")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasMaxLength(256);
+
+ b.Property<Guid>("UserId")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.ToTable("ActivityLogs");
+ });
+
+ 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()
+ .HasColumnType("TEXT")
+ .HasMaxLength(512);
+
+ b.Property<Guid?>("UserId")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId")
+ .IsUnique();
+
+ b.ToTable("ImageInfos");
+ });
+
+ 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<bool>("Value")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Permission_Permissions_Guid");
+
+ 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<string>("Value")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasMaxLength(65535);
+
+ b.HasKey("Id");
+
+ b.HasIndex("Preference_Preferences_Guid");
+
+ b.ToTable("Preferences");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.User", b =>
+ {
+ b.Property<Guid>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property<string>("AudioLanguagePreference")
+ .HasColumnType("TEXT")
+ .HasMaxLength(255);
+
+ b.Property<string>("AuthenticationProviderId")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasMaxLength(255);
+
+ b.Property<bool>("DisplayCollectionsView")
+ .HasColumnType("INTEGER");
+
+ b.Property<bool>("DisplayMissingEpisodes")
+ .HasColumnType("INTEGER");
+
+ b.Property<string>("EasyPassword")
+ .HasColumnType("TEXT")
+ .HasMaxLength(65535);
+
+ 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?>("MaxParentalAgeRating")
+ .HasColumnType("INTEGER");
+
+ b.Property<bool>("MustUpdatePassword")
+ .HasColumnType("INTEGER");
+
+ b.Property<string>("Password")
+ .HasColumnType("TEXT")
+ .HasMaxLength(65535);
+
+ b.Property<string>("PasswordResetProviderId")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasMaxLength(255);
+
+ 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")
+ .HasColumnType("TEXT")
+ .HasMaxLength(255);
+
+ b.Property<int>("SubtitleMode")
+ .HasColumnType("INTEGER");
+
+ b.Property<int>("SyncPlayAccess")
+ .HasColumnType("INTEGER");
+
+ b.Property<string>("Username")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasMaxLength(255);
+
+ b.HasKey("Id");
+
+ 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.ImageInfo", b =>
+ {
+ b.HasOne("Jellyfin.Data.Entities.User", null)
+ .WithOne("ProfileImage")
+ .HasForeignKey("Jellyfin.Data.Entities.ImageInfo", "UserId");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b =>
+ {
+ b.HasOne("Jellyfin.Data.Entities.User", null)
+ .WithMany("Permissions")
+ .HasForeignKey("Permission_Permissions_Guid");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b =>
+ {
+ b.HasOne("Jellyfin.Data.Entities.User", null)
+ .WithMany("Preferences")
+ .HasForeignKey("Preference_Preferences_Guid");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Migrations/20200613202153_AddUsers.cs b/Jellyfin.Server.Implementations/Migrations/20200613202153_AddUsers.cs
new file mode 100644
index 000000000..7e5a76850
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Migrations/20200613202153_AddUsers.cs
@@ -0,0 +1,197 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1601
+
+using System;
+using Microsoft.EntityFrameworkCore.Migrations;
+
+namespace Jellyfin.Server.Implementations.Migrations
+{
+ public partial class AddUsers : Migration
+ {
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.CreateTable(
+ name: "Users",
+ schema: "jellyfin",
+ columns: table => new
+ {
+ Id = table.Column<Guid>(nullable: false),
+ Username = table.Column<string>(maxLength: 255, nullable: false),
+ Password = table.Column<string>(maxLength: 65535, nullable: true),
+ EasyPassword = table.Column<string>(maxLength: 65535, nullable: true),
+ MustUpdatePassword = table.Column<bool>(nullable: false),
+ AudioLanguagePreference = table.Column<string>(maxLength: 255, nullable: true),
+ AuthenticationProviderId = table.Column<string>(maxLength: 255, nullable: false),
+ PasswordResetProviderId = table.Column<string>(maxLength: 255, nullable: false),
+ InvalidLoginAttemptCount = table.Column<int>(nullable: false),
+ LastActivityDate = table.Column<DateTime>(nullable: true),
+ LastLoginDate = table.Column<DateTime>(nullable: true),
+ LoginAttemptsBeforeLockout = table.Column<int>(nullable: true),
+ SubtitleMode = table.Column<int>(nullable: false),
+ PlayDefaultAudioTrack = table.Column<bool>(nullable: false),
+ SubtitleLanguagePreference = table.Column<string>(maxLength: 255, nullable: true),
+ DisplayMissingEpisodes = table.Column<bool>(nullable: false),
+ DisplayCollectionsView = table.Column<bool>(nullable: false),
+ EnableLocalPassword = table.Column<bool>(nullable: false),
+ HidePlayedInLatest = table.Column<bool>(nullable: false),
+ RememberAudioSelections = table.Column<bool>(nullable: false),
+ RememberSubtitleSelections = table.Column<bool>(nullable: false),
+ EnableNextEpisodeAutoPlay = table.Column<bool>(nullable: false),
+ EnableAutoLogin = table.Column<bool>(nullable: false),
+ EnableUserPreferenceAccess = table.Column<bool>(nullable: false),
+ MaxParentalAgeRating = table.Column<int>(nullable: true),
+ RemoteClientBitrateLimit = table.Column<int>(nullable: true),
+ InternalId = table.Column<long>(nullable: false),
+ SyncPlayAccess = table.Column<int>(nullable: false),
+ RowVersion = table.Column<uint>(nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_Users", x => x.Id);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "AccessSchedules",
+ schema: "jellyfin",
+ columns: table => new
+ {
+ Id = table.Column<int>(nullable: false)
+ .Annotation("Sqlite:Autoincrement", true),
+ UserId = table.Column<Guid>(nullable: false),
+ DayOfWeek = table.Column<int>(nullable: false),
+ StartHour = table.Column<double>(nullable: false),
+ EndHour = table.Column<double>(nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_AccessSchedules", x => x.Id);
+ table.ForeignKey(
+ name: "FK_AccessSchedules_Users_UserId",
+ column: x => x.UserId,
+ principalSchema: "jellyfin",
+ principalTable: "Users",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "ImageInfos",
+ schema: "jellyfin",
+ columns: table => new
+ {
+ Id = table.Column<int>(nullable: false)
+ .Annotation("Sqlite:Autoincrement", true),
+ UserId = table.Column<Guid>(nullable: true),
+ Path = table.Column<string>(maxLength: 512, nullable: false),
+ LastModified = table.Column<DateTime>(nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_ImageInfos", x => x.Id);
+ table.ForeignKey(
+ name: "FK_ImageInfos_Users_UserId",
+ column: x => x.UserId,
+ principalSchema: "jellyfin",
+ principalTable: "Users",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Restrict);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "Permissions",
+ schema: "jellyfin",
+ columns: table => new
+ {
+ Id = table.Column<int>(nullable: false)
+ .Annotation("Sqlite:Autoincrement", true),
+ Kind = table.Column<int>(nullable: false),
+ Value = table.Column<bool>(nullable: false),
+ RowVersion = table.Column<uint>(nullable: false),
+ Permission_Permissions_Guid = table.Column<Guid>(nullable: true)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_Permissions", x => x.Id);
+ table.ForeignKey(
+ name: "FK_Permissions_Users_Permission_Permissions_Guid",
+ column: x => x.Permission_Permissions_Guid,
+ principalSchema: "jellyfin",
+ principalTable: "Users",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Restrict);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "Preferences",
+ schema: "jellyfin",
+ columns: table => new
+ {
+ Id = table.Column<int>(nullable: false)
+ .Annotation("Sqlite:Autoincrement", true),
+ Kind = table.Column<int>(nullable: false),
+ Value = table.Column<string>(maxLength: 65535, nullable: false),
+ RowVersion = table.Column<uint>(nullable: false),
+ Preference_Preferences_Guid = table.Column<Guid>(nullable: true)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_Preferences", x => x.Id);
+ table.ForeignKey(
+ name: "FK_Preferences_Users_Preference_Preferences_Guid",
+ column: x => x.Preference_Preferences_Guid,
+ principalSchema: "jellyfin",
+ principalTable: "Users",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Restrict);
+ });
+
+ migrationBuilder.CreateIndex(
+ name: "IX_AccessSchedules_UserId",
+ schema: "jellyfin",
+ table: "AccessSchedules",
+ column: "UserId");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_ImageInfos_UserId",
+ schema: "jellyfin",
+ table: "ImageInfos",
+ column: "UserId",
+ unique: true);
+
+ migrationBuilder.CreateIndex(
+ name: "IX_Permissions_Permission_Permissions_Guid",
+ schema: "jellyfin",
+ table: "Permissions",
+ column: "Permission_Permissions_Guid");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_Preferences_Preference_Preferences_Guid",
+ schema: "jellyfin",
+ table: "Preferences",
+ column: "Preference_Preferences_Guid");
+ }
+
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropTable(
+ name: "AccessSchedules",
+ schema: "jellyfin");
+
+ migrationBuilder.DropTable(
+ name: "ImageInfos",
+ schema: "jellyfin");
+
+ migrationBuilder.DropTable(
+ name: "Permissions",
+ schema: "jellyfin");
+
+ migrationBuilder.DropTable(
+ name: "Preferences",
+ schema: "jellyfin");
+
+ migrationBuilder.DropTable(
+ name: "Users",
+ schema: "jellyfin");
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs b/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs
index 1e7ffd235..51fad8224 100644
--- a/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs
+++ b/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs
@@ -1,7 +1,9 @@
// <auto-generated />
using System;
+using Jellyfin.Server.Implementations;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace Jellyfin.Server.Implementations.Migrations
{
@@ -13,7 +15,32 @@ namespace Jellyfin.Server.Implementations.Migrations
#pragma warning disable 612, 618
modelBuilder
.HasDefaultSchema("jellyfin")
- .HasAnnotation("ProductVersion", "3.1.3");
+ .HasAnnotation("ProductVersion", "3.1.4");
+
+ 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 =>
{
@@ -60,6 +87,221 @@ namespace Jellyfin.Server.Implementations.Migrations
b.ToTable("ActivityLogs");
});
+
+ 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()
+ .HasColumnType("TEXT")
+ .HasMaxLength(512);
+
+ b.Property<Guid?>("UserId")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId")
+ .IsUnique();
+
+ b.ToTable("ImageInfos");
+ });
+
+ 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<bool>("Value")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Permission_Permissions_Guid");
+
+ 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<string>("Value")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasMaxLength(65535);
+
+ b.HasKey("Id");
+
+ b.HasIndex("Preference_Preferences_Guid");
+
+ b.ToTable("Preferences");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.User", b =>
+ {
+ b.Property<Guid>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property<string>("AudioLanguagePreference")
+ .HasColumnType("TEXT")
+ .HasMaxLength(255);
+
+ b.Property<string>("AuthenticationProviderId")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasMaxLength(255);
+
+ b.Property<bool>("DisplayCollectionsView")
+ .HasColumnType("INTEGER");
+
+ b.Property<bool>("DisplayMissingEpisodes")
+ .HasColumnType("INTEGER");
+
+ b.Property<string>("EasyPassword")
+ .HasColumnType("TEXT")
+ .HasMaxLength(65535);
+
+ 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?>("MaxParentalAgeRating")
+ .HasColumnType("INTEGER");
+
+ b.Property<bool>("MustUpdatePassword")
+ .HasColumnType("INTEGER");
+
+ b.Property<string>("Password")
+ .HasColumnType("TEXT")
+ .HasMaxLength(65535);
+
+ b.Property<string>("PasswordResetProviderId")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasMaxLength(255);
+
+ 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")
+ .HasColumnType("TEXT")
+ .HasMaxLength(255);
+
+ b.Property<int>("SubtitleMode")
+ .HasColumnType("INTEGER");
+
+ b.Property<int>("SyncPlayAccess")
+ .HasColumnType("INTEGER");
+
+ b.Property<string>("Username")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasMaxLength(255);
+
+ b.HasKey("Id");
+
+ 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.ImageInfo", b =>
+ {
+ b.HasOne("Jellyfin.Data.Entities.User", null)
+ .WithOne("ProfileImage")
+ .HasForeignKey("Jellyfin.Data.Entities.ImageInfo", "UserId");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b =>
+ {
+ b.HasOne("Jellyfin.Data.Entities.User", null)
+ .WithMany("Permissions")
+ .HasForeignKey("Permission_Permissions_Guid");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b =>
+ {
+ b.HasOne("Jellyfin.Data.Entities.User", null)
+ .WithMany("Preferences")
+ .HasForeignKey("Preference_Preferences_Guid");
+ });
#pragma warning restore 612, 618
}
}
diff --git a/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs b/Jellyfin.Server.Implementations/Users/DefaultAuthenticationProvider.cs
index 52c8facc3..162dc6f5e 100644
--- a/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs
+++ b/Jellyfin.Server.Implementations/Users/DefaultAuthenticationProvider.cs
@@ -1,14 +1,16 @@
+#nullable enable
+
using System;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
using MediaBrowser.Common;
using MediaBrowser.Common.Cryptography;
using MediaBrowser.Controller.Authentication;
-using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Cryptography;
-namespace Emby.Server.Implementations.Library
+namespace Jellyfin.Server.Implementations.Users
{
/// <summary>
/// The default authentication provider.
@@ -47,7 +49,7 @@ namespace Emby.Server.Implementations.Library
{
if (resolvedUser == null)
{
- throw new AuthenticationException($"Specified user does not exist.");
+ throw new AuthenticationException("Specified user does not exist.");
}
bool success = false;
@@ -61,7 +63,7 @@ namespace Emby.Server.Implementations.Library
});
}
- byte[] passwordbytes = Encoding.UTF8.GetBytes(password);
+ byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
PasswordHash readyHash = PasswordHash.Parse(resolvedUser.Password);
if (_cryptographyProvider.GetSupportedHashMethods().Contains(readyHash.Id)
@@ -69,7 +71,7 @@ namespace Emby.Server.Implementations.Library
{
byte[] calculatedHash = _cryptographyProvider.ComputeHash(
readyHash.Id,
- passwordbytes,
+ passwordBytes,
readyHash.Salt.ToArray());
if (readyHash.Hash.SequenceEqual(calculatedHash))
@@ -95,7 +97,7 @@ namespace Emby.Server.Implementations.Library
/// <inheritdoc />
public bool HasPassword(User user)
- => !string.IsNullOrEmpty(user.Password);
+ => !string.IsNullOrEmpty(user?.Password);
/// <inheritdoc />
public Task ChangePassword(User user, string newPassword)
@@ -129,49 +131,11 @@ namespace Emby.Server.Implementations.Library
}
/// <inheritdoc />
- public string GetEasyPasswordHash(User user)
+ public string? GetEasyPasswordHash(User user)
{
return string.IsNullOrEmpty(user.EasyPassword)
? null
: Hex.Encode(PasswordHash.Parse(user.EasyPassword).Hash);
}
-
- /// <summary>
- /// Gets the hashed string.
- /// </summary>
- public string GetHashedString(User user, string str)
- {
- if (string.IsNullOrEmpty(user.Password))
- {
- return _cryptographyProvider.CreatePasswordHash(str).ToString();
- }
-
- // TODO: make use of iterations parameter?
- PasswordHash passwordHash = PasswordHash.Parse(user.Password);
- var salt = passwordHash.Salt.ToArray();
- return new PasswordHash(
- passwordHash.Id,
- _cryptographyProvider.ComputeHash(
- passwordHash.Id,
- Encoding.UTF8.GetBytes(str),
- salt),
- salt,
- passwordHash.Parameters.ToDictionary(x => x.Key, y => y.Value)).ToString();
- }
-
- public ReadOnlySpan<byte> GetHashed(User user, string str)
- {
- if (string.IsNullOrEmpty(user.Password))
- {
- return _cryptographyProvider.CreatePasswordHash(str).Hash;
- }
-
- // TODO: make use of iterations parameter?
- PasswordHash passwordHash = PasswordHash.Parse(user.Password);
- return _cryptographyProvider.ComputeHash(
- passwordHash.Id,
- Encoding.UTF8.GetBytes(str),
- passwordHash.Salt.ToArray());
- }
}
}
diff --git a/Emby.Server.Implementations/Library/DefaultPasswordResetProvider.cs b/Jellyfin.Server.Implementations/Users/DefaultPasswordResetProvider.cs
index 6c6fbd86f..cf5a01f08 100644
--- a/Emby.Server.Implementations/Library/DefaultPasswordResetProvider.cs
+++ b/Jellyfin.Server.Implementations/Users/DefaultPasswordResetProvider.cs
@@ -1,8 +1,11 @@
+#nullable enable
+
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.Configuration;
@@ -10,7 +13,7 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Users;
-namespace Emby.Server.Implementations.Library
+namespace Jellyfin.Server.Implementations.Users
{
/// <summary>
/// The default password reset provider.
@@ -51,54 +54,49 @@ namespace Emby.Server.Implementations.Library
/// <inheritdoc />
public async Task<PinRedeemResult> RedeemPasswordResetPin(string pin)
{
- SerializablePasswordReset spr;
- List<string> usersreset = new List<string>();
- foreach (var resetfile in Directory.EnumerateFiles(_passwordResetFileBaseDir, $"{BaseResetFileName}*"))
+ var usersReset = new List<string>();
+ foreach (var resetFile in Directory.EnumerateFiles(_passwordResetFileBaseDir, $"{BaseResetFileName}*"))
{
- using (var str = File.OpenRead(resetfile))
+ SerializablePasswordReset spr;
+ await using (var str = File.OpenRead(resetFile))
{
spr = await _jsonSerializer.DeserializeFromStreamAsync<SerializablePasswordReset>(str).ConfigureAwait(false);
}
- if (spr.ExpirationDate < DateTime.Now)
+ if (spr.ExpirationDate < DateTime.UtcNow)
{
- File.Delete(resetfile);
+ File.Delete(resetFile);
}
else if (string.Equals(
spr.Pin.Replace("-", string.Empty, StringComparison.Ordinal),
pin.Replace("-", string.Empty, StringComparison.Ordinal),
StringComparison.InvariantCultureIgnoreCase))
{
- var resetUser = _userManager.GetUserByName(spr.UserName);
- if (resetUser == null)
- {
- throw new ResourceNotFoundException($"User with a username of {spr.UserName} not found");
- }
+ var resetUser = _userManager.GetUserByName(spr.UserName)
+ ?? throw new ResourceNotFoundException($"User with a username of {spr.UserName} not found");
await _userManager.ChangePassword(resetUser, pin).ConfigureAwait(false);
- usersreset.Add(resetUser.Name);
- File.Delete(resetfile);
+ usersReset.Add(resetUser.Username);
+ File.Delete(resetFile);
}
}
- if (usersreset.Count < 1)
+ if (usersReset.Count < 1)
{
throw new ResourceNotFoundException($"No Users found with a password reset request matching pin {pin}");
}
- else
+
+ return new PinRedeemResult
{
- return new PinRedeemResult
- {
- Success = true,
- UsersReset = usersreset.ToArray()
- };
- }
+ Success = true,
+ UsersReset = usersReset.ToArray()
+ };
}
/// <inheritdoc />
- public async Task<ForgotPasswordResult> StartForgotPasswordProcess(MediaBrowser.Controller.Entities.User user, bool isInNetwork)
+ public async Task<ForgotPasswordResult> StartForgotPasswordProcess(User user, bool isInNetwork)
{
- string pin = string.Empty;
+ string pin;
using (var cryptoRandom = RandomNumberGenerator.Create())
{
byte[] bytes = new byte[4];
@@ -106,30 +104,33 @@ namespace Emby.Server.Implementations.Library
pin = BitConverter.ToString(bytes);
}
- DateTime expireTime = DateTime.Now.AddMinutes(30);
- string filePath = _passwordResetFileBase + user.InternalId + ".json";
+ DateTime expireTime = DateTime.UtcNow.AddMinutes(30);
+ string filePath = _passwordResetFileBase + user.Id + ".json";
SerializablePasswordReset spr = new SerializablePasswordReset
{
ExpirationDate = expireTime,
Pin = pin,
PinFile = filePath,
- UserName = user.Name
+ UserName = user.Username
};
- using (FileStream fileStream = File.OpenWrite(filePath))
+ await using (FileStream fileStream = File.OpenWrite(filePath))
{
_jsonSerializer.SerializeToStream(spr, fileStream);
await fileStream.FlushAsync().ConfigureAwait(false);
}
+ user.EasyPassword = pin;
+ await _userManager.UpdateUserAsync(user).ConfigureAwait(false);
+
return new ForgotPasswordResult
{
Action = ForgotPasswordAction.PinCode,
PinExpirationDate = expireTime,
- PinFile = filePath
};
}
+#nullable disable
private class SerializablePasswordReset : PasswordPinCreationResult
{
public string Pin { get; set; }
diff --git a/Jellyfin.Server.Implementations/Users/DeviceAccessEntryPoint.cs b/Jellyfin.Server.Implementations/Users/DeviceAccessEntryPoint.cs
new file mode 100644
index 000000000..140853e52
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Users/DeviceAccessEntryPoint.cs
@@ -0,0 +1,67 @@
+#nullable enable
+#pragma warning disable CS1591
+
+using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Enums;
+using MediaBrowser.Controller.Devices;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Plugins;
+using MediaBrowser.Controller.Security;
+using MediaBrowser.Controller.Session;
+using MediaBrowser.Model.Events;
+
+namespace Jellyfin.Server.Implementations.Users
+{
+ public sealed class DeviceAccessEntryPoint : IServerEntryPoint
+ {
+ private readonly IUserManager _userManager;
+ private readonly IAuthenticationRepository _authRepo;
+ private readonly IDeviceManager _deviceManager;
+ private readonly ISessionManager _sessionManager;
+
+ public DeviceAccessEntryPoint(IUserManager userManager, IAuthenticationRepository authRepo, IDeviceManager deviceManager, ISessionManager sessionManager)
+ {
+ _userManager = userManager;
+ _authRepo = authRepo;
+ _deviceManager = deviceManager;
+ _sessionManager = sessionManager;
+ }
+
+ public Task RunAsync()
+ {
+ _userManager.OnUserUpdated += OnUserUpdated;
+
+ return Task.CompletedTask;
+ }
+
+ public void Dispose()
+ {
+ }
+
+ private void OnUserUpdated(object? sender, GenericEventArgs<User> e)
+ {
+ var user = e.Argument;
+ if (!user.HasPermission(PermissionKind.EnableAllDevices))
+ {
+ UpdateDeviceAccess(user);
+ }
+ }
+
+ private void UpdateDeviceAccess(User user)
+ {
+ var existing = _authRepo.Get(new AuthenticationInfoQuery
+ {
+ UserId = user.Id
+ }).Items;
+
+ foreach (var authInfo in existing)
+ {
+ if (!string.IsNullOrEmpty(authInfo.DeviceId) && !_deviceManager.CanAccessDevice(user, authInfo.DeviceId))
+ {
+ _sessionManager.Logout(authInfo);
+ }
+ }
+ }
+ }
+}
diff --git a/Emby.Server.Implementations/Library/InvalidAuthProvider.cs b/Jellyfin.Server.Implementations/Users/InvalidAuthProvider.cs
index dc61aacd7..491aba1d4 100644
--- a/Emby.Server.Implementations/Library/InvalidAuthProvider.cs
+++ b/Jellyfin.Server.Implementations/Users/InvalidAuthProvider.cs
@@ -1,8 +1,10 @@
+#nullable enable
+
using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Authentication;
-using MediaBrowser.Controller.Entities;
-namespace Emby.Server.Implementations.Library
+namespace Jellyfin.Server.Implementations.Users
{
/// <summary>
/// An invalid authentication provider.
@@ -40,12 +42,6 @@ namespace Emby.Server.Implementations.Library
}
/// <inheritdoc />
- public string GetPasswordHash(User user)
- {
- return string.Empty;
- }
-
- /// <inheritdoc />
public string GetEasyPasswordHash(User user)
{
return string.Empty;
diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs
new file mode 100644
index 000000000..904114758
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Users/UserManager.cs
@@ -0,0 +1,841 @@
+#nullable enable
+#pragma warning disable CA1307
+
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Enums;
+using MediaBrowser.Common;
+using MediaBrowser.Common.Cryptography;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Controller.Authentication;
+using MediaBrowser.Controller.Drawing;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Net;
+using MediaBrowser.Model.Configuration;
+using MediaBrowser.Model.Cryptography;
+using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Events;
+using MediaBrowser.Model.Users;
+using Microsoft.Extensions.Logging;
+
+namespace Jellyfin.Server.Implementations.Users
+{
+ /// <summary>
+ /// Manages the creation and retrieval of <see cref="User"/> instances.
+ /// </summary>
+ public class UserManager : IUserManager
+ {
+ private readonly JellyfinDbProvider _dbProvider;
+ private readonly ICryptoProvider _cryptoProvider;
+ private readonly INetworkManager _networkManager;
+ private readonly IApplicationHost _appHost;
+ private readonly IImageProcessor _imageProcessor;
+ private readonly ILogger<UserManager> _logger;
+
+ private IAuthenticationProvider[] _authenticationProviders = null!;
+ private DefaultAuthenticationProvider _defaultAuthenticationProvider = null!;
+ private InvalidAuthProvider _invalidAuthProvider = null!;
+ private IPasswordResetProvider[] _passwordResetProviders = null!;
+ private DefaultPasswordResetProvider _defaultPasswordResetProvider = null!;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UserManager"/> class.
+ /// </summary>
+ /// <param name="dbProvider">The database provider.</param>
+ /// <param name="cryptoProvider">The cryptography provider.</param>
+ /// <param name="networkManager">The network manager.</param>
+ /// <param name="appHost">The application host.</param>
+ /// <param name="imageProcessor">The image processor.</param>
+ /// <param name="logger">The logger.</param>
+ public UserManager(
+ JellyfinDbProvider dbProvider,
+ ICryptoProvider cryptoProvider,
+ INetworkManager networkManager,
+ IApplicationHost appHost,
+ IImageProcessor imageProcessor,
+ ILogger<UserManager> logger)
+ {
+ _dbProvider = dbProvider;
+ _cryptoProvider = cryptoProvider;
+ _networkManager = networkManager;
+ _appHost = appHost;
+ _imageProcessor = imageProcessor;
+ _logger = logger;
+ }
+
+ /// <inheritdoc/>
+ public event EventHandler<GenericEventArgs<User>>? OnUserPasswordChanged;
+
+ /// <inheritdoc/>
+ public event EventHandler<GenericEventArgs<User>>? OnUserUpdated;
+
+ /// <inheritdoc/>
+ public event EventHandler<GenericEventArgs<User>>? OnUserCreated;
+
+ /// <inheritdoc/>
+ public event EventHandler<GenericEventArgs<User>>? OnUserDeleted;
+
+ /// <inheritdoc/>
+ public event EventHandler<GenericEventArgs<User>>? OnUserLockedOut;
+
+ /// <inheritdoc/>
+ public IEnumerable<User> Users => _dbProvider.CreateContext().Users;
+
+ /// <inheritdoc/>
+ public IEnumerable<Guid> UsersIds => _dbProvider.CreateContext().Users.Select(u => u.Id);
+
+ /// <inheritdoc/>
+ public User? GetUserById(Guid id)
+ {
+ if (id == Guid.Empty)
+ {
+ throw new ArgumentException("Guid can't be empty", nameof(id));
+ }
+
+ return _dbProvider.CreateContext().Users.Find(id);
+ }
+
+ /// <inheritdoc/>
+ public User? GetUserByName(string name)
+ {
+ if (string.IsNullOrWhiteSpace(name))
+ {
+ throw new ArgumentException("Invalid username", nameof(name));
+ }
+
+ // This can't use an overload with StringComparer because that would cause the query to
+ // have to be evaluated client-side.
+ return _dbProvider.CreateContext().Users.FirstOrDefault(u => string.Equals(u.Username, name));
+ }
+
+ /// <inheritdoc/>
+ public async Task RenameUser(User user, string newName)
+ {
+ if (user == null)
+ {
+ throw new ArgumentNullException(nameof(user));
+ }
+
+ if (string.IsNullOrWhiteSpace(newName))
+ {
+ throw new ArgumentException("Invalid username", nameof(newName));
+ }
+
+ if (user.Username.Equals(newName, StringComparison.Ordinal))
+ {
+ throw new ArgumentException("The new and old names must be different.");
+ }
+
+ if (Users.Any(u => u.Id != user.Id && u.Username.Equals(newName, StringComparison.OrdinalIgnoreCase)))
+ {
+ throw new ArgumentException(string.Format(
+ CultureInfo.InvariantCulture,
+ "A user with the name '{0}' already exists.",
+ newName));
+ }
+
+ user.Username = newName;
+ await UpdateUserAsync(user).ConfigureAwait(false);
+
+ OnUserUpdated?.Invoke(this, new GenericEventArgs<User>(user));
+ }
+
+ /// <inheritdoc/>
+ public void UpdateUser(User user)
+ {
+ var dbContext = _dbProvider.CreateContext();
+ dbContext.Users.Update(user);
+ dbContext.SaveChanges();
+ }
+
+ /// <inheritdoc/>
+ public async Task UpdateUserAsync(User user)
+ {
+ var dbContext = _dbProvider.CreateContext();
+ dbContext.Users.Update(user);
+
+ await dbContext.SaveChangesAsync().ConfigureAwait(false);
+ }
+
+ /// <inheritdoc/>
+ public User CreateUser(string name)
+ {
+ if (!IsValidUsername(name))
+ {
+ throw new ArgumentException("Usernames can contain unicode symbols, numbers (0-9), dashes (-), underscores (_), apostrophes ('), and periods (.)");
+ }
+
+ var dbContext = _dbProvider.CreateContext();
+
+ // TODO: Remove after user item data is migrated.
+ var max = dbContext.Users.Any() ? dbContext.Users.Select(u => u.InternalId).Max() : 0;
+
+ var newUser = new User(
+ name,
+ _defaultAuthenticationProvider.GetType().FullName,
+ _defaultPasswordResetProvider.GetType().FullName)
+ {
+ InternalId = max + 1
+ };
+ dbContext.Users.Add(newUser);
+ dbContext.SaveChanges();
+
+ OnUserCreated?.Invoke(this, new GenericEventArgs<User>(newUser));
+
+ return newUser;
+ }
+
+ /// <inheritdoc/>
+ public void DeleteUser(User user)
+ {
+ if (user == null)
+ {
+ throw new ArgumentNullException(nameof(user));
+ }
+
+ var dbContext = _dbProvider.CreateContext();
+
+ if (dbContext.Users.Find(user.Id) == null)
+ {
+ throw new ArgumentException(string.Format(
+ CultureInfo.InvariantCulture,
+ "The user cannot be deleted because there is no user with the Name {0} and Id {1}.",
+ user.Username,
+ user.Id));
+ }
+
+ if (dbContext.Users.Count() == 1)
+ {
+ throw new InvalidOperationException(string.Format(
+ CultureInfo.InvariantCulture,
+ "The user '{0}' cannot be deleted because there must be at least one user in the system.",
+ user.Username));
+ }
+
+ if (user.HasPermission(PermissionKind.IsAdministrator)
+ && Users.Count(i => i.HasPermission(PermissionKind.IsAdministrator)) == 1)
+ {
+ throw new ArgumentException(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ "The user '{0}' cannot be deleted because there must be at least one admin user in the system.",
+ user.Username),
+ nameof(user));
+ }
+
+ dbContext.Users.Remove(user);
+ dbContext.SaveChanges();
+ OnUserDeleted?.Invoke(this, new GenericEventArgs<User>(user));
+ }
+
+ /// <inheritdoc/>
+ public Task ResetPassword(User user)
+ {
+ return ChangePassword(user, string.Empty);
+ }
+
+ /// <inheritdoc/>
+ public void ResetEasyPassword(User user)
+ {
+ ChangeEasyPassword(user, string.Empty, null);
+ }
+
+ /// <inheritdoc/>
+ public async Task ChangePassword(User user, string newPassword)
+ {
+ if (user == null)
+ {
+ throw new ArgumentNullException(nameof(user));
+ }
+
+ await GetAuthenticationProvider(user).ChangePassword(user, newPassword).ConfigureAwait(false);
+ await UpdateUserAsync(user).ConfigureAwait(false);
+
+ OnUserPasswordChanged?.Invoke(this, new GenericEventArgs<User>(user));
+ }
+
+ /// <inheritdoc/>
+ public void ChangeEasyPassword(User user, string newPassword, string? newPasswordSha1)
+ {
+ GetAuthenticationProvider(user).ChangeEasyPassword(user, newPassword, newPasswordSha1);
+ UpdateUser(user);
+
+ OnUserPasswordChanged?.Invoke(this, new GenericEventArgs<User>(user));
+ }
+
+ /// <inheritdoc/>
+ public UserDto GetUserDto(User user, string? remoteEndPoint = null)
+ {
+ var hasPassword = GetAuthenticationProvider(user).HasPassword(user);
+ return new UserDto
+ {
+ Name = user.Username,
+ Id = user.Id,
+ ServerId = _appHost.SystemId,
+ HasPassword = hasPassword,
+ HasConfiguredPassword = hasPassword,
+ HasConfiguredEasyPassword = !string.IsNullOrEmpty(user.EasyPassword),
+ EnableAutoLogin = user.EnableAutoLogin,
+ LastLoginDate = user.LastLoginDate,
+ LastActivityDate = user.LastActivityDate,
+ PrimaryImageTag = user.ProfileImage != null ? _imageProcessor.GetImageCacheTag(user) : null,
+ Configuration = new UserConfiguration
+ {
+ SubtitleMode = user.SubtitleMode,
+ HidePlayedInLatest = user.HidePlayedInLatest,
+ EnableLocalPassword = user.EnableLocalPassword,
+ PlayDefaultAudioTrack = user.PlayDefaultAudioTrack,
+ DisplayCollectionsView = user.DisplayCollectionsView,
+ DisplayMissingEpisodes = user.DisplayMissingEpisodes,
+ AudioLanguagePreference = user.AudioLanguagePreference,
+ RememberAudioSelections = user.RememberAudioSelections,
+ EnableNextEpisodeAutoPlay = user.EnableNextEpisodeAutoPlay,
+ RememberSubtitleSelections = user.RememberSubtitleSelections,
+ SubtitleLanguagePreference = user.SubtitleLanguagePreference ?? string.Empty,
+ OrderedViews = user.GetPreference(PreferenceKind.OrderedViews),
+ GroupedFolders = user.GetPreference(PreferenceKind.GroupedFolders),
+ MyMediaExcludes = user.GetPreference(PreferenceKind.MyMediaExcludes),
+ LatestItemsExcludes = user.GetPreference(PreferenceKind.LatestItemExcludes)
+ },
+ Policy = new UserPolicy
+ {
+ MaxParentalRating = user.MaxParentalAgeRating,
+ EnableUserPreferenceAccess = user.EnableUserPreferenceAccess,
+ RemoteClientBitrateLimit = user.RemoteClientBitrateLimit ?? 0,
+ AuthenticationProviderId = user.AuthenticationProviderId,
+ PasswordResetProviderId = user.PasswordResetProviderId,
+ InvalidLoginAttemptCount = user.InvalidLoginAttemptCount,
+ LoginAttemptsBeforeLockout = user.LoginAttemptsBeforeLockout ?? -1,
+ IsAdministrator = user.HasPermission(PermissionKind.IsAdministrator),
+ IsHidden = user.HasPermission(PermissionKind.IsHidden),
+ IsDisabled = user.HasPermission(PermissionKind.IsDisabled),
+ EnableSharedDeviceControl = user.HasPermission(PermissionKind.EnableSharedDeviceControl),
+ EnableRemoteAccess = user.HasPermission(PermissionKind.EnableRemoteAccess),
+ EnableLiveTvManagement = user.HasPermission(PermissionKind.EnableLiveTvManagement),
+ EnableLiveTvAccess = user.HasPermission(PermissionKind.EnableLiveTvAccess),
+ EnableMediaPlayback = user.HasPermission(PermissionKind.EnableMediaPlayback),
+ EnableAudioPlaybackTranscoding = user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding),
+ EnableVideoPlaybackTranscoding = user.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding),
+ EnableContentDeletion = user.HasPermission(PermissionKind.EnableContentDeletion),
+ EnableContentDownloading = user.HasPermission(PermissionKind.EnableContentDownloading),
+ EnableSyncTranscoding = user.HasPermission(PermissionKind.EnableSyncTranscoding),
+ EnableMediaConversion = user.HasPermission(PermissionKind.EnableMediaConversion),
+ EnableAllChannels = user.HasPermission(PermissionKind.EnableAllChannels),
+ EnableAllDevices = user.HasPermission(PermissionKind.EnableAllDevices),
+ EnableAllFolders = user.HasPermission(PermissionKind.EnableAllFolders),
+ EnableRemoteControlOfOtherUsers = user.HasPermission(PermissionKind.EnableRemoteControlOfOtherUsers),
+ EnablePlaybackRemuxing = user.HasPermission(PermissionKind.EnablePlaybackRemuxing),
+ ForceRemoteSourceTranscoding = user.HasPermission(PermissionKind.ForceRemoteSourceTranscoding),
+ EnablePublicSharing = user.HasPermission(PermissionKind.EnablePublicSharing),
+ AccessSchedules = user.AccessSchedules.ToArray(),
+ BlockedTags = user.GetPreference(PreferenceKind.BlockedTags),
+ EnabledChannels = user.GetPreference(PreferenceKind.EnabledChannels),
+ EnabledDevices = user.GetPreference(PreferenceKind.EnabledDevices),
+ EnabledFolders = user.GetPreference(PreferenceKind.EnabledFolders),
+ EnableContentDeletionFromFolders = user.GetPreference(PreferenceKind.EnableContentDeletionFromFolders),
+ SyncPlayAccess = user.SyncPlayAccess,
+ BlockedChannels = user.GetPreference(PreferenceKind.BlockedChannels),
+ BlockedMediaFolders = user.GetPreference(PreferenceKind.BlockedMediaFolders),
+ BlockUnratedItems = user.GetPreference(PreferenceKind.BlockUnratedItems).Select(Enum.Parse<UnratedItem>).ToArray()
+ }
+ };
+ }
+
+ /// <inheritdoc/>
+ public async Task<User?> AuthenticateUser(
+ string username,
+ string password,
+ string passwordSha1,
+ string remoteEndPoint,
+ bool isUserSession)
+ {
+ if (string.IsNullOrWhiteSpace(username))
+ {
+ _logger.LogInformation("Authentication request without username has been denied (IP: {IP}).", remoteEndPoint);
+ throw new ArgumentNullException(nameof(username));
+ }
+
+ var user = Users.ToList().FirstOrDefault(i => string.Equals(username, i.Username, StringComparison.OrdinalIgnoreCase));
+ bool success;
+ IAuthenticationProvider? authenticationProvider;
+
+ if (user != null)
+ {
+ var authResult = await AuthenticateLocalUser(username, password, user, remoteEndPoint)
+ .ConfigureAwait(false);
+ authenticationProvider = authResult.authenticationProvider;
+ success = authResult.success;
+ }
+ else
+ {
+ var authResult = await AuthenticateLocalUser(username, password, null, remoteEndPoint)
+ .ConfigureAwait(false);
+ authenticationProvider = authResult.authenticationProvider;
+ string updatedUsername = authResult.username;
+ success = authResult.success;
+
+ if (success
+ && authenticationProvider != null
+ && !(authenticationProvider is DefaultAuthenticationProvider))
+ {
+ // Trust the username returned by the authentication provider
+ username = updatedUsername;
+
+ // Search the database for the user again
+ // the authentication provider might have created it
+ user = Users
+ .ToList().FirstOrDefault(i => string.Equals(username, i.Username, StringComparison.OrdinalIgnoreCase));
+
+ if (authenticationProvider is IHasNewUserPolicy hasNewUserPolicy)
+ {
+ UpdatePolicy(user.Id, hasNewUserPolicy.GetNewUserPolicy());
+
+ await UpdateUserAsync(user).ConfigureAwait(false);
+ }
+ }
+ }
+
+ if (success && user != null && authenticationProvider != null)
+ {
+ var providerId = authenticationProvider.GetType().FullName;
+
+ if (!string.Equals(providerId, user.AuthenticationProviderId, StringComparison.OrdinalIgnoreCase))
+ {
+ user.AuthenticationProviderId = providerId;
+ await UpdateUserAsync(user).ConfigureAwait(false);
+ }
+ }
+
+ if (user == null)
+ {
+ _logger.LogInformation(
+ "Authentication request for {UserName} has been denied (IP: {IP}).",
+ username,
+ remoteEndPoint);
+ throw new AuthenticationException("Invalid username or password entered.");
+ }
+
+ if (user.HasPermission(PermissionKind.IsDisabled))
+ {
+ _logger.LogInformation(
+ "Authentication request for {UserName} has been denied because this account is currently disabled (IP: {IP}).",
+ username,
+ remoteEndPoint);
+ throw new SecurityException(
+ $"The {user.Username} account is currently disabled. Please consult with your administrator.");
+ }
+
+ if (!user.HasPermission(PermissionKind.EnableRemoteAccess) &&
+ !_networkManager.IsInLocalNetwork(remoteEndPoint))
+ {
+ _logger.LogInformation(
+ "Authentication request for {UserName} forbidden: remote access disabled and user not in local network (IP: {IP}).",
+ username,
+ remoteEndPoint);
+ throw new SecurityException("Forbidden.");
+ }
+
+ if (!user.IsParentalScheduleAllowed())
+ {
+ _logger.LogInformation(
+ "Authentication request for {UserName} is not allowed at this time due parental restrictions (IP: {IP}).",
+ username,
+ remoteEndPoint);
+ throw new SecurityException("User is not allowed access at this time.");
+ }
+
+ // Update LastActivityDate and LastLoginDate, then save
+ if (success)
+ {
+ if (isUserSession)
+ {
+ user.LastActivityDate = user.LastLoginDate = DateTime.UtcNow;
+ }
+
+ user.InvalidLoginAttemptCount = 0;
+ await UpdateUserAsync(user).ConfigureAwait(false);
+ _logger.LogInformation("Authentication request for {UserName} has succeeded.", user.Username);
+ }
+ else
+ {
+ IncrementInvalidLoginAttemptCount(user);
+ _logger.LogInformation(
+ "Authentication request for {UserName} has been denied (IP: {IP}).",
+ user.Username,
+ remoteEndPoint);
+ }
+
+ return success ? user : null;
+ }
+
+ /// <inheritdoc/>
+ public async Task<ForgotPasswordResult> StartForgotPasswordProcess(string enteredUsername, bool isInNetwork)
+ {
+ var user = string.IsNullOrWhiteSpace(enteredUsername) ? null : GetUserByName(enteredUsername);
+
+ if (user != null && isInNetwork)
+ {
+ var passwordResetProvider = GetPasswordResetProvider(user);
+ return await passwordResetProvider.StartForgotPasswordProcess(user, isInNetwork).ConfigureAwait(false);
+ }
+
+ return new ForgotPasswordResult
+ {
+ Action = ForgotPasswordAction.InNetworkRequired,
+ PinFile = string.Empty
+ };
+ }
+
+ /// <inheritdoc/>
+ public async Task<PinRedeemResult> RedeemPasswordResetPin(string pin)
+ {
+ foreach (var provider in _passwordResetProviders)
+ {
+ var result = await provider.RedeemPasswordResetPin(pin).ConfigureAwait(false);
+
+ if (result.Success)
+ {
+ return result;
+ }
+ }
+
+ return new PinRedeemResult
+ {
+ Success = false,
+ UsersReset = Array.Empty<string>()
+ };
+ }
+
+ /// <inheritdoc/>
+ public void AddParts(IEnumerable<IAuthenticationProvider> authenticationProviders, IEnumerable<IPasswordResetProvider> passwordResetProviders)
+ {
+ _authenticationProviders = authenticationProviders.ToArray();
+ _passwordResetProviders = passwordResetProviders.ToArray();
+
+ _invalidAuthProvider = _authenticationProviders.OfType<InvalidAuthProvider>().First();
+ _defaultAuthenticationProvider = _authenticationProviders.OfType<DefaultAuthenticationProvider>().First();
+ _defaultPasswordResetProvider = _passwordResetProviders.OfType<DefaultPasswordResetProvider>().First();
+ }
+
+ /// <inheritdoc />
+ public void Initialize()
+ {
+ // TODO: Refactor the startup wizard so that it doesn't require a user to already exist.
+ var dbContext = _dbProvider.CreateContext();
+
+ if (dbContext.Users.Any())
+ {
+ return;
+ }
+
+ var defaultName = Environment.UserName;
+ if (string.IsNullOrWhiteSpace(defaultName))
+ {
+ defaultName = "MyJellyfinUser";
+ }
+
+ _logger.LogWarning("No users, creating one with username {UserName}", defaultName);
+
+ if (!IsValidUsername(defaultName))
+ {
+ throw new ArgumentException("Provided username is not valid!", defaultName);
+ }
+
+ var newUser = CreateUser(defaultName);
+ newUser.SetPermission(PermissionKind.IsAdministrator, true);
+ newUser.SetPermission(PermissionKind.EnableContentDeletion, true);
+ newUser.SetPermission(PermissionKind.EnableRemoteControlOfOtherUsers, true);
+
+ dbContext.Users.Update(newUser);
+ dbContext.SaveChanges();
+ }
+
+ /// <inheritdoc/>
+ public NameIdPair[] GetAuthenticationProviders()
+ {
+ return _authenticationProviders
+ .Where(provider => provider.IsEnabled)
+ .OrderBy(i => i is DefaultAuthenticationProvider ? 0 : 1)
+ .ThenBy(i => i.Name)
+ .Select(i => new NameIdPair
+ {
+ Name = i.Name,
+ Id = i.GetType().FullName
+ })
+ .ToArray();
+ }
+
+ /// <inheritdoc/>
+ public NameIdPair[] GetPasswordResetProviders()
+ {
+ return _passwordResetProviders
+ .Where(provider => provider.IsEnabled)
+ .OrderBy(i => i is DefaultPasswordResetProvider ? 0 : 1)
+ .ThenBy(i => i.Name)
+ .Select(i => new NameIdPair
+ {
+ Name = i.Name,
+ Id = i.GetType().FullName
+ })
+ .ToArray();
+ }
+
+ /// <inheritdoc/>
+ public void UpdateConfiguration(Guid userId, UserConfiguration config)
+ {
+ var dbContext = _dbProvider.CreateContext();
+ var user = dbContext.Users.Find(userId) ?? throw new ArgumentException("No user exists with given Id!");
+ user.SubtitleMode = config.SubtitleMode;
+ user.HidePlayedInLatest = config.HidePlayedInLatest;
+ user.EnableLocalPassword = config.EnableLocalPassword;
+ user.PlayDefaultAudioTrack = config.PlayDefaultAudioTrack;
+ user.DisplayCollectionsView = config.DisplayCollectionsView;
+ user.DisplayMissingEpisodes = config.DisplayMissingEpisodes;
+ user.AudioLanguagePreference = config.AudioLanguagePreference;
+ user.RememberAudioSelections = config.RememberAudioSelections;
+ user.EnableNextEpisodeAutoPlay = config.EnableNextEpisodeAutoPlay;
+ user.RememberSubtitleSelections = config.RememberSubtitleSelections;
+ user.SubtitleLanguagePreference = config.SubtitleLanguagePreference;
+
+ user.SetPreference(PreferenceKind.OrderedViews, config.OrderedViews);
+ user.SetPreference(PreferenceKind.GroupedFolders, config.GroupedFolders);
+ user.SetPreference(PreferenceKind.MyMediaExcludes, config.MyMediaExcludes);
+ user.SetPreference(PreferenceKind.LatestItemExcludes, config.LatestItemsExcludes);
+
+ dbContext.Update(user);
+ dbContext.SaveChanges();
+ }
+
+ /// <inheritdoc/>
+ public void UpdatePolicy(Guid userId, UserPolicy policy)
+ {
+ var dbContext = _dbProvider.CreateContext();
+ var user = dbContext.Users.Find(userId) ?? throw new ArgumentException("No user exists with given Id!");
+
+ // The default number of login attempts is 3, but for some god forsaken reason it's sent to the server as "0"
+ int? maxLoginAttempts = policy.LoginAttemptsBeforeLockout switch
+ {
+ -1 => null,
+ 0 => 3,
+ _ => policy.LoginAttemptsBeforeLockout
+ };
+
+ user.MaxParentalAgeRating = policy.MaxParentalRating;
+ user.EnableUserPreferenceAccess = policy.EnableUserPreferenceAccess;
+ user.RemoteClientBitrateLimit = policy.RemoteClientBitrateLimit;
+ user.AuthenticationProviderId = policy.AuthenticationProviderId;
+ user.PasswordResetProviderId = policy.PasswordResetProviderId;
+ user.InvalidLoginAttemptCount = policy.InvalidLoginAttemptCount;
+ user.LoginAttemptsBeforeLockout = maxLoginAttempts;
+ user.SyncPlayAccess = policy.SyncPlayAccess;
+ user.SetPermission(PermissionKind.IsAdministrator, policy.IsAdministrator);
+ user.SetPermission(PermissionKind.IsHidden, policy.IsHidden);
+ user.SetPermission(PermissionKind.IsDisabled, policy.IsDisabled);
+ user.SetPermission(PermissionKind.EnableSharedDeviceControl, policy.EnableSharedDeviceControl);
+ user.SetPermission(PermissionKind.EnableRemoteAccess, policy.EnableRemoteAccess);
+ user.SetPermission(PermissionKind.EnableLiveTvManagement, policy.EnableLiveTvManagement);
+ user.SetPermission(PermissionKind.EnableLiveTvAccess, policy.EnableLiveTvAccess);
+ user.SetPermission(PermissionKind.EnableMediaPlayback, policy.EnableMediaPlayback);
+ user.SetPermission(PermissionKind.EnableAudioPlaybackTranscoding, policy.EnableAudioPlaybackTranscoding);
+ user.SetPermission(PermissionKind.EnableVideoPlaybackTranscoding, policy.EnableVideoPlaybackTranscoding);
+ user.SetPermission(PermissionKind.EnableContentDeletion, policy.EnableContentDeletion);
+ user.SetPermission(PermissionKind.EnableContentDownloading, policy.EnableContentDownloading);
+ user.SetPermission(PermissionKind.EnableSyncTranscoding, policy.EnableSyncTranscoding);
+ user.SetPermission(PermissionKind.EnableMediaConversion, policy.EnableMediaConversion);
+ user.SetPermission(PermissionKind.EnableAllChannels, policy.EnableAllChannels);
+ user.SetPermission(PermissionKind.EnableAllDevices, policy.EnableAllDevices);
+ user.SetPermission(PermissionKind.EnableAllFolders, policy.EnableAllFolders);
+ user.SetPermission(PermissionKind.EnableRemoteControlOfOtherUsers, policy.EnableRemoteControlOfOtherUsers);
+ user.SetPermission(PermissionKind.EnablePlaybackRemuxing, policy.EnablePlaybackRemuxing);
+ user.SetPermission(PermissionKind.ForceRemoteSourceTranscoding, policy.ForceRemoteSourceTranscoding);
+ user.SetPermission(PermissionKind.EnablePublicSharing, policy.EnablePublicSharing);
+
+ user.AccessSchedules.Clear();
+ foreach (var policyAccessSchedule in policy.AccessSchedules)
+ {
+ user.AccessSchedules.Add(policyAccessSchedule);
+ }
+
+ // TODO: fix this at some point
+ user.SetPreference(
+ PreferenceKind.BlockUnratedItems,
+ policy.BlockUnratedItems?.Select(i => i.ToString()).ToArray() ?? Array.Empty<string>());
+ user.SetPreference(PreferenceKind.BlockedTags, policy.BlockedTags);
+ user.SetPreference(PreferenceKind.EnabledChannels, policy.EnabledChannels);
+ user.SetPreference(PreferenceKind.EnabledDevices, policy.EnabledDevices);
+ user.SetPreference(PreferenceKind.EnabledFolders, policy.EnabledFolders);
+ user.SetPreference(PreferenceKind.EnableContentDeletionFromFolders, policy.EnableContentDeletionFromFolders);
+
+ dbContext.Update(user);
+ dbContext.SaveChanges();
+ }
+
+ /// <inheritdoc/>
+ public void ClearProfileImage(User user)
+ {
+ var dbContext = _dbProvider.CreateContext();
+ dbContext.Remove(user.ProfileImage);
+ dbContext.SaveChanges();
+ }
+
+ private static bool IsValidUsername(string name)
+ {
+ // This is some regex that matches only on unicode "word" characters, as well as -, _ and @
+ // In theory this will cut out most if not all 'control' characters which should help minimize any weirdness
+ // Usernames can contain letters (a-z + whatever else unicode is cool with), numbers (0-9), at-signs (@), dashes (-), underscores (_), apostrophes ('), and periods (.)
+ return Regex.IsMatch(name, @"^[\w\-'._@]*$");
+ }
+
+ private IAuthenticationProvider GetAuthenticationProvider(User user)
+ {
+ return GetAuthenticationProviders(user)[0];
+ }
+
+ private IPasswordResetProvider GetPasswordResetProvider(User user)
+ {
+ return GetPasswordResetProviders(user)[0];
+ }
+
+ private IList<IAuthenticationProvider> GetAuthenticationProviders(User? user)
+ {
+ var authenticationProviderId = user?.AuthenticationProviderId;
+
+ var providers = _authenticationProviders.Where(i => i.IsEnabled).ToList();
+
+ if (!string.IsNullOrEmpty(authenticationProviderId))
+ {
+ providers = providers.Where(i => string.Equals(authenticationProviderId, i.GetType().FullName, StringComparison.OrdinalIgnoreCase)).ToList();
+ }
+
+ if (providers.Count == 0)
+ {
+ // Assign the user to the InvalidAuthProvider since no configured auth provider was valid/found
+ _logger.LogWarning(
+ "User {Username} was found with invalid/missing Authentication Provider {AuthenticationProviderId}. Assigning user to InvalidAuthProvider until this is corrected",
+ user?.Username,
+ user?.AuthenticationProviderId);
+ providers = new List<IAuthenticationProvider>
+ {
+ _invalidAuthProvider
+ };
+ }
+
+ return providers;
+ }
+
+ private IList<IPasswordResetProvider> GetPasswordResetProviders(User user)
+ {
+ var passwordResetProviderId = user?.PasswordResetProviderId;
+ var providers = _passwordResetProviders.Where(i => i.IsEnabled).ToArray();
+
+ if (!string.IsNullOrEmpty(passwordResetProviderId))
+ {
+ providers = providers.Where(i =>
+ string.Equals(passwordResetProviderId, i.GetType().FullName, StringComparison.OrdinalIgnoreCase))
+ .ToArray();
+ }
+
+ if (providers.Length == 0)
+ {
+ providers = new IPasswordResetProvider[]
+ {
+ _defaultPasswordResetProvider
+ };
+ }
+
+ return providers;
+ }
+
+ private async Task<(IAuthenticationProvider? authenticationProvider, string username, bool success)> AuthenticateLocalUser(
+ string username,
+ string password,
+ User? user,
+ string remoteEndPoint)
+ {
+ bool success = false;
+ IAuthenticationProvider? authenticationProvider = null;
+
+ foreach (var provider in GetAuthenticationProviders(user))
+ {
+ var providerAuthResult =
+ await AuthenticateWithProvider(provider, username, password, user).ConfigureAwait(false);
+ var updatedUsername = providerAuthResult.username;
+ success = providerAuthResult.success;
+
+ if (success)
+ {
+ authenticationProvider = provider;
+ username = updatedUsername;
+ break;
+ }
+ }
+
+ if (!success
+ && _networkManager.IsInLocalNetwork(remoteEndPoint)
+ && user?.EnableLocalPassword == true
+ && !string.IsNullOrEmpty(user.EasyPassword))
+ {
+ // Check easy password
+ var passwordHash = PasswordHash.Parse(user.EasyPassword);
+ var hash = _cryptoProvider.ComputeHash(
+ passwordHash.Id,
+ Encoding.UTF8.GetBytes(password),
+ passwordHash.Salt.ToArray());
+ success = passwordHash.Hash.SequenceEqual(hash);
+ }
+
+ return (authenticationProvider, username, success);
+ }
+
+ private async Task<(string username, bool success)> AuthenticateWithProvider(
+ IAuthenticationProvider provider,
+ string username,
+ string password,
+ User? resolvedUser)
+ {
+ try
+ {
+ var authenticationResult = provider is IRequiresResolvedUser requiresResolvedUser
+ ? await requiresResolvedUser.Authenticate(username, password, resolvedUser).ConfigureAwait(false)
+ : await provider.Authenticate(username, password).ConfigureAwait(false);
+
+ if (authenticationResult.Username != username)
+ {
+ _logger.LogDebug("Authentication provider provided updated username {1}", authenticationResult.Username);
+ username = authenticationResult.Username;
+ }
+
+ return (username, true);
+ }
+ catch (AuthenticationException ex)
+ {
+ _logger.LogError(ex, "Error authenticating with provider {Provider}", provider.Name);
+
+ return (username, false);
+ }
+ }
+
+ private void IncrementInvalidLoginAttemptCount(User user)
+ {
+ user.InvalidLoginAttemptCount++;
+ int? maxInvalidLogins = user.LoginAttemptsBeforeLockout;
+ if (maxInvalidLogins.HasValue && user.InvalidLoginAttemptCount >= maxInvalidLogins)
+ {
+ user.SetPermission(PermissionKind.IsDisabled, true);
+ OnUserLockedOut?.Invoke(this, new GenericEventArgs<User>(user));
+ _logger.LogWarning(
+ "Disabling user {Username} due to {Attempts} unsuccessful login attempts.",
+ user.Username,
+ user.InvalidLoginAttemptCount);
+ }
+
+ UpdateUser(user);
+ }
+ }
+}
diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs
index 331a32c73..fe07411a6 100644
--- a/Jellyfin.Server/CoreAppHost.cs
+++ b/Jellyfin.Server/CoreAppHost.cs
@@ -7,8 +7,10 @@ using Emby.Server.Implementations;
using Jellyfin.Drawing.Skia;
using Jellyfin.Server.Implementations;
using Jellyfin.Server.Implementations.Activity;
+using Jellyfin.Server.Implementations.Users;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Drawing;
+using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Activity;
using MediaBrowser.Model.IO;
using Microsoft.EntityFrameworkCore;
@@ -63,12 +65,15 @@ namespace Jellyfin.Server
// TODO: Set up scoping and use AddDbContextPool
serviceCollection.AddDbContext<JellyfinDb>(
- options => options.UseSqlite($"Filename={Path.Combine(ApplicationPaths.DataPath, "jellyfin.db")}"),
- ServiceLifetime.Transient);
+ options => options
+ .UseSqlite($"Filename={Path.Combine(ApplicationPaths.DataPath, "jellyfin.db")}")
+ .UseLazyLoadingProxies(),
+ ServiceLifetime.Transient);
serviceCollection.AddSingleton<JellyfinDbProvider>();
serviceCollection.AddSingleton<IActivityManager, ActivityManager>();
+ serviceCollection.AddSingleton<IUserManager, UserManager>();
base.RegisterServices(serviceCollection);
}
@@ -79,7 +84,11 @@ namespace Jellyfin.Server
/// <inheritdoc />
protected override IEnumerable<Assembly> GetAssembliesWithPartsInternal()
{
+ // Jellyfin.Server
yield return typeof(CoreAppHost).Assembly;
+
+ // Jellyfin.Server.Implementations
+ yield return typeof(JellyfinDb).Assembly;
}
/// <inheritdoc />
diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj
index c93aa837e..7457830fa 100644
--- a/Jellyfin.Server/Jellyfin.Server.csproj
+++ b/Jellyfin.Server/Jellyfin.Server.csproj
@@ -41,8 +41,8 @@
<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.7.82" />
- <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.1.4" />
- <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.4" />
+ <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.1.5" />
+ <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.5" />
<PackageReference Include="prometheus-net" Version="3.5.0" />
<PackageReference Include="prometheus-net.AspNetCore" Version="3.5.0" />
<PackageReference Include="Serilog.AspNetCore" Version="3.2.0" />
@@ -52,7 +52,7 @@
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
<PackageReference Include="Serilog.Sinks.File" Version="4.1.0" />
<PackageReference Include="Serilog.Sinks.Graylog" Version="2.1.2" />
- <PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.0.2" />
+ <PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.0.3" />
<PackageReference Include="SQLitePCLRaw.provider.sqlite3.netstandard11" Version="1.1.14" />
</ItemGroup>
diff --git a/Jellyfin.Server/Migrations/IMigrationRoutine.cs b/Jellyfin.Server/Migrations/IMigrationRoutine.cs
index b79fdeac0..6b5780a26 100644
--- a/Jellyfin.Server/Migrations/IMigrationRoutine.cs
+++ b/Jellyfin.Server/Migrations/IMigrationRoutine.cs
@@ -1,5 +1,4 @@
using System;
-using Microsoft.Extensions.Logging;
namespace Jellyfin.Server.Migrations
{
diff --git a/Jellyfin.Server/Migrations/MigrationRunner.cs b/Jellyfin.Server/Migrations/MigrationRunner.cs
index 473f62737..c98263223 100644
--- a/Jellyfin.Server/Migrations/MigrationRunner.cs
+++ b/Jellyfin.Server/Migrations/MigrationRunner.cs
@@ -19,7 +19,8 @@ namespace Jellyfin.Server.Migrations
typeof(Routines.DisableTranscodingThrottling),
typeof(Routines.CreateUserLoggingConfigFile),
typeof(Routines.MigrateActivityLogDb),
- typeof(Routines.RemoveDuplicateExtras)
+ typeof(Routines.RemoveDuplicateExtras),
+ typeof(Routines.MigrateUserDb)
};
/// <summary>
diff --git a/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs b/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs
index 89514c89b..b15e09290 100644
--- a/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs
+++ b/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs
@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using MediaBrowser.Common.Configuration;
-using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
namespace Jellyfin.Server.Migrations.Routines
diff --git a/Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs b/Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs
index b2e957d5b..c18aa1629 100644
--- a/Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs
+++ b/Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs
@@ -10,7 +10,7 @@ namespace Jellyfin.Server.Migrations.Routines
/// </summary>
internal class DisableTranscodingThrottling : IMigrationRoutine
{
- private readonly ILogger _logger;
+ private readonly ILogger<DisableTranscodingThrottling> _logger;
private readonly IConfigurationManager _configManager;
public DisableTranscodingThrottling(ILogger<DisableTranscodingThrottling> logger, IConfigurationManager configManager)
diff --git a/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs
index b3cc29708..fb3466e13 100644
--- a/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs
+++ b/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
-using System.Linq;
using Emby.Server.Implementations.Data;
using Jellyfin.Data.Entities;
using Jellyfin.Server.Implementations;
@@ -64,10 +63,11 @@ namespace Jellyfin.Server.Migrations.Routines
ConnectionFlags.ReadOnly,
null))
{
+ using var userDbConnection = SQLite3.Open(Path.Combine(dataPath, "users.db"), ConnectionFlags.ReadOnly, null);
_logger.LogWarning("Migrating the activity database may take a while, do not stop Jellyfin.");
using var dbContext = _provider.CreateContext();
- var queryResult = connection.Query("SELECT * FROM ActivityLog ORDER BY Id ASC");
+ var queryResult = connection.Query("SELECT * FROM ActivityLog ORDER BY Id");
// Make sure that the database is empty in case of failed migration due to power outages, etc.
dbContext.ActivityLogs.RemoveRange(dbContext.ActivityLogs);
@@ -76,17 +76,35 @@ namespace Jellyfin.Server.Migrations.Routines
dbContext.Database.ExecuteSqlRaw("UPDATE sqlite_sequence SET seq = 0 WHERE name = 'ActivityLog';");
dbContext.SaveChanges();
- var newEntries = queryResult.Select(entry =>
+ var newEntries = new List<ActivityLog>();
+
+ foreach (var entry in queryResult)
{
if (!logLevelDictionary.TryGetValue(entry[8].ToString(), out var severity))
{
severity = LogLevel.Trace;
}
- var newEntry = new ActivityLog(
- entry[1].ToString(),
- entry[4].ToString(),
- entry[6].SQLiteType == SQLiteType.Null ? Guid.Empty : Guid.Parse(entry[6].ToString()))
+ var guid = Guid.Empty;
+ if (entry[6].SQLiteType != SQLiteType.Null && !Guid.TryParse(entry[6].ToString(), out guid))
+ {
+ // This is not a valid Guid, see if it is an internal ID from an old Emby schema
+ _logger.LogWarning("Invalid Guid in UserId column: ", entry[6].ToString());
+
+ using var statement = userDbConnection.PrepareStatement("SELECT guid FROM LocalUsersv2 WHERE Id=@Id");
+ statement.TryBind("@Id", entry[6].ToString());
+
+ foreach (var row in statement.Query())
+ {
+ if (row.Count > 0 && Guid.TryParse(row[0].ToString(), out guid))
+ {
+ // Successfully parsed a Guid from the user table.
+ break;
+ }
+ }
+ }
+
+ var newEntry = new ActivityLog(entry[1].ToString(), entry[4].ToString(), guid)
{
DateCreated = entry[7].ReadDateTime(),
LogSeverity = severity
@@ -107,8 +125,8 @@ namespace Jellyfin.Server.Migrations.Routines
newEntry.ItemId = entry[5].ToString();
}
- return newEntry;
- });
+ newEntries.Add(newEntry);
+ }
dbContext.ActivityLogs.AddRange(newEntries);
dbContext.SaveChanges();
diff --git a/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs
new file mode 100644
index 000000000..2be10c708
--- /dev/null
+++ b/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs
@@ -0,0 +1,208 @@
+using System;
+using System.IO;
+using Emby.Server.Implementations.Data;
+using Emby.Server.Implementations.Serialization;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Enums;
+using Jellyfin.Server.Implementations;
+using Jellyfin.Server.Implementations.Users;
+using MediaBrowser.Common.Json;
+using MediaBrowser.Controller;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Model.Configuration;
+using MediaBrowser.Model.Users;
+using Microsoft.Extensions.Logging;
+using SQLitePCL.pretty;
+using JsonSerializer = System.Text.Json.JsonSerializer;
+
+namespace Jellyfin.Server.Migrations.Routines
+{
+ /// <summary>
+ /// The migration routine for migrating the user database to EF Core.
+ /// </summary>
+ public class MigrateUserDb : IMigrationRoutine
+ {
+ private const string DbFilename = "users.db";
+
+ private readonly ILogger<MigrateUserDb> _logger;
+ private readonly IServerApplicationPaths _paths;
+ private readonly JellyfinDbProvider _provider;
+ private readonly MyXmlSerializer _xmlSerializer;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MigrateUserDb"/> class.
+ /// </summary>
+ /// <param name="logger">The logger.</param>
+ /// <param name="paths">The server application paths.</param>
+ /// <param name="provider">The database provider.</param>
+ /// <param name="xmlSerializer">The xml serializer.</param>
+ public MigrateUserDb(
+ ILogger<MigrateUserDb> logger,
+ IServerApplicationPaths paths,
+ JellyfinDbProvider provider,
+ MyXmlSerializer xmlSerializer)
+ {
+ _logger = logger;
+ _paths = paths;
+ _provider = provider;
+ _xmlSerializer = xmlSerializer;
+ }
+
+ /// <inheritdoc/>
+ public Guid Id => Guid.Parse("5C4B82A2-F053-4009-BD05-B6FCAD82F14C");
+
+ /// <inheritdoc/>
+ public string Name => "MigrateUserDatabase";
+
+ /// <inheritdoc/>
+ public void Perform()
+ {
+ var dataPath = _paths.DataPath;
+ _logger.LogInformation("Migrating the user database may take a while, do not stop Jellyfin.");
+
+ using (var connection = SQLite3.Open(Path.Combine(dataPath, DbFilename), ConnectionFlags.ReadOnly, null))
+ {
+ var dbContext = _provider.CreateContext();
+
+ var queryResult = connection.Query("SELECT * FROM LocalUsersv2");
+
+ dbContext.RemoveRange(dbContext.Users);
+ dbContext.SaveChanges();
+
+ foreach (var entry in queryResult)
+ {
+ UserMockup mockup = JsonSerializer.Deserialize<UserMockup>(entry[2].ToBlob(), JsonDefaults.GetOptions());
+ var userDataDir = Path.Combine(_paths.UserConfigurationDirectoryPath, mockup.Name);
+
+ var config = File.Exists(Path.Combine(userDataDir, "config.xml"))
+ ? (UserConfiguration)_xmlSerializer.DeserializeFromFile(typeof(UserConfiguration), Path.Combine(userDataDir, "config.xml"))
+ : new UserConfiguration();
+ var policy = File.Exists(Path.Combine(userDataDir, "policy.xml"))
+ ? (UserPolicy)_xmlSerializer.DeserializeFromFile(typeof(UserPolicy), Path.Combine(userDataDir, "policy.xml"))
+ : new UserPolicy();
+ policy.AuthenticationProviderId = policy.AuthenticationProviderId?.Replace(
+ "Emby.Server.Implementations.Library",
+ "Jellyfin.Server.Implementations.Users",
+ StringComparison.Ordinal)
+ ?? typeof(DefaultAuthenticationProvider).FullName;
+
+ policy.PasswordResetProviderId = typeof(DefaultPasswordResetProvider).FullName;
+ int? maxLoginAttempts = policy.LoginAttemptsBeforeLockout switch
+ {
+ -1 => null,
+ 0 => 3,
+ _ => policy.LoginAttemptsBeforeLockout
+ };
+
+ var user = new User(mockup.Name, policy.AuthenticationProviderId, policy.PasswordResetProviderId)
+ {
+ Id = entry[1].ReadGuidFromBlob(),
+ InternalId = entry[0].ToInt64(),
+ MaxParentalAgeRating = policy.MaxParentalRating,
+ EnableUserPreferenceAccess = policy.EnableUserPreferenceAccess,
+ RemoteClientBitrateLimit = policy.RemoteClientBitrateLimit,
+ InvalidLoginAttemptCount = policy.InvalidLoginAttemptCount,
+ LoginAttemptsBeforeLockout = maxLoginAttempts,
+ SubtitleMode = config.SubtitleMode,
+ HidePlayedInLatest = config.HidePlayedInLatest,
+ EnableLocalPassword = config.EnableLocalPassword,
+ PlayDefaultAudioTrack = config.PlayDefaultAudioTrack,
+ DisplayCollectionsView = config.DisplayCollectionsView,
+ DisplayMissingEpisodes = config.DisplayMissingEpisodes,
+ AudioLanguagePreference = config.AudioLanguagePreference,
+ RememberAudioSelections = config.RememberAudioSelections,
+ EnableNextEpisodeAutoPlay = config.EnableNextEpisodeAutoPlay,
+ RememberSubtitleSelections = config.RememberSubtitleSelections,
+ SubtitleLanguagePreference = config.SubtitleLanguagePreference,
+ Password = mockup.Password,
+ EasyPassword = mockup.EasyPassword,
+ LastLoginDate = mockup.LastLoginDate,
+ LastActivityDate = mockup.LastActivityDate
+ };
+
+ if (mockup.ImageInfos.Length > 0)
+ {
+ ItemImageInfo info = mockup.ImageInfos[0];
+
+ user.ProfileImage = new ImageInfo(info.Path)
+ {
+ LastModified = info.DateModified
+ };
+ }
+
+ user.SetPermission(PermissionKind.IsAdministrator, policy.IsAdministrator);
+ user.SetPermission(PermissionKind.IsHidden, policy.IsHidden);
+ user.SetPermission(PermissionKind.IsDisabled, policy.IsDisabled);
+ user.SetPermission(PermissionKind.EnableSharedDeviceControl, policy.EnableSharedDeviceControl);
+ user.SetPermission(PermissionKind.EnableRemoteAccess, policy.EnableRemoteAccess);
+ user.SetPermission(PermissionKind.EnableLiveTvManagement, policy.EnableLiveTvManagement);
+ user.SetPermission(PermissionKind.EnableLiveTvAccess, policy.EnableLiveTvAccess);
+ user.SetPermission(PermissionKind.EnableMediaPlayback, policy.EnableMediaPlayback);
+ user.SetPermission(PermissionKind.EnableAudioPlaybackTranscoding, policy.EnableAudioPlaybackTranscoding);
+ user.SetPermission(PermissionKind.EnableVideoPlaybackTranscoding, policy.EnableVideoPlaybackTranscoding);
+ user.SetPermission(PermissionKind.EnableContentDeletion, policy.EnableContentDeletion);
+ user.SetPermission(PermissionKind.EnableContentDownloading, policy.EnableContentDownloading);
+ user.SetPermission(PermissionKind.EnableSyncTranscoding, policy.EnableSyncTranscoding);
+ user.SetPermission(PermissionKind.EnableMediaConversion, policy.EnableMediaConversion);
+ user.SetPermission(PermissionKind.EnableAllChannels, policy.EnableAllChannels);
+ user.SetPermission(PermissionKind.EnableAllDevices, policy.EnableAllDevices);
+ user.SetPermission(PermissionKind.EnableAllFolders, policy.EnableAllFolders);
+ user.SetPermission(PermissionKind.EnableRemoteControlOfOtherUsers, policy.EnableRemoteControlOfOtherUsers);
+ user.SetPermission(PermissionKind.EnablePlaybackRemuxing, policy.EnablePlaybackRemuxing);
+ user.SetPermission(PermissionKind.ForceRemoteSourceTranscoding, policy.ForceRemoteSourceTranscoding);
+ user.SetPermission(PermissionKind.EnablePublicSharing, policy.EnablePublicSharing);
+
+ foreach (var policyAccessSchedule in policy.AccessSchedules)
+ {
+ user.AccessSchedules.Add(policyAccessSchedule);
+ }
+
+ user.SetPreference(PreferenceKind.BlockedTags, policy.BlockedTags);
+ user.SetPreference(PreferenceKind.EnabledChannels, policy.EnabledChannels);
+ user.SetPreference(PreferenceKind.EnabledDevices, policy.EnabledDevices);
+ user.SetPreference(PreferenceKind.EnabledFolders, policy.EnabledFolders);
+ user.SetPreference(PreferenceKind.EnableContentDeletionFromFolders, policy.EnableContentDeletionFromFolders);
+ user.SetPreference(PreferenceKind.OrderedViews, config.OrderedViews);
+ user.SetPreference(PreferenceKind.GroupedFolders, config.GroupedFolders);
+ user.SetPreference(PreferenceKind.MyMediaExcludes, config.MyMediaExcludes);
+ user.SetPreference(PreferenceKind.LatestItemExcludes, config.LatestItemsExcludes);
+
+ dbContext.Users.Add(user);
+ }
+
+ dbContext.SaveChanges();
+ }
+
+ try
+ {
+ File.Move(Path.Combine(dataPath, DbFilename), Path.Combine(dataPath, DbFilename + ".old"));
+
+ var journalPath = Path.Combine(dataPath, DbFilename + "-journal");
+ if (File.Exists(journalPath))
+ {
+ File.Move(journalPath, Path.Combine(dataPath, DbFilename + ".old-journal"));
+ }
+ }
+ catch (IOException e)
+ {
+ _logger.LogError(e, "Error renaming legacy user database to 'users.db.old'");
+ }
+ }
+
+#nullable disable
+ internal class UserMockup
+ {
+ public string Password { get; set; }
+
+ public string EasyPassword { get; set; }
+
+ public DateTime? LastLoginDate { get; set; }
+
+ public DateTime? LastActivityDate { get; set; }
+
+ public string Name { get; set; }
+
+ public ItemImageInfo[] ImageInfos { get; set; }
+ }
+ }
+}
diff --git a/Jellyfin.Server/Migrations/Routines/RemoveDuplicateExtras.cs b/Jellyfin.Server/Migrations/Routines/RemoveDuplicateExtras.cs
index e95536388..2ebef0241 100644
--- a/Jellyfin.Server/Migrations/Routines/RemoveDuplicateExtras.cs
+++ b/Jellyfin.Server/Migrations/Routines/RemoveDuplicateExtras.cs
@@ -14,7 +14,7 @@ namespace Jellyfin.Server.Migrations.Routines
internal class RemoveDuplicateExtras : IMigrationRoutine
{
private const string DbFilename = "library.db";
- private readonly ILogger _logger;
+ private readonly ILogger<RemoveDuplicateExtras> _logger;
private readonly IServerApplicationPaths _paths;
public RemoveDuplicateExtras(ILogger<RemoveDuplicateExtras> logger, IServerApplicationPaths paths)
diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs
index b9895386f..7c693f8c3 100644
--- a/Jellyfin.Server/Program.cs
+++ b/Jellyfin.Server/Program.cs
@@ -40,12 +40,12 @@ namespace Jellyfin.Server
/// <summary>
/// The name of logging configuration file containing application defaults.
/// </summary>
- public static readonly string LoggingConfigFileDefault = "logging.default.json";
+ public const string LoggingConfigFileDefault = "logging.default.json";
/// <summary>
/// The name of the logging configuration file containing the system-specific override settings.
/// </summary>
- public static readonly string LoggingConfigFileSystem = "logging.json";
+ public const string LoggingConfigFileSystem = "logging.json";
private static readonly CancellationTokenSource _tokenSource = new CancellationTokenSource();
private static readonly ILoggerFactory _loggerFactory = new SerilogLoggerFactory();
diff --git a/Jellyfin.Server/StartupOptions.cs b/Jellyfin.Server/StartupOptions.cs
index 6e15d058f..cc250b06e 100644
--- a/Jellyfin.Server/StartupOptions.cs
+++ b/Jellyfin.Server/StartupOptions.cs
@@ -1,6 +1,9 @@
+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;
@@ -80,6 +83,10 @@ namespace Jellyfin.Server
[Option("plugin-manifest-url", Required = false, HelpText = "A custom URL for the plugin repository JSON manifest")]
public string? PluginManifestUrl { get; set; }
+ /// <inheritdoc />
+ [Option("published-server-url", Required = false, HelpText = "Jellyfin Server URL to publish via auto discover process")]
+ public Uri? PublishedServerUrl { get; set; }
+
/// <summary>
/// Gets the command line options as a dictionary that can be used in the .NET configuration system.
/// </summary>
@@ -98,6 +105,11 @@ namespace Jellyfin.Server
config.Add(ConfigurationExtensions.HostWebClientKey, bool.FalseString);
}
+ if (PublishedServerUrl != null)
+ {
+ config.Add(UdpServer.AddressOverrideConfigKey, PublishedServerUrl.ToString());
+ }
+
return config;
}
}
diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs
index 6691080bc..9e651fb19 100644
--- a/MediaBrowser.Api/ApiEntryPoint.cs
+++ b/MediaBrowser.Api/ApiEntryPoint.cs
@@ -31,7 +31,7 @@ namespace MediaBrowser.Api
/// <summary>
/// The logger.
/// </summary>
- private ILogger _logger;
+ private ILogger<ApiEntryPoint> _logger;
/// <summary>
/// The configuration manager.
@@ -284,8 +284,8 @@ namespace MediaBrowser.Api
Width = state.OutputWidth,
Height = state.OutputHeight,
AudioChannels = state.OutputAudioChannels,
- IsAudioDirect = string.Equals(state.OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase),
- IsVideoDirect = string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase),
+ IsAudioDirect = EncodingHelper.IsCopyCodec(state.OutputAudioCodec),
+ IsVideoDirect = EncodingHelper.IsCopyCodec(state.OutputVideoCodec),
TranscodeReasons = state.TranscodeReasons
});
}
diff --git a/MediaBrowser.Api/BaseApiService.cs b/MediaBrowser.Api/BaseApiService.cs
index 2cd68ac1b..2ece16ee1 100644
--- a/MediaBrowser.Api/BaseApiService.cs
+++ b/MediaBrowser.Api/BaseApiService.cs
@@ -1,6 +1,7 @@
using System;
using System.IO;
using System.Linq;
+using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
@@ -94,8 +95,8 @@ namespace MediaBrowser.Api
var authenticatedUser = auth.User;
// If they're going to update the record of another user, they must be an administrator
- if ((!userId.Equals(auth.UserId) && !authenticatedUser.Policy.IsAdministrator)
- || (restrictUserPreferences && !authenticatedUser.Policy.EnableUserPreferenceAccess))
+ if ((!userId.Equals(auth.UserId) && !authenticatedUser.HasPermission(PermissionKind.IsAdministrator))
+ || (restrictUserPreferences && !authenticatedUser.EnableUserPreferenceAccess))
{
throw new SecurityException("Unauthorized access.");
}
diff --git a/MediaBrowser.Api/Devices/DeviceService.cs b/MediaBrowser.Api/Devices/DeviceService.cs
index 53eb9667d..dd3f3e738 100644
--- a/MediaBrowser.Api/Devices/DeviceService.cs
+++ b/MediaBrowser.Api/Devices/DeviceService.cs
@@ -1,4 +1,3 @@
-using System.IO;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Net;
@@ -40,33 +39,6 @@ namespace MediaBrowser.Api.Devices
public string Id { get; set; }
}
- [Route("/Devices/CameraUploads", "GET", Summary = "Gets camera upload history for a device")]
- [Authenticated]
- public class GetCameraUploads : IReturn<ContentUploadHistory>
- {
- [ApiMember(Name = "Id", Description = "Device Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string DeviceId { get; set; }
- }
-
- [Route("/Devices/CameraUploads", "POST", Summary = "Uploads content")]
- [Authenticated]
- public class PostCameraUpload : IRequiresRequestStream, IReturnVoid
- {
- [ApiMember(Name = "DeviceId", Description = "Device Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
- public string DeviceId { get; set; }
-
- [ApiMember(Name = "Album", Description = "Album", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
- public string Album { get; set; }
-
- [ApiMember(Name = "Name", Description = "Name", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
- public string Name { get; set; }
-
- [ApiMember(Name = "Id", Description = "Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
- public string Id { get; set; }
-
- public Stream RequestStream { get; set; }
- }
-
[Route("/Devices/Options", "POST", Summary = "Updates device options")]
[Authenticated(Roles = "Admin")]
public class PostDeviceOptions : DeviceOptions, IReturnVoid
diff --git a/MediaBrowser.Api/EnvironmentService.cs b/MediaBrowser.Api/EnvironmentService.cs
index d199ce154..82d471412 100644
--- a/MediaBrowser.Api/EnvironmentService.cs
+++ b/MediaBrowser.Api/EnvironmentService.cs
@@ -226,12 +226,7 @@ namespace MediaBrowser.Api
/// <returns>IEnumerable{FileSystemEntryInfo}.</returns>
private IEnumerable<FileSystemEntryInfo> GetDrives()
{
- return _fileSystem.GetDrives().Select(d => new FileSystemEntryInfo
- {
- Name = d.Name,
- Path = d.FullName,
- Type = FileSystemEntryType.Directory
- });
+ return _fileSystem.GetDrives().Select(d => new FileSystemEntryInfo(d.Name, d.FullName, FileSystemEntryType.Directory));
}
/// <summary>
@@ -261,13 +256,7 @@ namespace MediaBrowser.Api
return request.IncludeDirectories || !isDirectory;
});
- return entries.Select(f => new FileSystemEntryInfo
- {
- Name = f.Name,
- Path = f.FullName,
- Type = f.IsDirectory ? FileSystemEntryType.Directory : FileSystemEntryType.File
-
- });
+ return entries.Select(f => new FileSystemEntryInfo(f.Name, f.FullName, f.IsDirectory ? FileSystemEntryType.Directory : FileSystemEntryType.File));
}
public object Get(GetParentPath request)
diff --git a/MediaBrowser.Api/FilterService.cs b/MediaBrowser.Api/FilterService.cs
index 5eb72cdb1..bd67ec41f 100644
--- a/MediaBrowser.Api/FilterService.cs
+++ b/MediaBrowser.Api/FilterService.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs
index 2e9b3e6cb..0b8ddeacd 100644
--- a/MediaBrowser.Api/Images/ImageService.cs
+++ b/MediaBrowser.Api/Images/ImageService.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
+using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Extensions;
@@ -17,9 +18,11 @@ using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Net;
using MediaBrowser.Model.Services;
using Microsoft.Extensions.Logging;
using Microsoft.Net.Http.Headers;
+using User = Jellyfin.Data.Entities.User;
namespace MediaBrowser.Api.Images
{
@@ -280,9 +283,16 @@ namespace MediaBrowser.Api.Images
public List<ImageInfo> GetItemImageInfos(BaseItem item)
{
var list = new List<ImageInfo>();
-
var itemImages = item.ImageInfos;
+ if (itemImages.Length == 0)
+ {
+ // short-circuit
+ return list;
+ }
+
+ _libraryManager.UpdateImages(item); // this makes sure dimensions and hashes are correct
+
foreach (var image in itemImages)
{
if (!item.AllowsMultipleImages(image.Type))
@@ -323,6 +333,7 @@ namespace MediaBrowser.Api.Images
{
int? width = null;
int? height = null;
+ string blurhash = null;
long length = 0;
try
@@ -332,10 +343,9 @@ namespace MediaBrowser.Api.Images
var fileInfo = _fileSystem.GetFileInfo(info.Path);
length = fileInfo.Length;
- ImageDimensions size = _imageProcessor.GetImageDimensions(item, info);
- _libraryManager.UpdateImages(item);
- width = size.Width;
- height = size.Height;
+ blurhash = info.BlurHash;
+ width = info.Width;
+ height = info.Height;
if (width <= 0 || height <= 0)
{
@@ -358,6 +368,7 @@ namespace MediaBrowser.Api.Images
ImageType = info.Type,
ImageTag = _imageProcessor.GetImageCacheTag(item, info),
Size = length,
+ BlurHash = blurhash,
Width = width,
Height = height
};
@@ -399,14 +410,14 @@ namespace MediaBrowser.Api.Images
{
var item = _userManager.GetUserById(request.Id);
- return GetImage(request, Guid.Empty, item, false);
+ return GetImage(request, item, false);
}
public object Head(GetUserImage request)
{
var item = _userManager.GetUserById(request.Id);
- return GetImage(request, Guid.Empty, item, true);
+ return GetImage(request, item, true);
}
public object Get(GetItemByNameImage request)
@@ -439,9 +450,9 @@ namespace MediaBrowser.Api.Images
request.Type = Enum.Parse<ImageType>(GetPathValue(3).ToString(), true);
- var item = _userManager.GetUserById(id);
+ var user = _userManager.GetUserById(id);
- return PostImage(item, request.RequestStream, request.Type, Request.ContentType);
+ return PostImage(user, request.RequestStream, Request.ContentType);
}
/// <summary>
@@ -468,9 +479,17 @@ namespace MediaBrowser.Api.Images
var userId = request.Id;
AssertCanUpdateUser(_authContext, _userManager, userId, true);
- var item = _userManager.GetUserById(userId);
+ var user = _userManager.GetUserById(userId);
+ try
+ {
+ File.Delete(user.ProfileImage.Path);
+ }
+ catch (IOException e)
+ {
+ Logger.LogError(e, "Error deleting user profile image:");
+ }
- item.DeleteImage(request.Type, request.Index ?? 0);
+ _userManager.ClearProfileImage(user);
}
/// <summary>
@@ -555,18 +574,17 @@ namespace MediaBrowser.Api.Images
var imageInfo = GetImageInfo(request, item);
if (imageInfo == null)
{
- var displayText = item == null ? itemId.ToString() : item.Name;
- throw new ResourceNotFoundException(string.Format("{0} does not have an image of type {1}", displayText, request.Type));
+ throw new ResourceNotFoundException(string.Format("{0} does not have an image of type {1}", item.Name, request.Type));
}
- bool cropwhitespace;
+ bool cropWhitespace;
if (request.CropWhitespace.HasValue)
{
- cropwhitespace = request.CropWhitespace.Value;
+ cropWhitespace = request.CropWhitespace.Value;
}
else
{
- cropwhitespace = request.Type == ImageType.Logo || request.Type == ImageType.Art;
+ cropWhitespace = request.Type == ImageType.Logo || request.Type == ImageType.Art;
}
var outputFormats = GetOutputFormats(request);
@@ -589,7 +607,35 @@ namespace MediaBrowser.Api.Images
itemId,
request,
imageInfo,
- cropwhitespace,
+ cropWhitespace,
+ outputFormats,
+ cacheDuration,
+ responseHeaders,
+ isHeadRequest);
+ }
+
+ public Task<object> GetImage(ImageRequest request, User user, bool isHeadRequest)
+ {
+ var imageInfo = GetImageInfo(request, user);
+
+ TimeSpan? cacheDuration = null;
+
+ if (!string.IsNullOrEmpty(request.Tag))
+ {
+ cacheDuration = TimeSpan.FromDays(365);
+ }
+
+ var responseHeaders = new Dictionary<string, string>
+ {
+ {"transferMode.dlna.org", "Interactive"},
+ {"realTimeInfo.dlna.org", "DLNA.ORG_TLAG=*"}
+ };
+
+ var outputFormats = GetOutputFormats(request);
+
+ return GetImageResult(user.Id,
+ request,
+ imageInfo,
outputFormats,
cacheDuration,
responseHeaders,
@@ -597,6 +643,55 @@ namespace MediaBrowser.Api.Images
}
private async Task<object> GetImageResult(
+ Guid itemId,
+ ImageRequest request,
+ ItemImageInfo info,
+ IReadOnlyCollection<ImageFormat> supportedFormats,
+ TimeSpan? cacheDuration,
+ IDictionary<string, string> headers,
+ bool isHeadRequest)
+ {
+ info.Type = ImageType.Profile;
+ var options = new ImageProcessingOptions
+ {
+ CropWhiteSpace = true,
+ Height = request.Height,
+ ImageIndex = request.Index ?? 0,
+ Image = info,
+ Item = null, // Hack alert
+ ItemId = itemId,
+ MaxHeight = request.MaxHeight,
+ MaxWidth = request.MaxWidth,
+ Quality = request.Quality ?? 100,
+ Width = request.Width,
+ AddPlayedIndicator = request.AddPlayedIndicator,
+ PercentPlayed = 0,
+ UnplayedCount = request.UnplayedCount,
+ Blur = request.Blur,
+ BackgroundColor = request.BackgroundColor,
+ ForegroundLayer = request.ForegroundLayer,
+ SupportedOutputFormats = supportedFormats
+ };
+
+ var imageResult = await _imageProcessor.ProcessImage(options).ConfigureAwait(false);
+
+ headers[HeaderNames.Vary] = HeaderNames.Accept;
+
+ return await ResultFactory.GetStaticFileResult(Request, new StaticFileResultOptions
+ {
+ CacheDuration = cacheDuration,
+ ResponseHeaders = headers,
+ ContentType = imageResult.Item2,
+ DateLastModified = imageResult.Item3,
+ IsHeadRequest = isHeadRequest,
+ Path = imageResult.Item1,
+
+ FileShare = FileShare.Read
+
+ }).ConfigureAwait(false);
+ }
+
+ private async Task<object> GetImageResult(
BaseItem item,
Guid itemId,
ImageRequest request,
@@ -733,13 +828,35 @@ namespace MediaBrowser.Api.Images
/// <param name="request">The request.</param>
/// <param name="item">The item.</param>
/// <returns>System.String.</returns>
- private ItemImageInfo GetImageInfo(ImageRequest request, BaseItem item)
+ private static ItemImageInfo GetImageInfo(ImageRequest request, BaseItem item)
{
var index = request.Index ?? 0;
return item.GetImageInfo(request.Type, index);
}
+ private static ItemImageInfo GetImageInfo(ImageRequest request, User user)
+ {
+ var info = new ItemImageInfo
+ {
+ Path = user.ProfileImage.Path,
+ Type = ImageType.Primary,
+ DateModified = user.ProfileImage.LastModified,
+ };
+
+ if (request.Width.HasValue)
+ {
+ info.Width = request.Width.Value;
+ }
+
+ if (request.Height.HasValue)
+ {
+ info.Height = request.Height.Value;
+ }
+
+ return info;
+ }
+
/// <summary>
/// Posts the image.
/// </summary>
@@ -750,22 +867,41 @@ namespace MediaBrowser.Api.Images
/// <returns>Task.</returns>
public async Task PostImage(BaseItem entity, Stream inputStream, ImageType imageType, string mimeType)
{
+ var memoryStream = await GetMemoryStream(inputStream);
+
+ // Handle image/png; charset=utf-8
+ mimeType = mimeType.Split(';').FirstOrDefault();
+
+ await _providerManager.SaveImage(entity, memoryStream, mimeType, imageType, null, CancellationToken.None).ConfigureAwait(false);
+
+ entity.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None);
+ }
+
+ private static async Task<MemoryStream> GetMemoryStream(Stream inputStream)
+ {
using var reader = new StreamReader(inputStream);
var text = await reader.ReadToEndAsync().ConfigureAwait(false);
var bytes = Convert.FromBase64String(text);
-
- var memoryStream = new MemoryStream(bytes)
+ return new MemoryStream(bytes)
{
Position = 0
};
+ }
+
+ private async Task PostImage(User user, Stream inputStream, string mimeType)
+ {
+ var memoryStream = await GetMemoryStream(inputStream);
// Handle image/png; charset=utf-8
mimeType = mimeType.Split(';').FirstOrDefault();
+ var userDataPath = Path.Combine(ServerConfigurationManager.ApplicationPaths.UserConfigurationDirectoryPath, user.Username);
+ user.ProfileImage = new Jellyfin.Data.Entities.ImageInfo(Path.Combine(userDataPath, "profile" + MimeTypes.ToExtension(mimeType)));
- await _providerManager.SaveImage(entity, memoryStream, mimeType, imageType, null, CancellationToken.None).ConfigureAwait(false);
-
- entity.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None);
+ await _providerManager
+ .SaveImage(user, memoryStream, mimeType, user.ProfileImage.Path)
+ .ConfigureAwait(false);
+ await _userManager.UpdateUserAsync(user);
}
}
}
diff --git a/MediaBrowser.Api/Images/RemoteImageService.cs b/MediaBrowser.Api/Images/RemoteImageService.cs
index 23bf54712..358ac30fa 100644
--- a/MediaBrowser.Api/Images/RemoteImageService.cs
+++ b/MediaBrowser.Api/Images/RemoteImageService.cs
@@ -147,9 +147,8 @@ namespace MediaBrowser.Api.Images
{
var item = _libraryManager.GetItemById(request.Id);
- var images = await _providerManager.GetAvailableRemoteImages(item, new RemoteImageQuery
+ var images = await _providerManager.GetAvailableRemoteImages(item, new RemoteImageQuery(request.ProviderName)
{
- ProviderName = request.ProviderName,
IncludeAllLanguages = request.IncludeAllLanguages,
IncludeDisabledProviders = true,
ImageType = request.Type
diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs
index 5bc9cffa0..e96875403 100644
--- a/MediaBrowser.Api/Library/LibraryService.cs
+++ b/MediaBrowser.Api/Library/LibraryService.cs
@@ -6,6 +6,7 @@ using System.Net;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
using MediaBrowser.Api.Movies;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Progress;
@@ -14,7 +15,6 @@ 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.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Providers;
@@ -27,6 +27,12 @@ using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Services;
using Microsoft.Extensions.Logging;
using Microsoft.Net.Http.Headers;
+using Book = MediaBrowser.Controller.Entities.Book;
+using Episode = MediaBrowser.Controller.Entities.TV.Episode;
+using MetadataProvider = MediaBrowser.Model.Entities.MetadataProvider;
+using Movie = MediaBrowser.Controller.Entities.Movies.Movie;
+using MusicAlbum = MediaBrowser.Controller.Entities.Audio.MusicAlbum;
+using Series = MediaBrowser.Controller.Entities.TV.Series;
namespace MediaBrowser.Api.Library
{
@@ -655,7 +661,7 @@ namespace MediaBrowser.Api.Library
EnableImages = false
}
- }).Where(i => string.Equals(request.TvdbId, i.GetProviderId(MetadataProviders.Tvdb), StringComparison.OrdinalIgnoreCase)).ToArray();
+ }).Where(i => string.Equals(request.TvdbId, i.GetProviderId(MetadataProvider.Tvdb), StringComparison.OrdinalIgnoreCase)).ToArray();
foreach (var item in series)
{
@@ -688,11 +694,11 @@ namespace MediaBrowser.Api.Library
if (!string.IsNullOrWhiteSpace(request.ImdbId))
{
- movies = movies.Where(i => string.Equals(request.ImdbId, i.GetProviderId(MetadataProviders.Imdb), StringComparison.OrdinalIgnoreCase)).ToList();
+ movies = movies.Where(i => string.Equals(request.ImdbId, i.GetProviderId(MetadataProvider.Imdb), StringComparison.OrdinalIgnoreCase)).ToList();
}
else if (!string.IsNullOrWhiteSpace(request.TmdbId))
{
- movies = movies.Where(i => string.Equals(request.TmdbId, i.GetProviderId(MetadataProviders.Tmdb), StringComparison.OrdinalIgnoreCase)).ToList();
+ movies = movies.Where(i => string.Equals(request.TmdbId, i.GetProviderId(MetadataProvider.Tmdb), StringComparison.OrdinalIgnoreCase)).ToList();
}
else
{
@@ -763,8 +769,8 @@ namespace MediaBrowser.Api.Library
{
try
{
- _activityManager.Create(new Jellyfin.Data.Entities.ActivityLog(
- string.Format(_localization.GetLocalizedString("UserDownloadingItemWithValues"), user.Name, item.Name),
+ _activityManager.Create(new ActivityLog(
+ string.Format(_localization.GetLocalizedString("UserDownloadingItemWithValues"), user.Username, item.Name),
"UserDownloadingContent",
auth.UserId)
{
diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs
index 5fe4c0cca..279fd6ee9 100644
--- a/MediaBrowser.Api/LiveTv/LiveTvService.cs
+++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs
@@ -7,6 +7,7 @@ using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Enums;
using MediaBrowser.Api.UserLibrary;
using MediaBrowser.Common;
using MediaBrowser.Common.Configuration;
@@ -859,7 +860,7 @@ namespace MediaBrowser.Api.LiveTv
throw new SecurityException("Anonymous live tv management is not allowed.");
}
- if (!user.Policy.EnableLiveTvManagement)
+ if (!user.HasPermission(PermissionKind.EnableLiveTvManagement))
{
throw new SecurityException("The current user does not have permission to manage live tv.");
}
diff --git a/MediaBrowser.Api/Movies/MoviesService.cs b/MediaBrowser.Api/Movies/MoviesService.cs
index cdd027634..2d61299c7 100644
--- a/MediaBrowser.Api/Movies/MoviesService.cs
+++ b/MediaBrowser.Api/Movies/MoviesService.cs
@@ -2,11 +2,11 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
+using Jellyfin.Data.Entities;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Net;
@@ -15,6 +15,8 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Services;
using Microsoft.Extensions.Logging;
+using MetadataProvider = MediaBrowser.Model.Entities.MetadataProvider;
+using Movie = MediaBrowser.Controller.Entities.Movies.Movie;
namespace MediaBrowser.Api.Movies
{
@@ -251,7 +253,12 @@ namespace MediaBrowser.Api.Movies
return categories.OrderBy(i => i.RecommendationType);
}
- private IEnumerable<RecommendationDto> GetWithDirector(User user, IEnumerable<string> names, int itemLimit, DtoOptions dtoOptions, RecommendationType type)
+ private IEnumerable<RecommendationDto> GetWithDirector(
+ User user,
+ IEnumerable<string> names,
+ int itemLimit,
+ DtoOptions dtoOptions,
+ RecommendationType type)
{
var itemTypes = new List<string> { typeof(Movie).Name };
if (ServerConfigurationManager.Configuration.EnableExternalContentInSuggestions)
@@ -273,7 +280,7 @@ namespace MediaBrowser.Api.Movies
EnableGroupByMetadataKey = true,
DtoOptions = dtoOptions
- }).GroupBy(i => i.GetProviderId(MetadataProviders.Imdb) ?? Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture))
+ }).GroupBy(i => i.GetProviderId(MetadataProvider.Imdb) ?? Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture))
.Select(x => x.First())
.Take(itemLimit)
.ToList();
@@ -314,7 +321,7 @@ namespace MediaBrowser.Api.Movies
EnableGroupByMetadataKey = true,
DtoOptions = dtoOptions
- }).GroupBy(i => i.GetProviderId(MetadataProviders.Imdb) ?? Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture))
+ }).GroupBy(i => i.GetProviderId(MetadataProvider.Imdb) ?? Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture))
.Select(x => x.First())
.Take(itemLimit)
.ToList();
diff --git a/MediaBrowser.Api/Music/AlbumsService.cs b/MediaBrowser.Api/Music/AlbumsService.cs
index 58c95d053..f257d1014 100644
--- a/MediaBrowser.Api/Music/AlbumsService.cs
+++ b/MediaBrowser.Api/Music/AlbumsService.cs
@@ -67,12 +67,13 @@ namespace MediaBrowser.Api.Music
{
var dtoOptions = GetDtoOptions(_authContext, request);
- var result = SimilarItemsHelper.GetSimilarItemsResult(dtoOptions, _userManager,
+ var result = SimilarItemsHelper.GetSimilarItemsResult(
+ dtoOptions,
+ _userManager,
_itemRepo,
_libraryManager,
_userDataRepository,
_dtoService,
- Logger,
request, new[] { typeof(MusicArtist) },
SimilarItemsHelper.GetSimiliarityScore);
@@ -88,12 +89,13 @@ namespace MediaBrowser.Api.Music
{
var dtoOptions = GetDtoOptions(_authContext, request);
- var result = SimilarItemsHelper.GetSimilarItemsResult(dtoOptions, _userManager,
+ var result = SimilarItemsHelper.GetSimilarItemsResult(
+ dtoOptions,
+ _userManager,
_itemRepo,
_libraryManager,
_userDataRepository,
_dtoService,
- Logger,
request, new[] { typeof(MusicAlbum) },
GetAlbumSimilarityScore);
diff --git a/MediaBrowser.Api/Music/InstantMixService.cs b/MediaBrowser.Api/Music/InstantMixService.cs
index cacec8d64..7d10c9427 100644
--- a/MediaBrowser.Api/Music/InstantMixService.cs
+++ b/MediaBrowser.Api/Music/InstantMixService.cs
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Linq;
+using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index f796aa486..957e4ae91 100644
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs
@@ -7,6 +7,7 @@ using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Enums;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration;
@@ -193,10 +194,10 @@ namespace MediaBrowser.Api.Playback
await AcquireResources(state, cancellationTokenSource).ConfigureAwait(false);
- if (state.VideoRequest != null && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
+ if (state.VideoRequest != null && !EncodingHelper.IsCopyCodec(state.OutputVideoCodec))
{
var auth = AuthorizationContext.GetAuthorizationInfo(Request);
- if (auth.User != null && !auth.User.Policy.EnableVideoPlaybackTranscoding)
+ if (auth.User != null && !auth.User.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding))
{
ApiEntryPoint.Instance.OnTranscodeFailedToStart(outputPath, TranscodingJobType, state);
@@ -243,9 +244,9 @@ namespace MediaBrowser.Api.Playback
var logFilePrefix = "ffmpeg-transcode";
if (state.VideoRequest != null
- && string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
+ && EncodingHelper.IsCopyCodec(state.OutputVideoCodec))
{
- logFilePrefix = string.Equals(state.OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase)
+ logFilePrefix = EncodingHelper.IsCopyCodec(state.OutputAudioCodec)
? "ffmpeg-remux" : "ffmpeg-directstream";
}
@@ -328,7 +329,7 @@ namespace MediaBrowser.Api.Playback
state.RunTimeTicks.Value >= TimeSpan.FromMinutes(5).Ticks &&
state.IsInputVideo &&
state.VideoType == VideoType.VideoFile &&
- !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase);
+ !EncodingHelper.IsCopyCodec(state.OutputVideoCodec);
}
return false;
@@ -791,7 +792,7 @@ namespace MediaBrowser.Api.Playback
EncodingHelper.TryStreamCopy(state);
}
- if (state.OutputVideoBitrate.HasValue && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
+ if (state.OutputVideoBitrate.HasValue && !EncodingHelper.IsCopyCodec(state.OutputVideoCodec))
{
var resolution = ResolutionNormalizer.Normalize(
state.VideoStream?.BitRate,
diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
index 061316cb8..0a6ed2786 100644
--- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
@@ -700,12 +700,12 @@ namespace MediaBrowser.Api.Playback.Hls
return false;
}
- if (string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
+ if (EncodingHelper.IsCopyCodec(state.OutputVideoCodec))
{
return false;
}
- if (string.Equals(state.OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase))
+ if (EncodingHelper.IsCopyCodec(state.OutputAudioCodec))
{
return false;
}
@@ -728,7 +728,7 @@ namespace MediaBrowser.Api.Playback.Hls
private int? GetOutputVideoCodecLevel(StreamState state)
{
string levelString;
- if (string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)
+ if (EncodingHelper.IsCopyCodec(state.OutputVideoCodec)
&& state.VideoStream.Level.HasValue)
{
levelString = state.VideoStream?.Level.ToString();
@@ -865,16 +865,15 @@ namespace MediaBrowser.Api.Playback.Hls
{
framerate = Math.Round(state.TargetFramerate.GetValueOrDefault(), 3);
}
- else if (state.VideoStream.RealFrameRate.HasValue)
+ else if (state.VideoStream?.RealFrameRate != null)
{
framerate = Math.Round(state.VideoStream.RealFrameRate.GetValueOrDefault(), 3);
}
if (framerate.HasValue)
{
- builder.Append(",FRAME-RATE=\"")
- .Append(framerate.Value)
- .Append('"');
+ builder.Append(",FRAME-RATE=")
+ .Append(framerate.Value);
}
}
@@ -888,11 +887,10 @@ namespace MediaBrowser.Api.Playback.Hls
{
if (state.OutputWidth.HasValue && state.OutputHeight.HasValue)
{
- builder.Append(",RESOLUTION=\"")
+ builder.Append(",RESOLUTION=")
.Append(state.OutputWidth.GetValueOrDefault())
.Append('x')
- .Append(state.OutputHeight.GetValueOrDefault())
- .Append('"');
+ .Append(state.OutputHeight.GetValueOrDefault());
}
}
@@ -1008,7 +1006,7 @@ namespace MediaBrowser.Api.Playback.Hls
if (!state.IsOutputVideo)
{
- if (string.Equals(audioCodec, "copy", StringComparison.OrdinalIgnoreCase))
+ if (EncodingHelper.IsCopyCodec(audioCodec))
{
return "-acodec copy";
}
@@ -1036,11 +1034,11 @@ namespace MediaBrowser.Api.Playback.Hls
return string.Join(" ", audioTranscodeParams.ToArray());
}
- if (string.Equals(audioCodec, "copy", StringComparison.OrdinalIgnoreCase))
+ if (EncodingHelper.IsCopyCodec(audioCodec))
{
var videoCodec = EncodingHelper.GetVideoEncoder(state, encodingOptions);
- if (string.Equals(videoCodec, "copy", StringComparison.OrdinalIgnoreCase) && state.EnableBreakOnNonKeyFrames(videoCodec))
+ if (EncodingHelper.IsCopyCodec(videoCodec) && state.EnableBreakOnNonKeyFrames(videoCodec))
{
return "-codec:a:0 copy -copypriorss:a:0 0";
}
@@ -1091,7 +1089,7 @@ namespace MediaBrowser.Api.Playback.Hls
// }
// See if we can save come cpu cycles by avoiding encoding
- if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
+ if (EncodingHelper.IsCopyCodec(codec))
{
if (state.VideoStream != null && !string.Equals(state.VideoStream.NalLengthSize, "0", StringComparison.OrdinalIgnoreCase))
{
diff --git a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
index d1c53c1c1..aefb3f019 100644
--- a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
@@ -72,7 +72,7 @@ namespace MediaBrowser.Api.Playback.Hls
{
var codec = EncodingHelper.GetAudioEncoder(state);
- if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
+ if (EncodingHelper.IsCopyCodec(codec))
{
return "-codec:a:0 copy";
}
diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs
index e2d771ec6..2c6534cc0 100644
--- a/MediaBrowser.Api/Playback/MediaInfoService.cs
+++ b/MediaBrowser.Api/Playback/MediaInfoService.cs
@@ -9,6 +9,7 @@ using System.Linq;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Enums;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices;
@@ -400,21 +401,24 @@ namespace MediaBrowser.Api.Playback
if (item is Audio)
{
- Logger.LogInformation("User policy for {0}. EnableAudioPlaybackTranscoding: {1}", user.Name, user.Policy.EnableAudioPlaybackTranscoding);
+ Logger.LogInformation(
+ "User policy for {0}. EnableAudioPlaybackTranscoding: {1}",
+ user.Username,
+ user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding));
}
else
{
Logger.LogInformation("User policy for {0}. EnablePlaybackRemuxing: {1} EnableVideoPlaybackTranscoding: {2} EnableAudioPlaybackTranscoding: {3}",
- user.Name,
- user.Policy.EnablePlaybackRemuxing,
- user.Policy.EnableVideoPlaybackTranscoding,
- user.Policy.EnableAudioPlaybackTranscoding);
+ user.Username,
+ user.HasPermission(PermissionKind.EnablePlaybackRemuxing),
+ user.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding),
+ user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding));
}
// Beginning of Playback Determination: Attempt DirectPlay first
if (mediaSource.SupportsDirectPlay)
{
- if (mediaSource.IsRemote && user.Policy.ForceRemoteSourceTranscoding)
+ if (mediaSource.IsRemote && user.HasPermission(PermissionKind.ForceRemoteSourceTranscoding))
{
mediaSource.SupportsDirectPlay = false;
}
@@ -428,14 +432,16 @@ namespace MediaBrowser.Api.Playback
if (item is Audio)
{
- if (!user.Policy.EnableAudioPlaybackTranscoding)
+ if (!user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding))
{
options.ForceDirectPlay = true;
}
}
else if (item is Video)
{
- if (!user.Policy.EnableAudioPlaybackTranscoding && !user.Policy.EnableVideoPlaybackTranscoding && !user.Policy.EnablePlaybackRemuxing)
+ if (!user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding)
+ && !user.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding)
+ && !user.HasPermission(PermissionKind.EnablePlaybackRemuxing))
{
options.ForceDirectPlay = true;
}
@@ -463,7 +469,7 @@ namespace MediaBrowser.Api.Playback
if (mediaSource.SupportsDirectStream)
{
- if (mediaSource.IsRemote && user.Policy.ForceRemoteSourceTranscoding)
+ if (mediaSource.IsRemote && user.HasPermission(PermissionKind.ForceRemoteSourceTranscoding))
{
mediaSource.SupportsDirectStream = false;
}
@@ -473,14 +479,16 @@ namespace MediaBrowser.Api.Playback
if (item is Audio)
{
- if (!user.Policy.EnableAudioPlaybackTranscoding)
+ if (!user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding))
{
options.ForceDirectStream = true;
}
}
else if (item is Video)
{
- if (!user.Policy.EnableAudioPlaybackTranscoding && !user.Policy.EnableVideoPlaybackTranscoding && !user.Policy.EnablePlaybackRemuxing)
+ if (!user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding)
+ && !user.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding)
+ && !user.HasPermission(PermissionKind.EnablePlaybackRemuxing))
{
options.ForceDirectStream = true;
}
@@ -512,7 +520,7 @@ namespace MediaBrowser.Api.Playback
? streamBuilder.BuildAudioItem(options)
: streamBuilder.BuildVideoItem(options);
- if (mediaSource.IsRemote && user.Policy.ForceRemoteSourceTranscoding)
+ if (mediaSource.IsRemote && user.HasPermission(PermissionKind.ForceRemoteSourceTranscoding))
{
if (streamInfo != null)
{
@@ -576,10 +584,10 @@ namespace MediaBrowser.Api.Playback
}
}
- private long? GetMaxBitrate(long? clientMaxBitrate, User user)
+ private long? GetMaxBitrate(long? clientMaxBitrate, Jellyfin.Data.Entities.User user)
{
var maxBitrate = clientMaxBitrate;
- var remoteClientMaxBitrate = user?.Policy.RemoteClientBitrateLimit ?? 0;
+ var remoteClientMaxBitrate = user?.RemoteClientBitrateLimit ?? 0;
if (remoteClientMaxBitrate <= 0)
{
diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs
index d5d2f58c0..c244b0033 100644
--- a/MediaBrowser.Api/Playback/StreamState.cs
+++ b/MediaBrowser.Api/Playback/StreamState.cs
@@ -42,7 +42,7 @@ namespace MediaBrowser.Api.Playback
return Request.SegmentLength.Value;
}
- if (string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
+ if (EncodingHelper.IsCopyCodec(OutputVideoCodec))
{
var userAgent = UserAgent ?? string.Empty;
diff --git a/MediaBrowser.Api/Sessions/SessionInfoWebSocketListener.cs b/MediaBrowser.Api/Sessions/SessionInfoWebSocketListener.cs
index 0e74c9267..175984575 100644
--- a/MediaBrowser.Api/Sessions/SessionInfoWebSocketListener.cs
+++ b/MediaBrowser.Api/Sessions/SessionInfoWebSocketListener.cs
@@ -40,39 +40,39 @@ namespace MediaBrowser.Api.Sessions
_sessionManager.SessionActivity += OnSessionManagerSessionActivity;
}
- private void OnSessionManagerSessionActivity(object sender, SessionEventArgs e)
+ private async void OnSessionManagerSessionActivity(object sender, SessionEventArgs e)
{
- SendData(false);
+ await SendData(false).ConfigureAwait(false);
}
- private void OnSessionManagerCapabilitiesChanged(object sender, SessionEventArgs e)
+ private async void OnSessionManagerCapabilitiesChanged(object sender, SessionEventArgs e)
{
- SendData(true);
+ await SendData(true).ConfigureAwait(false);
}
- private void OnSessionManagerPlaybackProgress(object sender, PlaybackProgressEventArgs e)
+ private async void OnSessionManagerPlaybackProgress(object sender, PlaybackProgressEventArgs e)
{
- SendData(!e.IsAutomated);
+ await SendData(!e.IsAutomated).ConfigureAwait(false);
}
- private void OnSessionManagerPlaybackStopped(object sender, PlaybackStopEventArgs e)
+ private async void OnSessionManagerPlaybackStopped(object sender, PlaybackStopEventArgs e)
{
- SendData(true);
+ await SendData(true).ConfigureAwait(false);
}
- private void OnSessionManagerPlaybackStart(object sender, PlaybackProgressEventArgs e)
+ private async void OnSessionManagerPlaybackStart(object sender, PlaybackProgressEventArgs e)
{
- SendData(true);
+ await SendData(true).ConfigureAwait(false);
}
- private void OnSessionManagerSessionEnded(object sender, SessionEventArgs e)
+ private async void OnSessionManagerSessionEnded(object sender, SessionEventArgs e)
{
- SendData(true);
+ await SendData(true).ConfigureAwait(false);
}
- private void OnSessionManagerSessionStarted(object sender, SessionEventArgs e)
+ private async void OnSessionManagerSessionStarted(object sender, SessionEventArgs e)
{
- SendData(true);
+ await SendData(true).ConfigureAwait(false);
}
/// <summary>
diff --git a/MediaBrowser.Api/Sessions/SessionService.cs b/MediaBrowser.Api/Sessions/SessionService.cs
index 020bb5042..d986eea65 100644
--- a/MediaBrowser.Api/Sessions/SessionService.cs
+++ b/MediaBrowser.Api/Sessions/SessionService.cs
@@ -2,6 +2,7 @@ using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Library;
@@ -326,12 +327,12 @@ namespace MediaBrowser.Api.Sessions
var user = _userManager.GetUserById(request.ControllableByUserId);
- if (!user.Policy.EnableRemoteControlOfOtherUsers)
+ if (!user.HasPermission(PermissionKind.EnableRemoteControlOfOtherUsers))
{
result = result.Where(i => i.UserId.Equals(Guid.Empty) || i.ContainsUser(request.ControllableByUserId));
}
- if (!user.Policy.EnableSharedDeviceControl)
+ if (!user.HasPermission(PermissionKind.EnableSharedDeviceControl))
{
result = result.Where(i => !i.UserId.Equals(Guid.Empty));
}
diff --git a/MediaBrowser.Api/SimilarItemsHelper.cs b/MediaBrowser.Api/SimilarItemsHelper.cs
index 44bb24ef2..dcd22280a 100644
--- a/MediaBrowser.Api/SimilarItemsHelper.cs
+++ b/MediaBrowser.Api/SimilarItemsHelper.cs
@@ -69,7 +69,7 @@ namespace MediaBrowser.Api
/// </summary>
public static class SimilarItemsHelper
{
- internal static QueryResult<BaseItemDto> GetSimilarItemsResult(DtoOptions dtoOptions, IUserManager userManager, IItemRepository itemRepository, ILibraryManager libraryManager, IUserDataManager userDataRepository, IDtoService dtoService, ILogger logger, BaseGetSimilarItemsFromItem request, Type[] includeTypes, Func<BaseItem, List<PersonInfo>, List<PersonInfo>, BaseItem, int> getSimilarityScore)
+ internal static QueryResult<BaseItemDto> GetSimilarItemsResult(DtoOptions dtoOptions, IUserManager userManager, IItemRepository itemRepository, ILibraryManager libraryManager, IUserDataManager userDataRepository, IDtoService dtoService, BaseGetSimilarItemsFromItem request, Type[] includeTypes, Func<BaseItem, List<PersonInfo>, List<PersonInfo>, BaseItem, int> getSimilarityScore)
{
var user = !request.UserId.Equals(Guid.Empty) ? userManager.GetUserById(request.UserId) : null;
diff --git a/MediaBrowser.Api/SuggestionsService.cs b/MediaBrowser.Api/SuggestionsService.cs
index 91f85db6f..32d3bde5c 100644
--- a/MediaBrowser.Api/SuggestionsService.cs
+++ b/MediaBrowser.Api/SuggestionsService.cs
@@ -1,5 +1,6 @@
using System;
using System.Linq;
+using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
diff --git a/MediaBrowser.Api/SyncPlay/SyncPlayService.cs b/MediaBrowser.Api/SyncPlay/SyncPlayService.cs
new file mode 100644
index 000000000..1e14ea552
--- /dev/null
+++ b/MediaBrowser.Api/SyncPlay/SyncPlayService.cs
@@ -0,0 +1,302 @@
+using System.Threading;
+using System;
+using System.Collections.Generic;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Net;
+using MediaBrowser.Controller.Session;
+using MediaBrowser.Controller.SyncPlay;
+using MediaBrowser.Model.Services;
+using MediaBrowser.Model.SyncPlay;
+using Microsoft.Extensions.Logging;
+
+namespace MediaBrowser.Api.SyncPlay
+{
+ [Route("/SyncPlay/{SessionId}/NewGroup", "POST", Summary = "Create a new SyncPlay group")]
+ [Authenticated]
+ public class SyncPlayNewGroup : IReturnVoid
+ {
+ [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
+ public string SessionId { get; set; }
+ }
+
+ [Route("/SyncPlay/{SessionId}/JoinGroup", "POST", Summary = "Join an existing SyncPlay group")]
+ [Authenticated]
+ public class SyncPlayJoinGroup : IReturnVoid
+ {
+ [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
+ public string SessionId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the Group id.
+ /// </summary>
+ /// <value>The Group id to join.</value>
+ [ApiMember(Name = "GroupId", Description = "Group Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
+ public string GroupId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the playing item id.
+ /// </summary>
+ /// <value>The client's currently playing item id.</value>
+ [ApiMember(Name = "PlayingItemId", Description = "Client's playing item id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
+ public string PlayingItemId { get; set; }
+ }
+
+ [Route("/SyncPlay/{SessionId}/LeaveGroup", "POST", Summary = "Leave joined SyncPlay group")]
+ [Authenticated]
+ public class SyncPlayLeaveGroup : IReturnVoid
+ {
+ [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
+ public string SessionId { get; set; }
+ }
+
+ [Route("/SyncPlay/{SessionId}/ListGroups", "POST", Summary = "List SyncPlay groups")]
+ [Authenticated]
+ public class SyncPlayListGroups : IReturnVoid
+ {
+ [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
+ public string SessionId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the filter item id.
+ /// </summary>
+ /// <value>The filter item id.</value>
+ [ApiMember(Name = "FilterItemId", Description = "Filter by item id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
+ public string FilterItemId { get; set; }
+ }
+
+ [Route("/SyncPlay/{SessionId}/PlayRequest", "POST", Summary = "Request play in SyncPlay group")]
+ [Authenticated]
+ public class SyncPlayPlayRequest : IReturnVoid
+ {
+ [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
+ public string SessionId { get; set; }
+ }
+
+ [Route("/SyncPlay/{SessionId}/PauseRequest", "POST", Summary = "Request pause in SyncPlay group")]
+ [Authenticated]
+ public class SyncPlayPauseRequest : IReturnVoid
+ {
+ [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
+ public string SessionId { get; set; }
+ }
+
+ [Route("/SyncPlay/{SessionId}/SeekRequest", "POST", Summary = "Request seek in SyncPlay group")]
+ [Authenticated]
+ public class SyncPlaySeekRequest : IReturnVoid
+ {
+ [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
+ public string SessionId { get; set; }
+
+ [ApiMember(Name = "PositionTicks", IsRequired = true, DataType = "long", ParameterType = "query", Verb = "POST")]
+ public long PositionTicks { get; set; }
+ }
+
+ [Route("/SyncPlay/{SessionId}/BufferingRequest", "POST", Summary = "Request group wait in SyncPlay group while buffering")]
+ [Authenticated]
+ public class SyncPlayBufferingRequest : IReturnVoid
+ {
+ [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
+ public string SessionId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the date used to pin PositionTicks in time.
+ /// </summary>
+ /// <value>The date related to PositionTicks.</value>
+ [ApiMember(Name = "When", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
+ public string When { get; set; }
+
+ [ApiMember(Name = "PositionTicks", IsRequired = true, DataType = "long", ParameterType = "query", Verb = "POST")]
+ public long PositionTicks { get; set; }
+
+ /// <summary>
+ /// Gets or sets whether this is a buffering or a buffering-done request.
+ /// </summary>
+ /// <value><c>true</c> if buffering is complete; <c>false</c> otherwise.</value>
+ [ApiMember(Name = "BufferingDone", IsRequired = true, DataType = "bool", ParameterType = "query", Verb = "POST")]
+ public bool BufferingDone { get; set; }
+ }
+
+ [Route("/SyncPlay/{SessionId}/UpdatePing", "POST", Summary = "Update session ping")]
+ [Authenticated]
+ public class SyncPlayUpdatePing : IReturnVoid
+ {
+ [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
+ public string SessionId { get; set; }
+
+ [ApiMember(Name = "Ping", IsRequired = true, DataType = "double", ParameterType = "query", Verb = "POST")]
+ public double Ping { get; set; }
+ }
+
+ /// <summary>
+ /// Class SyncPlayService.
+ /// </summary>
+ public class SyncPlayService : BaseApiService
+ {
+ /// <summary>
+ /// The session context.
+ /// </summary>
+ private readonly ISessionContext _sessionContext;
+
+ /// <summary>
+ /// The SyncPlay manager.
+ /// </summary>
+ private readonly ISyncPlayManager _syncPlayManager;
+
+ public SyncPlayService(
+ ILogger<SyncPlayService> logger,
+ IServerConfigurationManager serverConfigurationManager,
+ IHttpResultFactory httpResultFactory,
+ ISessionContext sessionContext,
+ ISyncPlayManager syncPlayManager)
+ : base(logger, serverConfigurationManager, httpResultFactory)
+ {
+ _sessionContext = sessionContext;
+ _syncPlayManager = syncPlayManager;
+ }
+
+ /// <summary>
+ /// Handles the specified request.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ public void Post(SyncPlayNewGroup request)
+ {
+ var currentSession = GetSession(_sessionContext);
+ _syncPlayManager.NewGroup(currentSession, CancellationToken.None);
+ }
+
+ /// <summary>
+ /// Handles the specified request.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ public void Post(SyncPlayJoinGroup request)
+ {
+ var currentSession = GetSession(_sessionContext);
+
+ Guid groupId;
+ Guid playingItemId = Guid.Empty;
+
+ if (!Guid.TryParse(request.GroupId, out groupId))
+ {
+ Logger.LogError("JoinGroup: {0} is not a valid format for GroupId. Ignoring request.", request.GroupId);
+ return;
+ }
+
+ // Both null and empty strings mean that client isn't playing anything
+ if (!string.IsNullOrEmpty(request.PlayingItemId) && !Guid.TryParse(request.PlayingItemId, out playingItemId))
+ {
+ Logger.LogError("JoinGroup: {0} is not a valid format for PlayingItemId. Ignoring request.", request.PlayingItemId);
+ return;
+ }
+
+ var joinRequest = new JoinGroupRequest()
+ {
+ GroupId = groupId,
+ PlayingItemId = playingItemId
+ };
+
+ _syncPlayManager.JoinGroup(currentSession, groupId, joinRequest, CancellationToken.None);
+ }
+
+ /// <summary>
+ /// Handles the specified request.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ public void Post(SyncPlayLeaveGroup request)
+ {
+ var currentSession = GetSession(_sessionContext);
+ _syncPlayManager.LeaveGroup(currentSession, CancellationToken.None);
+ }
+
+ /// <summary>
+ /// Handles the specified request.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ /// <value>The requested list of groups.</value>
+ public List<GroupInfoView> Post(SyncPlayListGroups request)
+ {
+ var currentSession = GetSession(_sessionContext);
+ var filterItemId = Guid.Empty;
+
+ if (!string.IsNullOrEmpty(request.FilterItemId) && !Guid.TryParse(request.FilterItemId, out filterItemId))
+ {
+ Logger.LogWarning("ListGroups: {0} is not a valid format for FilterItemId. Ignoring filter.", request.FilterItemId);
+ }
+
+ return _syncPlayManager.ListGroups(currentSession, filterItemId);
+ }
+
+ /// <summary>
+ /// Handles the specified request.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ public void Post(SyncPlayPlayRequest request)
+ {
+ var currentSession = GetSession(_sessionContext);
+ var syncPlayRequest = new PlaybackRequest()
+ {
+ Type = PlaybackRequestType.Play
+ };
+ _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None);
+ }
+
+ /// <summary>
+ /// Handles the specified request.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ public void Post(SyncPlayPauseRequest request)
+ {
+ var currentSession = GetSession(_sessionContext);
+ var syncPlayRequest = new PlaybackRequest()
+ {
+ Type = PlaybackRequestType.Pause
+ };
+ _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None);
+ }
+
+ /// <summary>
+ /// Handles the specified request.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ public void Post(SyncPlaySeekRequest request)
+ {
+ var currentSession = GetSession(_sessionContext);
+ var syncPlayRequest = new PlaybackRequest()
+ {
+ Type = PlaybackRequestType.Seek,
+ PositionTicks = request.PositionTicks
+ };
+ _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None);
+ }
+
+ /// <summary>
+ /// Handles the specified request.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ public void Post(SyncPlayBufferingRequest request)
+ {
+ var currentSession = GetSession(_sessionContext);
+ var syncPlayRequest = new PlaybackRequest()
+ {
+ Type = request.BufferingDone ? PlaybackRequestType.BufferingDone : PlaybackRequestType.Buffering,
+ When = DateTime.Parse(request.When),
+ PositionTicks = request.PositionTicks
+ };
+ _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None);
+ }
+
+ /// <summary>
+ /// Handles the specified request.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ public void Post(SyncPlayUpdatePing request)
+ {
+ var currentSession = GetSession(_sessionContext);
+ var syncPlayRequest = new PlaybackRequest()
+ {
+ Type = PlaybackRequestType.UpdatePing,
+ Ping = Convert.ToInt64(request.Ping)
+ };
+ _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None);
+ }
+ }
+}
diff --git a/MediaBrowser.Api/SyncPlay/TimeSyncService.cs b/MediaBrowser.Api/SyncPlay/TimeSyncService.cs
new file mode 100644
index 000000000..4a9307e62
--- /dev/null
+++ b/MediaBrowser.Api/SyncPlay/TimeSyncService.cs
@@ -0,0 +1,52 @@
+using System;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Net;
+using MediaBrowser.Model.Services;
+using MediaBrowser.Model.SyncPlay;
+using Microsoft.Extensions.Logging;
+
+namespace MediaBrowser.Api.SyncPlay
+{
+ [Route("/GetUtcTime", "GET", Summary = "Get UtcTime")]
+ public class GetUtcTime : IReturnVoid
+ {
+ // Nothing
+ }
+
+ /// <summary>
+ /// Class TimeSyncService.
+ /// </summary>
+ public class TimeSyncService : BaseApiService
+ {
+ public TimeSyncService(
+ ILogger<TimeSyncService> logger,
+ IServerConfigurationManager serverConfigurationManager,
+ IHttpResultFactory httpResultFactory)
+ : base(logger, serverConfigurationManager, httpResultFactory)
+ {
+ // Do nothing
+ }
+
+ /// <summary>
+ /// Handles the specified request.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ /// <value>The current UTC time response.</value>
+ public UtcTimeResponse Get(GetUtcTime request)
+ {
+ // Important to keep the following line at the beginning
+ var requestReceptionTime = DateTime.UtcNow.ToUniversalTime().ToString("o");
+
+ var response = new UtcTimeResponse();
+ response.RequestReceptionTime = requestReceptionTime;
+
+ // Important to keep the following two lines at the end
+ var responseTransmissionTime = DateTime.UtcNow.ToUniversalTime().ToString("o");
+ response.ResponseTransmissionTime = responseTransmissionTime;
+
+ // Implementing NTP on such a high level results in this useless
+ // information being sent. On the other hand it enables future additions.
+ return response;
+ }
+ }
+}
diff --git a/MediaBrowser.Api/TvShowsService.cs b/MediaBrowser.Api/TvShowsService.cs
index cd8e8dfbe..0c23d8b29 100644
--- a/MediaBrowser.Api/TvShowsService.cs
+++ b/MediaBrowser.Api/TvShowsService.cs
@@ -378,7 +378,7 @@ namespace MediaBrowser.Api
{
var user = _userManager.GetUserById(request.UserId);
- var series = GetSeries(request.Id, user);
+ var series = GetSeries(request.Id);
if (series == null)
{
@@ -404,7 +404,7 @@ namespace MediaBrowser.Api
};
}
- private Series GetSeries(string seriesId, User user)
+ private Series GetSeries(string seriesId)
{
if (!string.IsNullOrWhiteSpace(seriesId))
{
@@ -433,7 +433,7 @@ namespace MediaBrowser.Api
}
else if (request.Season.HasValue)
{
- var series = GetSeries(request.Id, user);
+ var series = GetSeries(request.Id);
if (series == null)
{
@@ -446,7 +446,7 @@ namespace MediaBrowser.Api
}
else
{
- var series = GetSeries(request.Id, user);
+ var series = GetSeries(request.Id);
if (series == null)
{
diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
index 559082ff4..a1ec08467 100644
--- a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
+++ b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs
index f3c0441e1..49d534c36 100644
--- a/MediaBrowser.Api/UserLibrary/ItemsService.cs
+++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs
@@ -2,10 +2,11 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Dto;
@@ -14,6 +15,7 @@ using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Services;
using Microsoft.Extensions.Logging;
+using MusicAlbum = MediaBrowser.Controller.Entities.Audio.MusicAlbum;
namespace MediaBrowser.Api.UserLibrary
{
@@ -86,7 +88,7 @@ namespace MediaBrowser.Api.UserLibrary
var ancestorIds = Array.Empty<Guid>();
- var excludeFolderIds = user.Configuration.LatestItemsExcludes;
+ var excludeFolderIds = user.GetPreference(PreferenceKind.LatestItemExcludes);
if (parentIdGuid.Equals(Guid.Empty) && excludeFolderIds.Length > 0)
{
ancestorIds = _libraryManager.GetUserRootFolder().GetChildren(user, true)
@@ -211,14 +213,14 @@ namespace MediaBrowser.Api.UserLibrary
request.IncludeItemTypes = "Playlist";
}
- bool isInEnabledFolder = user.Policy.EnabledFolders.Any(i => new Guid(i) == item.Id)
+ bool isInEnabledFolder = user.GetPreference(PreferenceKind.EnabledFolders).Any(i => new Guid(i) == item.Id)
// Assume all folders inside an EnabledChannel are enabled
- || user.Policy.EnabledChannels.Any(i => new Guid(i) == item.Id);
+ || user.GetPreference(PreferenceKind.EnabledChannels).Any(i => new Guid(i) == item.Id);
var collectionFolders = _libraryManager.GetCollectionFolders(item);
foreach (var collectionFolder in collectionFolders)
{
- if (user.Policy.EnabledFolders.Contains(
+ if (user.GetPreference(PreferenceKind.EnabledFolders).Contains(
collectionFolder.Id.ToString("N", CultureInfo.InvariantCulture),
StringComparer.OrdinalIgnoreCase))
{
@@ -226,9 +228,12 @@ namespace MediaBrowser.Api.UserLibrary
}
}
- if (!(item is UserRootFolder) && !user.Policy.EnableAllFolders && !isInEnabledFolder && !user.Policy.EnableAllChannels)
+ if (!(item is UserRootFolder)
+ && !isInEnabledFolder
+ && !user.HasPermission(PermissionKind.EnableAllFolders)
+ && !user.HasPermission(PermissionKind.EnableAllChannels))
{
- Logger.LogWarning("{UserName} is not permitted to access Library {ItemName}.", user.Name, item.Name);
+ Logger.LogWarning("{UserName} is not permitted to access Library {ItemName}.", user.Username, item.Name);
return new QueryResult<BaseItem>
{
Items = Array.Empty<BaseItem>(),
diff --git a/MediaBrowser.Api/UserLibrary/PlaystateService.cs b/MediaBrowser.Api/UserLibrary/PlaystateService.cs
index d0faca163..ab231626b 100644
--- a/MediaBrowser.Api/UserLibrary/PlaystateService.cs
+++ b/MediaBrowser.Api/UserLibrary/PlaystateService.cs
@@ -1,8 +1,8 @@
using System;
using System.Globalization;
using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Session;
diff --git a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
index 7fa750adb..f75852885 100644
--- a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
+++ b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
@@ -312,7 +312,7 @@ namespace MediaBrowser.Api.UserLibrary
if (!request.IsPlayed.HasValue)
{
- if (user.Configuration.HidePlayedInLatest)
+ if (user.HidePlayedInLatest)
{
request.IsPlayed = false;
}
diff --git a/MediaBrowser.Api/UserService.cs b/MediaBrowser.Api/UserService.cs
index 7d4d5fcf9..9cb9baf63 100644
--- a/MediaBrowser.Api/UserService.cs
+++ b/MediaBrowser.Api/UserService.cs
@@ -1,6 +1,7 @@
using System;
using System.Linq;
using System.Threading.Tasks;
+using Jellyfin.Data.Enums;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Authentication;
@@ -35,7 +36,7 @@ namespace MediaBrowser.Api
}
[Route("/Users/Public", "GET", Summary = "Gets a list of publicly visible users for display on a login screen.")]
- public class GetPublicUsers : IReturn<PublicUserDto[]>
+ public class GetPublicUsers : IReturn<UserDto[]>
{
}
@@ -266,38 +267,22 @@ namespace MediaBrowser.Api
_authContext = authContext;
}
- /// <summary>
- /// Gets the public available Users information
- /// </summary>
- /// <param name="request">The request.</param>
- /// <returns>System.Object.</returns>
public object Get(GetPublicUsers request)
{
- var result = _userManager
- .Users
- .Where(item => !item.Policy.IsDisabled);
-
- if (ServerConfigurationManager.Configuration.IsStartupWizardCompleted)
+ // If the startup wizard hasn't been completed then just return all users
+ if (!ServerConfigurationManager.Configuration.IsStartupWizardCompleted)
{
- var deviceId = _authContext.GetAuthorizationInfo(Request).DeviceId;
- result = result.Where(item => !item.Policy.IsHidden);
-
- if (!string.IsNullOrWhiteSpace(deviceId))
+ return Get(new GetUsers
{
- result = result.Where(i => _deviceManager.CanAccessDevice(i, deviceId));
- }
-
- if (!_networkManager.IsInLocalNetwork(Request.RemoteIp))
- {
- result = result.Where(i => i.Policy.EnableRemoteAccess);
- }
+ IsDisabled = false
+ });
}
- return ToOptimizedResult(result
- .OrderBy(u => u.Name)
- .Select(i => _userManager.GetPublicUserDto(i, Request.RemoteIp))
- .ToArray()
- );
+ return Get(new GetUsers
+ {
+ IsHidden = false,
+ IsDisabled = false
+ }, true, true);
}
/// <summary>
@@ -316,12 +301,12 @@ namespace MediaBrowser.Api
if (request.IsDisabled.HasValue)
{
- users = users.Where(i => i.Policy.IsDisabled == request.IsDisabled.Value);
+ users = users.Where(i => i.HasPermission(PermissionKind.IsDisabled) == request.IsDisabled.Value);
}
if (request.IsHidden.HasValue)
{
- users = users.Where(i => i.Policy.IsHidden == request.IsHidden.Value);
+ users = users.Where(i => i.HasPermission(PermissionKind.IsHidden) == request.IsHidden.Value);
}
if (filterByDevice)
@@ -338,12 +323,12 @@ namespace MediaBrowser.Api
{
if (!_networkManager.IsInLocalNetwork(Request.RemoteIp))
{
- users = users.Where(i => i.Policy.EnableRemoteAccess);
+ users = users.Where(i => i.HasPermission(PermissionKind.EnableRemoteAccess));
}
}
var result = users
- .OrderBy(u => u.Name)
+ .OrderBy(u => u.Username)
.Select(i => _userManager.GetUserDto(i, Request.RemoteIp))
.ToArray();
@@ -413,7 +398,7 @@ namespace MediaBrowser.Api
// Password should always be null
return Post(new AuthenticateUserByName
{
- Username = user.Name,
+ Username = user.Username,
Password = null,
Pw = request.Pw
});
@@ -472,7 +457,12 @@ namespace MediaBrowser.Api
}
else
{
- var success = await _userManager.AuthenticateUser(user.Name, request.CurrentPw, request.CurrentPassword, Request.RemoteIp, false).ConfigureAwait(false);
+ var success = await _userManager.AuthenticateUser(
+ user.Username,
+ request.CurrentPw,
+ request.CurrentPassword,
+ Request.RemoteIp,
+ false).ConfigureAwait(false);
if (success == null)
{
@@ -522,10 +512,10 @@ namespace MediaBrowser.Api
var user = _userManager.GetUserById(id);
- if (string.Equals(user.Name, dtoUser.Name, StringComparison.Ordinal))
+ if (string.Equals(user.Username, dtoUser.Name, StringComparison.Ordinal))
{
- _userManager.UpdateUser(user);
- _userManager.UpdateConfiguration(user, dtoUser.Configuration);
+ await _userManager.UpdateUserAsync(user);
+ _userManager.UpdateConfiguration(user.Id, dtoUser.Configuration);
}
else
{
@@ -576,7 +566,6 @@ namespace MediaBrowser.Api
AssertCanUpdateUser(_authContext, _userManager, request.Id, false);
_userManager.UpdateConfiguration(request.Id, request);
-
}
public void Post(UpdateUserPolicy request)
@@ -584,24 +573,24 @@ namespace MediaBrowser.Api
var user = _userManager.GetUserById(request.Id);
// If removing admin access
- if (!request.IsAdministrator && user.Policy.IsAdministrator)
+ if (!request.IsAdministrator && user.HasPermission(PermissionKind.IsAdministrator))
{
- if (_userManager.Users.Count(i => i.Policy.IsAdministrator) == 1)
+ if (_userManager.Users.Count(i => i.HasPermission(PermissionKind.IsAdministrator)) == 1)
{
throw new ArgumentException("There must be at least one user in the system with administrative access.");
}
}
// If disabling
- if (request.IsDisabled && user.Policy.IsAdministrator)
+ if (request.IsDisabled && user.HasPermission(PermissionKind.IsAdministrator))
{
throw new ArgumentException("Administrators cannot be disabled.");
}
// If disabling
- if (request.IsDisabled && !user.Policy.IsDisabled)
+ if (request.IsDisabled && !user.HasPermission(PermissionKind.IsDisabled))
{
- if (_userManager.Users.Count(i => !i.Policy.IsDisabled) == 1)
+ if (_userManager.Users.Count(i => !i.HasPermission(PermissionKind.IsDisabled)) == 1)
{
throw new ArgumentException("There must be at least one enabled user in the system.");
}
@@ -610,7 +599,7 @@ namespace MediaBrowser.Api
_sessionMananger.RevokeUserTokens(user.Id, currentToken);
}
- _userManager.UpdateUserPolicy(request.Id, request);
+ _userManager.UpdatePolicy(request.Id, request);
}
}
}
diff --git a/MediaBrowser.Api/VideosService.cs b/MediaBrowser.Api/VideosService.cs
index b11fd48d3..957a279f8 100644
--- a/MediaBrowser.Api/VideosService.cs
+++ b/MediaBrowser.Api/VideosService.cs
@@ -128,6 +128,7 @@ namespace MediaBrowser.Api
var items = request.Ids.Split(',')
.Select(i => _libraryManager.GetItemById(i))
.OfType<Video>()
+ .OrderBy(i => i.Id)
.ToList();
if (items.Count < 2)
diff --git a/MediaBrowser.Common/Configuration/ConfigurationStore.cs b/MediaBrowser.Common/Configuration/ConfigurationStore.cs
new file mode 100644
index 000000000..d31d45e4c
--- /dev/null
+++ b/MediaBrowser.Common/Configuration/ConfigurationStore.cs
@@ -0,0 +1,20 @@
+using System;
+
+namespace MediaBrowser.Common.Configuration
+{
+ /// <summary>
+ /// Describes a single entry in the application configuration.
+ /// </summary>
+ public class ConfigurationStore
+ {
+ /// <summary>
+ /// Gets or sets the unique identifier for the configuration.
+ /// </summary>
+ public string Key { get; set; }
+
+ /// <summary>
+ /// Gets or sets the type used to store the data for this configuration entry.
+ /// </summary>
+ public Type ConfigurationType { get; set; }
+ }
+}
diff --git a/MediaBrowser.Common/Configuration/IApplicationPaths.cs b/MediaBrowser.Common/Configuration/IApplicationPaths.cs
index 870b90796..4cea61682 100644
--- a/MediaBrowser.Common/Configuration/IApplicationPaths.cs
+++ b/MediaBrowser.Common/Configuration/IApplicationPaths.cs
@@ -3,12 +3,12 @@ using MediaBrowser.Model.Configuration;
namespace MediaBrowser.Common.Configuration
{
/// <summary>
- /// Interface IApplicationPaths
+ /// Interface IApplicationPaths.
/// </summary>
public interface IApplicationPaths
{
/// <summary>
- /// Gets the path to the program data folder
+ /// Gets the path to the program data folder.
/// </summary>
/// <value>The program data path.</value>
string ProgramDataPath { get; }
@@ -23,13 +23,13 @@ namespace MediaBrowser.Common.Configuration
string WebPath { get; }
/// <summary>
- /// Gets the path to the program system folder
+ /// Gets the path to the program system folder.
/// </summary>
/// <value>The program data path.</value>
string ProgramSystemPath { get; }
/// <summary>
- /// Gets the folder path to the data directory
+ /// Gets the folder path to the data directory.
/// </summary>
/// <value>The data directory.</value>
string DataPath { get; }
@@ -41,43 +41,43 @@ namespace MediaBrowser.Common.Configuration
string ImageCachePath { get; }
/// <summary>
- /// Gets the path to the plugin directory
+ /// Gets the path to the plugin directory.
/// </summary>
/// <value>The plugins path.</value>
string PluginsPath { get; }
/// <summary>
- /// Gets the path to the plugin configurations directory
+ /// Gets the path to the plugin configurations directory.
/// </summary>
/// <value>The plugin configurations path.</value>
string PluginConfigurationsPath { get; }
/// <summary>
- /// Gets the path to the log directory
+ /// Gets the path to the log directory.
/// </summary>
/// <value>The log directory path.</value>
string LogDirectoryPath { get; }
/// <summary>
- /// Gets the path to the application configuration root directory
+ /// Gets the path to the application configuration root directory.
/// </summary>
/// <value>The configuration directory path.</value>
string ConfigurationDirectoryPath { get; }
/// <summary>
- /// Gets the path to the system configuration file
+ /// Gets the path to the system configuration file.
/// </summary>
/// <value>The system configuration file path.</value>
string SystemConfigurationFilePath { get; }
/// <summary>
- /// Gets the folder path to the cache directory
+ /// Gets the folder path to the cache directory.
/// </summary>
/// <value>The cache directory.</value>
string CachePath { get; }
/// <summary>
- /// Gets the folder path to the temp directory within the cache folder
+ /// Gets the folder path to the temp directory within the cache folder.
/// </summary>
/// <value>The temp directory.</value>
string TempDirectory { get; }
diff --git a/MediaBrowser.Common/Configuration/IConfigurationFactory.cs b/MediaBrowser.Common/Configuration/IConfigurationFactory.cs
index 07ca2b58b..6db1f1364 100644
--- a/MediaBrowser.Common/Configuration/IConfigurationFactory.cs
+++ b/MediaBrowser.Common/Configuration/IConfigurationFactory.cs
@@ -1,4 +1,3 @@
-using System;
using System.Collections.Generic;
namespace MediaBrowser.Common.Configuration
@@ -15,33 +14,4 @@ namespace MediaBrowser.Common.Configuration
/// <returns>The configuration store.</returns>
IEnumerable<ConfigurationStore> GetConfigurations();
}
-
- /// <summary>
- /// Describes a single entry in the application configuration.
- /// </summary>
- public class ConfigurationStore
- {
- /// <summary>
- /// Gets or sets the unique identifier for the configuration.
- /// </summary>
- public string Key { get; set; }
-
- /// <summary>
- /// Gets or sets the type used to store the data for this configuration entry.
- /// </summary>
- public Type ConfigurationType { get; set; }
- }
-
- /// <summary>
- /// A configuration store that can be validated.
- /// </summary>
- public interface IValidatingConfiguration
- {
- /// <summary>
- /// Validation method to be invoked before saving the configuration.
- /// </summary>
- /// <param name="oldConfig">The old configuration.</param>
- /// <param name="newConfig">The new configuration.</param>
- void Validate(object oldConfig, object newConfig);
- }
}
diff --git a/MediaBrowser.Common/Configuration/IConfigurationManager.cs b/MediaBrowser.Common/Configuration/IConfigurationManager.cs
index caf2edd83..fe726090d 100644
--- a/MediaBrowser.Common/Configuration/IConfigurationManager.cs
+++ b/MediaBrowser.Common/Configuration/IConfigurationManager.cs
@@ -24,7 +24,7 @@ namespace MediaBrowser.Common.Configuration
event EventHandler<ConfigurationUpdateEventArgs> NamedConfigurationUpdated;
/// <summary>
- /// Gets or sets the application paths.
+ /// Gets the application paths.
/// </summary>
/// <value>The application paths.</value>
IApplicationPaths CommonApplicationPaths { get; }
diff --git a/MediaBrowser.Common/Configuration/IValidatingConfiguration.cs b/MediaBrowser.Common/Configuration/IValidatingConfiguration.cs
new file mode 100644
index 000000000..3b1d84f3c
--- /dev/null
+++ b/MediaBrowser.Common/Configuration/IValidatingConfiguration.cs
@@ -0,0 +1,15 @@
+namespace MediaBrowser.Common.Configuration
+{
+ /// <summary>
+ /// A configuration store that can be validated.
+ /// </summary>
+ public interface IValidatingConfiguration
+ {
+ /// <summary>
+ /// Validation method to be invoked before saving the configuration.
+ /// </summary>
+ /// <param name="oldConfig">The old configuration.</param>
+ /// <param name="newConfig">The new configuration.</param>
+ void Validate(object oldConfig, object newConfig);
+ }
+}
diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj
index a597b9052..c9ca153c7 100644
--- a/MediaBrowser.Common/MediaBrowser.Common.csproj
+++ b/MediaBrowser.Common/MediaBrowser.Common.csproj
@@ -17,8 +17,8 @@
</ItemGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.4" />
- <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.1.4" />
+ <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.5" />
+ <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.1.5" />
<PackageReference Include="Microsoft.Net.Http.Headers" Version="2.2.8" />
</ItemGroup>
@@ -37,7 +37,7 @@
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8" PrivateAssets="All" />
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
- <!-- <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" /> -->
+ <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" />
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
</ItemGroup>
diff --git a/MediaBrowser.Common/Net/CacheMode.cs b/MediaBrowser.Common/Net/CacheMode.cs
new file mode 100644
index 000000000..78fa3bf9b
--- /dev/null
+++ b/MediaBrowser.Common/Net/CacheMode.cs
@@ -0,0 +1,11 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1602
+
+namespace MediaBrowser.Common.Net
+{
+ public enum CacheMode
+ {
+ None = 0,
+ Unconditional = 1
+ }
+}
diff --git a/MediaBrowser.Common/Net/CompressionMethods.cs b/MediaBrowser.Common/Net/CompressionMethods.cs
new file mode 100644
index 000000000..39b72609f
--- /dev/null
+++ b/MediaBrowser.Common/Net/CompressionMethods.cs
@@ -0,0 +1,15 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1602
+
+using System;
+
+namespace MediaBrowser.Common.Net
+{
+ [Flags]
+ public enum CompressionMethods
+ {
+ None = 0b00000001,
+ Deflate = 0b00000010,
+ Gzip = 0b00000100
+ }
+}
diff --git a/MediaBrowser.Common/Net/HttpRequestOptions.cs b/MediaBrowser.Common/Net/HttpRequestOptions.cs
index 38274a80e..347fc9833 100644
--- a/MediaBrowser.Common/Net/HttpRequestOptions.cs
+++ b/MediaBrowser.Common/Net/HttpRequestOptions.cs
@@ -102,18 +102,4 @@ namespace MediaBrowser.Common.Net
return value;
}
}
-
- public enum CacheMode
- {
- None = 0,
- Unconditional = 1
- }
-
- [Flags]
- public enum CompressionMethods
- {
- None = 0b00000001,
- Deflate = 0b00000010,
- Gzip = 0b00000100
- }
}
diff --git a/MediaBrowser.Common/Progress/ActionableProgress.cs b/MediaBrowser.Common/Progress/ActionableProgress.cs
index af69055aa..d5bcd5be9 100644
--- a/MediaBrowser.Common/Progress/ActionableProgress.cs
+++ b/MediaBrowser.Common/Progress/ActionableProgress.cs
@@ -5,15 +5,16 @@ using System;
namespace MediaBrowser.Common.Progress
{
/// <summary>
- /// Class ActionableProgress
+ /// Class ActionableProgress.
/// </summary>
- /// <typeparam name="T"></typeparam>
+ /// <typeparam name="T">The type for the action parameter.</typeparam>
public class ActionableProgress<T> : IProgress<T>
{
/// <summary>
- /// The _actions
+ /// The _actions.
/// </summary>
private Action<T> _action;
+
public event EventHandler<T> ProgressChanged;
/// <summary>
@@ -32,14 +33,4 @@ namespace MediaBrowser.Common.Progress
_action?.Invoke(value);
}
}
-
- public class SimpleProgress<T> : IProgress<T>
- {
- public event EventHandler<T> ProgressChanged;
-
- public void Report(T value)
- {
- ProgressChanged?.Invoke(this, value);
- }
- }
}
diff --git a/MediaBrowser.Common/Progress/SimpleProgress.cs b/MediaBrowser.Common/Progress/SimpleProgress.cs
new file mode 100644
index 000000000..d75675bf1
--- /dev/null
+++ b/MediaBrowser.Common/Progress/SimpleProgress.cs
@@ -0,0 +1,16 @@
+#pragma warning disable CS1591
+
+using System;
+
+namespace MediaBrowser.Common.Progress
+{
+ public class SimpleProgress<T> : IProgress<T>
+ {
+ public event EventHandler<T> ProgressChanged;
+
+ public void Report(T value)
+ {
+ ProgressChanged?.Invoke(this, value);
+ }
+ }
+}
diff --git a/MediaBrowser.Common/Updates/IInstallationManager.cs b/MediaBrowser.Common/Updates/IInstallationManager.cs
index 950604432..965ffe0ec 100644
--- a/MediaBrowser.Common/Updates/IInstallationManager.cs
+++ b/MediaBrowser.Common/Updates/IInstallationManager.cs
@@ -12,28 +12,28 @@ namespace MediaBrowser.Common.Updates
{
public interface IInstallationManager : IDisposable
{
- event EventHandler<InstallationEventArgs> PackageInstalling;
+ event EventHandler<InstallationInfo> PackageInstalling;
- event EventHandler<InstallationEventArgs> PackageInstallationCompleted;
+ event EventHandler<InstallationInfo> PackageInstallationCompleted;
event EventHandler<InstallationFailedEventArgs> PackageInstallationFailed;
- event EventHandler<InstallationEventArgs> PackageInstallationCancelled;
+ event EventHandler<InstallationInfo> PackageInstallationCancelled;
/// <summary>
/// Occurs when a plugin is uninstalled.
/// </summary>
- event EventHandler<GenericEventArgs<IPlugin>> PluginUninstalled;
+ event EventHandler<IPlugin> PluginUninstalled;
/// <summary>
/// Occurs when a plugin is updated.
/// </summary>
- event EventHandler<GenericEventArgs<(IPlugin, VersionInfo)>> PluginUpdated;
+ event EventHandler<InstallationInfo> PluginUpdated;
/// <summary>
/// Occurs when a plugin is installed.
/// </summary>
- event EventHandler<GenericEventArgs<VersionInfo>> PluginInstalled;
+ event EventHandler<InstallationInfo> PluginInstalled;
/// <summary>
/// Gets the completed installations.
@@ -62,22 +62,12 @@ namespace MediaBrowser.Common.Updates
/// <summary>
/// Returns all compatible versions ordered from newest to oldest.
/// </summary>
- /// <param name="availableVersions">The available version of the plugin.</param>
- /// <param name="minVersion">The minimum required version of the plugin.</param>
- /// <returns>All compatible versions ordered from newest to oldest.</returns>
- IEnumerable<VersionInfo> GetCompatibleVersions(
- IEnumerable<VersionInfo> availableVersions,
- Version minVersion = null);
-
- /// <summary>
- /// Returns all compatible versions ordered from newest to oldest.
- /// </summary>
/// <param name="availablePackages">The available packages.</param>
/// <param name="name">The name.</param>
/// <param name="guid">The guid of the plugin.</param>
/// <param name="minVersion">The minimum required version of the plugin.</param>
/// <returns>All compatible versions ordered from newest to oldest.</returns>
- IEnumerable<VersionInfo> GetCompatibleVersions(
+ IEnumerable<InstallationInfo> GetCompatibleVersions(
IEnumerable<PackageInfo> availablePackages,
string name = null,
Guid guid = default,
@@ -88,7 +78,7 @@ namespace MediaBrowser.Common.Updates
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The available plugin updates.</returns>
- Task<IEnumerable<VersionInfo>> GetAvailablePluginUpdates(CancellationToken cancellationToken = default);
+ Task<IEnumerable<InstallationInfo>> GetAvailablePluginUpdates(CancellationToken cancellationToken = default);
/// <summary>
/// Installs the package.
@@ -96,7 +86,7 @@ namespace MediaBrowser.Common.Updates
/// <param name="package">The package.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns><see cref="Task" />.</returns>
- Task InstallPackage(VersionInfo package, CancellationToken cancellationToken = default);
+ Task InstallPackage(InstallationInfo package, CancellationToken cancellationToken = default);
/// <summary>
/// Uninstalls a plugin.
diff --git a/MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs b/MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs
index f5571065f..c0324a384 100644
--- a/MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs
+++ b/MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs
@@ -1,5 +1,5 @@
using System.Threading.Tasks;
-using MediaBrowser.Controller.Entities;
+using Jellyfin.Data.Entities;
using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Authentication
diff --git a/MediaBrowser.Controller/Authentication/IPasswordResetProvider.cs b/MediaBrowser.Controller/Authentication/IPasswordResetProvider.cs
index 2639960e7..d9b814f69 100644
--- a/MediaBrowser.Controller/Authentication/IPasswordResetProvider.cs
+++ b/MediaBrowser.Controller/Authentication/IPasswordResetProvider.cs
@@ -1,6 +1,6 @@
using System;
using System.Threading.Tasks;
-using MediaBrowser.Controller.Entities;
+using Jellyfin.Data.Entities;
using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Authentication
diff --git a/MediaBrowser.Controller/Channels/Channel.cs b/MediaBrowser.Controller/Channels/Channel.cs
index cdf2ca69e..dbb047804 100644
--- a/MediaBrowser.Controller/Channels/Channel.cs
+++ b/MediaBrowser.Controller/Channels/Channel.cs
@@ -3,6 +3,8 @@ using System.Globalization;
using System.Linq;
using System.Text.Json.Serialization;
using System.Threading;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Enums;
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Querying;
@@ -13,16 +15,18 @@ namespace MediaBrowser.Controller.Channels
{
public override bool IsVisible(User user)
{
- if (user.Policy.BlockedChannels != null)
+ if (user.GetPreference(PreferenceKind.BlockedChannels) != null)
{
- if (user.Policy.BlockedChannels.Contains(Id.ToString("N", CultureInfo.InvariantCulture), StringComparer.OrdinalIgnoreCase))
+ if (user.GetPreference(PreferenceKind.BlockedChannels).Contains(Id.ToString("N", CultureInfo.InvariantCulture), StringComparer.OrdinalIgnoreCase))
{
return false;
}
}
else
{
- if (!user.Policy.EnableAllChannels && !user.Policy.EnabledChannels.Contains(Id.ToString("N", CultureInfo.InvariantCulture), StringComparer.OrdinalIgnoreCase))
+ if (!user.HasPermission(PermissionKind.EnableAllChannels)
+ && !user.GetPreference(PreferenceKind.EnabledChannels)
+ .Contains(Id.ToString("N", CultureInfo.InvariantCulture), StringComparer.OrdinalIgnoreCase))
{
return false;
}
diff --git a/MediaBrowser.Controller/Collections/ICollectionManager.cs b/MediaBrowser.Controller/Collections/ICollectionManager.cs
index cfe8493d3..701423c0f 100644
--- a/MediaBrowser.Controller/Collections/ICollectionManager.cs
+++ b/MediaBrowser.Controller/Collections/ICollectionManager.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
diff --git a/MediaBrowser.Controller/Devices/CameraImageUploadInfo.cs b/MediaBrowser.Controller/Devices/CameraImageUploadInfo.cs
deleted file mode 100644
index 89d0be58f..000000000
--- a/MediaBrowser.Controller/Devices/CameraImageUploadInfo.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using MediaBrowser.Model.Devices;
-
-namespace MediaBrowser.Controller.Devices
-{
- public class CameraImageUploadInfo
- {
- public LocalFileInfo FileInfo { get; set; }
- public DeviceInfo Device { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/Devices/IDeviceManager.cs b/MediaBrowser.Controller/Devices/IDeviceManager.cs
index ef3f43c75..7d279230b 100644
--- a/MediaBrowser.Controller/Devices/IDeviceManager.cs
+++ b/MediaBrowser.Controller/Devices/IDeviceManager.cs
@@ -1,5 +1,5 @@
using System;
-using MediaBrowser.Controller.Entities;
+using Jellyfin.Data.Entities;
using MediaBrowser.Model.Devices;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Querying;
diff --git a/MediaBrowser.Controller/Drawing/IImageEncoder.cs b/MediaBrowser.Controller/Drawing/IImageEncoder.cs
index 88e67b648..e09ccd204 100644
--- a/MediaBrowser.Controller/Drawing/IImageEncoder.cs
+++ b/MediaBrowser.Controller/Drawing/IImageEncoder.cs
@@ -44,6 +44,15 @@ namespace MediaBrowser.Controller.Drawing
ImageDimensions GetImageSize(string path);
/// <summary>
+ /// Gets the blurhash of an image.
+ /// </summary>
+ /// <param name="xComp">Amount of X components of DCT to take.</param>
+ /// <param name="yComp">Amount of Y components of DCT to take.</param>
+ /// <param name="path">The filepath of the image.</param>
+ /// <returns>The blurhash.</returns>
+ string GetImageBlurHash(int xComp, int yComp, string path);
+
+ /// <summary>
/// Encode an image.
/// </summary>
string EncodeImage(string inputPath, DateTime dateModified, string outputPath, bool autoOrient, ImageOrientation? orientation, int quality, ImageProcessingOptions options, ImageFormat outputFormat);
diff --git a/MediaBrowser.Controller/Drawing/IImageProcessor.cs b/MediaBrowser.Controller/Drawing/IImageProcessor.cs
index 36c746624..f1873d539 100644
--- a/MediaBrowser.Controller/Drawing/IImageProcessor.cs
+++ b/MediaBrowser.Controller/Drawing/IImageProcessor.cs
@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Entities;
@@ -41,14 +42,24 @@ namespace MediaBrowser.Controller.Drawing
ImageDimensions GetImageDimensions(BaseItem item, ItemImageInfo info);
/// <summary>
+ /// Gets the blurhash of the image.
+ /// </summary>
+ /// <param name="path">Path to the image file.</param>
+ /// <returns>BlurHash</returns>
+ string GetImageBlurHash(string path);
+
+ /// <summary>
/// Gets the image cache tag.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="image">The image.</param>
/// <returns>Guid.</returns>
string GetImageCacheTag(BaseItem item, ItemImageInfo image);
+
string GetImageCacheTag(BaseItem item, ChapterInfo info);
+ string GetImageCacheTag(User user);
+
/// <summary>
/// Processes the image.
/// </summary>
diff --git a/MediaBrowser.Controller/Drawing/ImageHelper.cs b/MediaBrowser.Controller/Drawing/ImageHelper.cs
index d5a5f547e..c87a248b5 100644
--- a/MediaBrowser.Controller/Drawing/ImageHelper.cs
+++ b/MediaBrowser.Controller/Drawing/ImageHelper.cs
@@ -57,6 +57,7 @@ namespace MediaBrowser.Controller.Drawing
case ImageType.BoxRear:
case ImageType.Disc:
case ImageType.Menu:
+ case ImageType.Profile:
return 1;
case ImageType.Logo:
return 2.58;
diff --git a/MediaBrowser.Controller/Dto/IDtoService.cs b/MediaBrowser.Controller/Dto/IDtoService.cs
index ba693a065..56e6c47c4 100644
--- a/MediaBrowser.Controller/Dto/IDtoService.cs
+++ b/MediaBrowser.Controller/Dto/IDtoService.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Querying;
diff --git a/MediaBrowser.Controller/Entities/Audio/Audio.cs b/MediaBrowser.Controller/Entities/Audio/Audio.cs
index a700d0be4..a8ea2157d 100644
--- a/MediaBrowser.Controller/Entities/Audio/Audio.cs
+++ b/MediaBrowser.Controller/Entities/Audio/Audio.cs
@@ -2,9 +2,9 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Serialization;
+using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
index c216176e7..f7b2f9549 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
@@ -4,12 +4,13 @@ using System.Linq;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Users;
+using MetadataProvider = MediaBrowser.Model.Entities.MetadataProvider;
namespace MediaBrowser.Controller.Entities.Audio
{
@@ -97,14 +98,14 @@ namespace MediaBrowser.Controller.Entities.Audio
list.Insert(0, albumArtist + "-" + Name);
}
- var id = this.GetProviderId(MetadataProviders.MusicBrainzAlbum);
+ var id = this.GetProviderId(MetadataProvider.MusicBrainzAlbum);
if (!string.IsNullOrEmpty(id))
{
list.Insert(0, "MusicAlbum-Musicbrainz-" + id);
}
- id = this.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup);
+ id = this.GetProviderId(MetadataProvider.MusicBrainzReleaseGroup);
if (!string.IsNullOrEmpty(id))
{
@@ -114,9 +115,9 @@ namespace MediaBrowser.Controller.Entities.Audio
return list;
}
- protected override bool GetBlockUnratedValue(UserPolicy config)
+ protected override bool GetBlockUnratedValue(User user)
{
- return config.BlockUnratedItems.Contains(UnratedItem.Music);
+ return user.GetPreference(PreferenceKind.BlockUnratedItems).Contains(UnratedItem.Music.ToString());
}
public override UnratedItem GetBlockUnratedType()
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
index 5e3056ccb..63db3cfab 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
@@ -4,12 +4,13 @@ using System.Linq;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Extensions;
using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Users;
using Microsoft.Extensions.Logging;
+using MetadataProvider = MediaBrowser.Model.Entities.MetadataProvider;
namespace MediaBrowser.Controller.Entities.Audio
{
@@ -76,11 +77,7 @@ namespace MediaBrowser.Controller.Entities.Audio
public override int GetChildCount(User user)
{
- if (IsAccessedByName)
- {
- return 0;
- }
- return base.GetChildCount(user);
+ return IsAccessedByName ? 0 : base.GetChildCount(user);
}
public override bool IsSaveLocalMetadataEnabled()
@@ -128,7 +125,7 @@ namespace MediaBrowser.Controller.Entities.Audio
private static List<string> GetUserDataKeys(MusicArtist item)
{
var list = new List<string>();
- var id = item.GetProviderId(MetadataProviders.MusicBrainzArtist);
+ var id = item.GetProviderId(MetadataProvider.MusicBrainzArtist);
if (!string.IsNullOrEmpty(id))
{
@@ -142,9 +139,10 @@ namespace MediaBrowser.Controller.Entities.Audio
{
return "Artist-" + (Name ?? string.Empty).RemoveDiacritics();
}
- protected override bool GetBlockUnratedValue(UserPolicy config)
+
+ protected override bool GetBlockUnratedValue(User user)
{
- return config.BlockUnratedItems.Contains(UnratedItem.Music);
+ return user.GetPreference(PreferenceKind.BlockUnratedItems).Contains(UnratedItem.Music.ToString());
}
public override UnratedItem GetBlockUnratedType()
diff --git a/MediaBrowser.Controller/Entities/AudioBook.cs b/MediaBrowser.Controller/Entities/AudioBook.cs
index a13873bf9..4adaf4c6e 100644
--- a/MediaBrowser.Controller/Entities/AudioBook.cs
+++ b/MediaBrowser.Controller/Entities/AudioBook.cs
@@ -1,7 +1,7 @@
using System;
using System.Text.Json.Serialization;
+using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Configuration;
namespace MediaBrowser.Controller.Entities
{
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs
index 7ed8fa767..0eabb4b7a 100644
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ b/MediaBrowser.Controller/Entities/BaseItem.cs
@@ -7,6 +7,8 @@ using System.Text;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Enums;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
@@ -24,7 +26,6 @@ using MediaBrowser.Model.Library;
using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Providers;
-using MediaBrowser.Model.Users;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.Controller.Entities
@@ -63,7 +64,7 @@ namespace MediaBrowser.Controller.Entities
Genres = Array.Empty<string>();
Studios = Array.Empty<string>();
ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- LockedFields = Array.Empty<MetadataFields>();
+ LockedFields = Array.Empty<MetadataField>();
ImageInfos = Array.Empty<ItemImageInfo>();
ProductionLocations = Array.Empty<string>();
RemoteTrailers = Array.Empty<MediaUrl>();
@@ -481,12 +482,12 @@ namespace MediaBrowser.Controller.Entities
public virtual bool IsAuthorizedToDelete(User user, List<Folder> allCollectionFolders)
{
- if (user.Policy.EnableContentDeletion)
+ if (user.HasPermission(PermissionKind.EnableContentDeletion))
{
return true;
}
- var allowed = user.Policy.EnableContentDeletionFromFolders;
+ var allowed = user.GetPreference(PreferenceKind.EnableContentDeletionFromFolders);
if (SourceType == SourceType.Channel)
{
@@ -527,7 +528,7 @@ namespace MediaBrowser.Controller.Entities
public virtual bool IsAuthorizedToDownload(User user)
{
- return user.Policy.EnableContentDownloading;
+ return user.HasPermission(PermissionKind.EnableContentDownloading);
}
public bool CanDownload(User user)
@@ -557,7 +558,8 @@ namespace MediaBrowser.Controller.Entities
/// <summary>
/// The logger
/// </summary>
- public static ILogger Logger { get; set; }
+ public static ILoggerFactory LoggerFactory { get; set; }
+ public static ILogger<BaseItem> Logger { get; set; }
public static ILibraryManager LibraryManager { get; set; }
public static IServerConfigurationManager ConfigurationManager { get; set; }
public static IProviderManager ProviderManager { get; set; }
@@ -585,7 +587,7 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
/// <value>The locked fields.</value>
[JsonIgnore]
- public MetadataFields[] LockedFields { get; set; }
+ public MetadataField[] LockedFields { get; set; }
/// <summary>
/// Gets the type of the media.
@@ -1004,7 +1006,7 @@ namespace MediaBrowser.Controller.Entities
/// <returns>PlayAccess.</returns>
public PlayAccess GetPlayAccess(User user)
{
- if (!user.Policy.EnableMediaPlayback)
+ if (!user.HasPermission(PermissionKind.EnableMediaPlayback))
{
return PlayAccess.None;
}
@@ -1213,11 +1215,11 @@ namespace MediaBrowser.Controller.Entities
{
if (video.IsoType.HasValue)
{
- if (video.IsoType.Value == Model.Entities.IsoType.BluRay)
+ if (video.IsoType.Value == IsoType.BluRay)
{
terms.Add("Bluray");
}
- else if (video.IsoType.Value == Model.Entities.IsoType.Dvd)
+ else if (video.IsoType.Value == IsoType.Dvd)
{
terms.Add("DVD");
}
@@ -1374,6 +1376,7 @@ namespace MediaBrowser.Controller.Entities
new List<FileSystemMetadata>();
var ownedItemsChanged = await RefreshedOwnedItems(options, files, cancellationToken).ConfigureAwait(false);
+ LibraryManager.UpdateImages(this); // ensure all image properties in DB are fresh
if (ownedItemsChanged)
{
@@ -1772,7 +1775,7 @@ namespace MediaBrowser.Controller.Entities
return false;
}
- var maxAllowedRating = user.Policy.MaxParentalRating;
+ var maxAllowedRating = user.MaxParentalAgeRating;
if (maxAllowedRating == null)
{
@@ -1788,7 +1791,7 @@ namespace MediaBrowser.Controller.Entities
if (string.IsNullOrEmpty(rating))
{
- return !GetBlockUnratedValue(user.Policy);
+ return !GetBlockUnratedValue(user);
}
var value = LocalizationManager.GetRatingLevel(rating);
@@ -1796,7 +1799,7 @@ namespace MediaBrowser.Controller.Entities
// Could not determine the integer value
if (!value.HasValue)
{
- var isAllowed = !GetBlockUnratedValue(user.Policy);
+ var isAllowed = !GetBlockUnratedValue(user);
if (!isAllowed)
{
@@ -1858,8 +1861,7 @@ namespace MediaBrowser.Controller.Entities
private bool IsVisibleViaTags(User user)
{
- var policy = user.Policy;
- if (policy.BlockedTags.Any(i => Tags.Contains(i, StringComparer.OrdinalIgnoreCase)))
+ if (user.GetPreference(PreferenceKind.BlockedTags).Any(i => Tags.Contains(i, StringComparer.OrdinalIgnoreCase)))
{
return false;
}
@@ -1885,22 +1887,18 @@ namespace MediaBrowser.Controller.Entities
/// <summary>
/// Gets the block unrated value.
/// </summary>
- /// <param name="config">The configuration.</param>
+ /// <param name="user">The configuration.</param>
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
- protected virtual bool GetBlockUnratedValue(UserPolicy config)
+ protected virtual bool GetBlockUnratedValue(User user)
{
// Don't block plain folders that are unrated. Let the media underneath get blocked
// Special folders like series and albums will override this method.
- if (IsFolder)
- {
- return false;
- }
- if (this is IItemByName)
+ if (IsFolder || this is IItemByName)
{
return false;
}
- return config.BlockUnratedItems.Contains(GetBlockUnratedType());
+ return user.GetPreference(PreferenceKind.BlockUnratedItems).Contains(GetBlockUnratedType().ToString());
}
/// <summary>
@@ -2130,7 +2128,8 @@ namespace MediaBrowser.Controller.Entities
/// <param name="resetPosition">if set to <c>true</c> [reset position].</param>
/// <returns>Task.</returns>
/// <exception cref="ArgumentNullException"></exception>
- public virtual void MarkPlayed(User user,
+ public virtual void MarkPlayed(
+ User user,
DateTime? datePlayed,
bool resetPosition)
{
@@ -2222,6 +2221,7 @@ namespace MediaBrowser.Controller.Entities
existingImage.DateModified = image.DateModified;
existingImage.Width = image.Width;
existingImage.Height = image.Height;
+ existingImage.BlurHash = image.BlurHash;
}
else
{
@@ -2373,6 +2373,46 @@ namespace MediaBrowser.Controller.Entities
.ElementAtOrDefault(imageIndex);
}
+ /// <summary>
+ /// Computes image index for given image or raises if no matching image found.
+ /// </summary>
+ /// <param name="image">Image to compute index for.</param>
+ /// <exception cref="ArgumentException">Image index cannot be computed as no matching image found.
+ /// </exception>
+ /// <returns>Image index.</returns>
+ public int GetImageIndex(ItemImageInfo image)
+ {
+ if (image == null)
+ {
+ throw new ArgumentNullException(nameof(image));
+ }
+
+ if (image.Type == ImageType.Chapter)
+ {
+ var chapters = ItemRepository.GetChapters(this);
+ for (var i = 0; i < chapters.Count; i++)
+ {
+ if (chapters[i].ImagePath == image.Path)
+ {
+ return i;
+ }
+ }
+
+ throw new ArgumentException("No chapter index found for image path", image.Path);
+ }
+
+ var images = GetImages(image.Type).ToArray();
+ for (var i = 0; i < images.Length; i++)
+ {
+ if (images[i].Path == image.Path)
+ {
+ return i;
+ }
+ }
+
+ throw new ArgumentException("No image index found for image path", image.Path);
+ }
+
public IEnumerable<ItemImageInfo> GetImages(ImageType imageType)
{
if (imageType == ImageType.Chapter)
@@ -2763,14 +2803,7 @@ namespace MediaBrowser.Controller.Entities
return this;
}
- foreach (var parent in GetParents())
- {
- if (parent.IsTopParent)
- {
- return parent;
- }
- }
- return null;
+ return GetParents().FirstOrDefault(parent => parent.IsTopParent);
}
[JsonIgnore]
diff --git a/MediaBrowser.Controller/Entities/Book.cs b/MediaBrowser.Controller/Entities/Book.cs
index dcad2554b..11c6c6e45 100644
--- a/MediaBrowser.Controller/Entities/Book.cs
+++ b/MediaBrowser.Controller/Entities/Book.cs
@@ -1,8 +1,8 @@
using System;
using System.Linq;
using System.Text.Json.Serialization;
+using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Configuration;
namespace MediaBrowser.Controller.Entities
{
@@ -11,6 +11,10 @@ namespace MediaBrowser.Controller.Entities
[JsonIgnore]
public override string MediaType => Model.Entities.MediaType.Book;
+ public override bool SupportsPlayedStatus => true;
+
+ public override bool SupportsPositionTicksResume => true;
+
[JsonIgnore]
public string SeriesPresentationUniqueKey { get; set; }
@@ -20,6 +24,11 @@ namespace MediaBrowser.Controller.Entities
[JsonIgnore]
public Guid SeriesId { get; set; }
+ public Book()
+ {
+ this.RunTimeTicks = TimeSpan.TicksPerSecond;
+ }
+
public string FindSeriesSortName()
{
return SeriesName;
diff --git a/MediaBrowser.Controller/Entities/DayOfWeekHelper.cs b/MediaBrowser.Controller/Entities/DayOfWeekHelper.cs
deleted file mode 100644
index 8a79e0783..000000000
--- a/MediaBrowser.Controller/Entities/DayOfWeekHelper.cs
+++ /dev/null
@@ -1,71 +0,0 @@
-using System;
-using System.Collections.Generic;
-using MediaBrowser.Model.Configuration;
-
-namespace MediaBrowser.Controller.Entities
-{
- public static class DayOfWeekHelper
- {
- public static List<DayOfWeek> GetDaysOfWeek(DynamicDayOfWeek day)
- {
- return GetDaysOfWeek(new List<DynamicDayOfWeek> { day });
- }
-
- public static List<DayOfWeek> GetDaysOfWeek(List<DynamicDayOfWeek> days)
- {
- var list = new List<DayOfWeek>();
-
- if (days.Contains(DynamicDayOfWeek.Sunday) ||
- days.Contains(DynamicDayOfWeek.Weekend) ||
- days.Contains(DynamicDayOfWeek.Everyday))
- {
- list.Add(DayOfWeek.Sunday);
- }
-
- if (days.Contains(DynamicDayOfWeek.Saturday) ||
- days.Contains(DynamicDayOfWeek.Weekend) ||
- days.Contains(DynamicDayOfWeek.Everyday))
- {
- list.Add(DayOfWeek.Saturday);
- }
-
- if (days.Contains(DynamicDayOfWeek.Monday) ||
- days.Contains(DynamicDayOfWeek.Weekday) ||
- days.Contains(DynamicDayOfWeek.Everyday))
- {
- list.Add(DayOfWeek.Monday);
- }
-
- if (days.Contains(DynamicDayOfWeek.Tuesday) ||
- days.Contains(DynamicDayOfWeek.Weekday) ||
- days.Contains(DynamicDayOfWeek.Everyday))
- {
- list.Add(DayOfWeek.Tuesday
- );
- }
-
- if (days.Contains(DynamicDayOfWeek.Wednesday) ||
- days.Contains(DynamicDayOfWeek.Weekday) ||
- days.Contains(DynamicDayOfWeek.Everyday))
- {
- list.Add(DayOfWeek.Wednesday);
- }
-
- if (days.Contains(DynamicDayOfWeek.Thursday) ||
- days.Contains(DynamicDayOfWeek.Weekday) ||
- days.Contains(DynamicDayOfWeek.Everyday))
- {
- list.Add(DayOfWeek.Thursday);
- }
-
- if (days.Contains(DynamicDayOfWeek.Friday) ||
- days.Contains(DynamicDayOfWeek.Weekday) ||
- days.Contains(DynamicDayOfWeek.Everyday))
- {
- list.Add(DayOfWeek.Friday);
- }
-
- return list;
- }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs
index a468e0c35..4af74f9cd 100644
--- a/MediaBrowser.Controller/Entities/Folder.cs
+++ b/MediaBrowser.Controller/Entities/Folder.cs
@@ -8,6 +8,8 @@ using System.Linq;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Enums;
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Collections;
@@ -15,13 +17,16 @@ using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies;
-using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Querying;
using Microsoft.Extensions.Logging;
+using Episode = MediaBrowser.Controller.Entities.TV.Episode;
+using MusicAlbum = MediaBrowser.Controller.Entities.Audio.MusicAlbum;
+using Season = MediaBrowser.Controller.Entities.TV.Season;
+using Series = MediaBrowser.Controller.Entities.TV.Series;
namespace MediaBrowser.Controller.Entities
{
@@ -177,19 +182,22 @@ namespace MediaBrowser.Controller.Entities
{
if (this is ICollectionFolder && !(this is BasePluginFolder))
{
- if (user.Policy.BlockedMediaFolders != null)
+ var blockedMediaFolders = user.GetPreference(PreferenceKind.BlockedMediaFolders);
+ if (blockedMediaFolders.Length > 0)
{
- if (user.Policy.BlockedMediaFolders.Contains(Id.ToString("N", CultureInfo.InvariantCulture), StringComparer.OrdinalIgnoreCase) ||
+ if (blockedMediaFolders.Contains(Id.ToString("N", CultureInfo.InvariantCulture), StringComparer.OrdinalIgnoreCase) ||
// Backwards compatibility
- user.Policy.BlockedMediaFolders.Contains(Name, StringComparer.OrdinalIgnoreCase))
+ blockedMediaFolders.Contains(Name, StringComparer.OrdinalIgnoreCase))
{
return false;
}
}
else
{
- if (!user.Policy.EnableAllFolders && !user.Policy.EnabledFolders.Contains(Id.ToString("N", CultureInfo.InvariantCulture), StringComparer.OrdinalIgnoreCase))
+ if (!user.HasPermission(PermissionKind.EnableAllFolders)
+ && !user.GetPreference(PreferenceKind.EnabledFolders)
+ .Contains(Id.ToString("N", CultureInfo.InvariantCulture), StringComparer.OrdinalIgnoreCase))
{
return false;
}
@@ -341,6 +349,11 @@ namespace MediaBrowser.Controller.Entities
{
currentChild.UpdateToRepository(ItemUpdateType.MetadataImport, cancellationToken);
}
+ else
+ {
+ // metadata is up-to-date; make sure DB has correct images dimensions and hash
+ LibraryManager.UpdateImages(currentChild);
+ }
continue;
}
@@ -877,7 +890,7 @@ namespace MediaBrowser.Controller.Entities
try
{
query.Parent = this;
- query.ChannelIds = new Guid[] { ChannelId };
+ query.ChannelIds = new[] { ChannelId };
// Don't blow up here because it could cause parent screens with other content to fail
return ChannelManager.GetChannelItemsInternal(query, new SimpleProgress<double>(), CancellationToken.None).Result;
@@ -947,11 +960,13 @@ namespace MediaBrowser.Controller.Entities
return UserViewBuilder.SortAndPage(items, null, query, LibraryManager, enableSorting);
}
- private static IEnumerable<BaseItem> CollapseBoxSetItemsIfNeeded(IEnumerable<BaseItem> items,
+ private static IEnumerable<BaseItem> CollapseBoxSetItemsIfNeeded(
+ IEnumerable<BaseItem> items,
InternalItemsQuery query,
BaseItem queryParent,
User user,
- IServerConfigurationManager configurationManager, ICollectionManager collectionManager)
+ IServerConfigurationManager configurationManager,
+ ICollectionManager collectionManager)
{
if (items == null)
{
@@ -1577,7 +1592,7 @@ namespace MediaBrowser.Controller.Entities
EnableTotalRecordCount = false
};
- if (!user.Configuration.DisplayMissingEpisodes)
+ if (!user.DisplayMissingEpisodes)
{
query.IsVirtualItem = false;
}
diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
index bd96059e3..496bee857 100644
--- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
+++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
@@ -1,8 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Dto;
-using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
namespace MediaBrowser.Controller.Entities
@@ -223,15 +224,16 @@ namespace MediaBrowser.Controller.Entities
{
if (user != null)
{
- var policy = user.Policy;
- MaxParentalRating = policy.MaxParentalRating;
+ MaxParentalRating = user.MaxParentalAgeRating;
- if (policy.MaxParentalRating.HasValue)
+ if (MaxParentalRating.HasValue)
{
- BlockUnratedItems = policy.BlockUnratedItems.Where(i => i != UnratedItem.Other).ToArray();
+ BlockUnratedItems = user.GetPreference(PreferenceKind.BlockUnratedItems)
+ .Where(i => i != UnratedItem.Other.ToString())
+ .Select(e => Enum.Parse<UnratedItem>(e, true)).ToArray();
}
- ExcludeInheritedTags = policy.BlockedTags;
+ ExcludeInheritedTags = user.GetPreference(PreferenceKind.BlockedTags);
User = user;
}
diff --git a/MediaBrowser.Controller/Entities/ItemImageInfo.cs b/MediaBrowser.Controller/Entities/ItemImageInfo.cs
index fc46dec2e..12f5db2e0 100644
--- a/MediaBrowser.Controller/Entities/ItemImageInfo.cs
+++ b/MediaBrowser.Controller/Entities/ItemImageInfo.cs
@@ -28,6 +28,12 @@ namespace MediaBrowser.Controller.Entities
public int Height { get; set; }
+ /// <summary>
+ /// Gets or sets the blurhash.
+ /// </summary>
+ /// <value>The blurhash.</value>
+ public string BlurHash { get; set; }
+
[JsonIgnore]
public bool IsLocalFile => Path == null || !Path.StartsWith("http", StringComparison.OrdinalIgnoreCase);
}
diff --git a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
index feaf8c45a..be71bcc3c 100644
--- a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
+++ b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
@@ -2,11 +2,11 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Serialization;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
-using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Entities.Movies
{
@@ -45,9 +45,9 @@ namespace MediaBrowser.Controller.Entities.Movies
/// <value>The display order.</value>
public string DisplayOrder { get; set; }
- protected override bool GetBlockUnratedValue(UserPolicy config)
+ protected override bool GetBlockUnratedValue(User user)
{
- return config.BlockUnratedItems.Contains(UnratedItem.Movie);
+ return user.GetPreference(PreferenceKind.BlockUnratedItems).Contains(UnratedItem.Movie.ToString());
}
public override double GetDefaultPrimaryImageAspectRatio()
diff --git a/MediaBrowser.Controller/Entities/Movies/Movie.cs b/MediaBrowser.Controller/Entities/Movies/Movie.cs
index 11dc472b6..26a165025 100644
--- a/MediaBrowser.Controller/Entities/Movies/Movie.cs
+++ b/MediaBrowser.Controller/Entities/Movies/Movie.cs
@@ -4,8 +4,8 @@ using System.Linq;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Providers;
@@ -173,7 +173,7 @@ namespace MediaBrowser.Controller.Entities.Movies
{
var list = base.GetRelatedUrls();
- var imdbId = this.GetProviderId(MetadataProviders.Imdb);
+ var imdbId = this.GetProviderId(MetadataProvider.Imdb);
if (!string.IsNullOrEmpty(imdbId))
{
list.Add(new ExternalUrl
diff --git a/MediaBrowser.Controller/Entities/MusicVideo.cs b/MediaBrowser.Controller/Entities/MusicVideo.cs
index 603242063..6e7f2812d 100644
--- a/MediaBrowser.Controller/Entities/MusicVideo.cs
+++ b/MediaBrowser.Controller/Entities/MusicVideo.cs
@@ -1,9 +1,9 @@
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
+using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Configuration;
namespace MediaBrowser.Controller.Entities
{
diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs
index 49229fa4b..4ec60e7cd 100644
--- a/MediaBrowser.Controller/Entities/TV/Episode.cs
+++ b/MediaBrowser.Controller/Entities/TV/Episode.cs
@@ -3,8 +3,8 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text.Json.Serialization;
+using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using Microsoft.Extensions.Logging;
diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs
index 9c8a469e2..7dfd1a759 100644
--- a/MediaBrowser.Controller/Entities/TV/Season.cs
+++ b/MediaBrowser.Controller/Entities/TV/Season.cs
@@ -2,11 +2,11 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Serialization;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Querying;
-using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Entities.TV
{
@@ -168,7 +168,7 @@ namespace MediaBrowser.Controller.Entities.TV
return GetEpisodes(user, new DtoOptions(true));
}
- protected override bool GetBlockUnratedValue(UserPolicy config)
+ protected override bool GetBlockUnratedValue(User user)
{
// Don't block. Let either the entire series rating or episode rating determine it
return false;
@@ -203,7 +203,7 @@ namespace MediaBrowser.Controller.Entities.TV
public Guid FindSeriesId()
{
var series = FindParent<Series>();
- return series == null ? Guid.Empty : series.Id;
+ return series?.Id ?? Guid.Empty;
}
/// <summary>
@@ -234,7 +234,7 @@ namespace MediaBrowser.Controller.Entities.TV
if (!IndexNumber.HasValue && !string.IsNullOrEmpty(Path))
{
- IndexNumber = IndexNumber ?? LibraryManager.GetSeasonNumberFromPath(Path);
+ IndexNumber ??= LibraryManager.GetSeasonNumberFromPath(Path);
// If a change was made record it
if (IndexNumber.HasValue)
diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs
index 2475b2b7e..a519089b3 100644
--- a/MediaBrowser.Controller/Entities/TV/Series.cs
+++ b/MediaBrowser.Controller/Entities/TV/Series.cs
@@ -5,13 +5,14 @@ using System.Linq;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Querying;
-using MediaBrowser.Model.Users;
+using MetadataProvider = MediaBrowser.Model.Entities.MetadataProvider;
namespace MediaBrowser.Controller.Entities.TV
{
@@ -119,7 +120,7 @@ namespace MediaBrowser.Controller.Entities.TV
{
AncestorWithPresentationUniqueKey = null,
SeriesPresentationUniqueKey = seriesKey,
- IncludeItemTypes = new[] { typeof(Season).Name },
+ IncludeItemTypes = new[] { nameof(Season) },
IsVirtualItem = false,
Limit = 0,
DtoOptions = new DtoOptions(false)
@@ -164,13 +165,13 @@ namespace MediaBrowser.Controller.Entities.TV
{
var list = base.GetUserDataKeys();
- var key = this.GetProviderId(MetadataProviders.Imdb);
+ var key = this.GetProviderId(MetadataProvider.Imdb);
if (!string.IsNullOrEmpty(key))
{
list.Insert(0, key);
}
- key = this.GetProviderId(MetadataProviders.Tvdb);
+ key = this.GetProviderId(MetadataProvider.Tvdb);
if (!string.IsNullOrEmpty(key))
{
list.Insert(0, key);
@@ -205,14 +206,9 @@ namespace MediaBrowser.Controller.Entities.TV
query.IncludeItemTypes = new[] { typeof(Season).Name };
query.OrderBy = new[] { ItemSortBy.SortName }.Select(i => new ValueTuple<string, SortOrder>(i, SortOrder.Ascending)).ToArray();
- if (user != null)
+ if (user != null && !user.DisplayMissingEpisodes)
{
- var config = user.Configuration;
-
- if (!config.DisplayMissingEpisodes)
- {
- query.IsMissing = false;
- }
+ query.IsMissing = false;
}
}
@@ -257,8 +253,8 @@ namespace MediaBrowser.Controller.Entities.TV
OrderBy = new[] { ItemSortBy.SortName }.Select(i => new ValueTuple<string, SortOrder>(i, SortOrder.Ascending)).ToArray(),
DtoOptions = options
};
- var config = user.Configuration;
- if (!config.DisplayMissingEpisodes)
+
+ if (!user.DisplayMissingEpisodes)
{
query.IsMissing = false;
}
@@ -311,7 +307,7 @@ namespace MediaBrowser.Controller.Entities.TV
// Refresh episodes and other children
foreach (var item in items)
{
- if ((item is Season))
+ if (item is Season)
{
continue;
}
@@ -370,8 +366,7 @@ namespace MediaBrowser.Controller.Entities.TV
};
if (user != null)
{
- var config = user.Configuration;
- if (!config.DisplayMissingEpisodes)
+ if (!user.DisplayMissingEpisodes)
{
query.IsMissing = false;
}
@@ -452,9 +447,9 @@ namespace MediaBrowser.Controller.Entities.TV
}
- protected override bool GetBlockUnratedValue(UserPolicy config)
+ protected override bool GetBlockUnratedValue(User user)
{
- return config.BlockUnratedItems.Contains(UnratedItem.Series);
+ return user.GetPreference(PreferenceKind.BlockUnratedItems).Contains(UnratedItem.Series.ToString());
}
public override UnratedItem GetBlockUnratedType()
@@ -493,7 +488,7 @@ namespace MediaBrowser.Controller.Entities.TV
{
var list = base.GetRelatedUrls();
- var imdbId = this.GetProviderId(MetadataProviders.Imdb);
+ var imdbId = this.GetProviderId(MetadataProvider.Imdb);
if (!string.IsNullOrEmpty(imdbId))
{
list.Add(new ExternalUrl
diff --git a/MediaBrowser.Controller/Entities/Trailer.cs b/MediaBrowser.Controller/Entities/Trailer.cs
index 0b8be90cd..c327d17c9 100644
--- a/MediaBrowser.Controller/Entities/Trailer.cs
+++ b/MediaBrowser.Controller/Entities/Trailer.cs
@@ -1,8 +1,8 @@
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
+using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
@@ -80,7 +80,7 @@ namespace MediaBrowser.Controller.Entities
{
var list = base.GetRelatedUrls();
- var imdbId = this.GetProviderId(MetadataProviders.Imdb);
+ var imdbId = this.GetProviderId(MetadataProvider.Imdb);
if (!string.IsNullOrEmpty(imdbId))
{
list.Add(new ExternalUrl
diff --git a/MediaBrowser.Controller/Entities/User.cs b/MediaBrowser.Controller/Entities/User.cs
deleted file mode 100644
index 53601a610..000000000
--- a/MediaBrowser.Controller/Entities/User.cs
+++ /dev/null
@@ -1,262 +0,0 @@
-using System;
-using System.Globalization;
-using System.IO;
-using System.Text.Json.Serialization;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Users;
-
-namespace MediaBrowser.Controller.Entities
-{
- /// <summary>
- /// Class User
- /// </summary>
- public class User : BaseItem
- {
- public static IUserManager UserManager { get; set; }
-
- /// <summary>
- /// Gets or sets the password.
- /// </summary>
- /// <value>The password.</value>
- public string Password { get; set; }
- public string EasyPassword { get; set; }
-
- // Strictly to remove JsonIgnore
- public override ItemImageInfo[] ImageInfos
- {
- get => base.ImageInfos;
- set => base.ImageInfos = value;
- }
-
- /// <summary>
- /// Gets or sets the path.
- /// </summary>
- /// <value>The path.</value>
- [JsonIgnore]
- public override string Path
- {
- get => ConfigurationDirectoryPath;
- set => base.Path = value;
- }
-
- private string _name;
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public override string Name
- {
- get => _name;
- set
- {
- _name = value;
-
- // lazy load this again
- SortName = null;
- }
- }
-
- /// <summary>
- /// Returns the folder containing the item.
- /// If the item is a folder, it returns the folder itself
- /// </summary>
- /// <value>The containing folder path.</value>
- [JsonIgnore]
- public override string ContainingFolderPath => Path;
-
- /// <summary>
- /// Gets the root folder.
- /// </summary>
- /// <value>The root folder.</value>
- [JsonIgnore]
- public Folder RootFolder => LibraryManager.GetUserRootFolder();
-
- /// <summary>
- /// Gets or sets the last login date.
- /// </summary>
- /// <value>The last login date.</value>
- public DateTime? LastLoginDate { get; set; }
- /// <summary>
- /// Gets or sets the last activity date.
- /// </summary>
- /// <value>The last activity date.</value>
- public DateTime? LastActivityDate { get; set; }
-
- private volatile UserConfiguration _config;
- private readonly object _configSyncLock = new object();
- [JsonIgnore]
- public UserConfiguration Configuration
- {
- get
- {
- if (_config == null)
- {
- lock (_configSyncLock)
- {
- if (_config == null)
- {
- _config = UserManager.GetUserConfiguration(this);
- }
- }
- }
-
- return _config;
- }
- set => _config = value;
- }
-
- private volatile UserPolicy _policy;
- private readonly object _policySyncLock = new object();
- [JsonIgnore]
- public UserPolicy Policy
- {
- get
- {
- if (_policy == null)
- {
- lock (_policySyncLock)
- {
- if (_policy == null)
- {
- _policy = UserManager.GetUserPolicy(this);
- }
- }
- }
-
- return _policy;
- }
- set => _policy = value;
- }
-
- /// <summary>
- /// Renames the user.
- /// </summary>
- /// <param name="newName">The new name.</param>
- /// <returns>Task.</returns>
- /// <exception cref="ArgumentNullException"></exception>
- public Task Rename(string newName)
- {
- if (string.IsNullOrWhiteSpace(newName))
- {
- throw new ArgumentException("Username can't be empty", nameof(newName));
- }
-
- Name = newName;
-
- return RefreshMetadata(
- new MetadataRefreshOptions(new DirectoryService(FileSystem))
- {
- ReplaceAllMetadata = true,
- ImageRefreshMode = MetadataRefreshMode.FullRefresh,
- MetadataRefreshMode = MetadataRefreshMode.FullRefresh,
- ForceSave = true
-
- },
- CancellationToken.None);
- }
-
- public override void UpdateToRepository(ItemUpdateType updateReason, CancellationToken cancellationToken)
- {
- UserManager.UpdateUser(this);
- }
-
- /// <summary>
- /// Gets the path to the user's configuration directory
- /// </summary>
- /// <value>The configuration directory path.</value>
- [JsonIgnore]
- public string ConfigurationDirectoryPath => GetConfigurationDirectoryPath(Name);
-
- public override double GetDefaultPrimaryImageAspectRatio()
- {
- return 1;
- }
-
- /// <summary>
- /// Gets the configuration directory path.
- /// </summary>
- /// <param name="username">The username.</param>
- /// <returns>System.String.</returns>
- private string GetConfigurationDirectoryPath(string username)
- {
- var parentPath = ConfigurationManager.ApplicationPaths.UserConfigurationDirectoryPath;
-
- // TODO: Remove idPath and just use usernamePath for future releases
- var usernamePath = System.IO.Path.Combine(parentPath, username);
- var idPath = System.IO.Path.Combine(parentPath, Id.ToString("N", CultureInfo.InvariantCulture));
- if (!Directory.Exists(usernamePath) && Directory.Exists(idPath))
- {
- Directory.Move(idPath, usernamePath);
- }
-
- return usernamePath;
- }
-
- public bool IsParentalScheduleAllowed()
- {
- return IsParentalScheduleAllowed(DateTime.UtcNow);
- }
-
- public bool IsParentalScheduleAllowed(DateTime date)
- {
- var schedules = Policy.AccessSchedules;
-
- if (schedules.Length == 0)
- {
- return true;
- }
-
- foreach (var i in schedules)
- {
- if (IsParentalScheduleAllowed(i, date))
- {
- return true;
- }
- }
- return false;
- }
-
- private bool IsParentalScheduleAllowed(AccessSchedule schedule, DateTime date)
- {
- if (date.Kind != DateTimeKind.Utc)
- {
- throw new ArgumentException("Utc date expected");
- }
-
- var localTime = date.ToLocalTime();
-
- return DayOfWeekHelper.GetDaysOfWeek(schedule.DayOfWeek).Contains(localTime.DayOfWeek) &&
- IsWithinTime(schedule, localTime);
- }
-
- private bool IsWithinTime(AccessSchedule schedule, DateTime localTime)
- {
- var hour = localTime.TimeOfDay.TotalHours;
-
- return hour >= schedule.StartHour && hour <= schedule.EndHour;
- }
-
- public bool IsFolderGrouped(Guid id)
- {
- foreach (var i in Configuration.GroupedFolders)
- {
- if (new Guid(i) == id)
- {
- return true;
- }
- }
- return false;
- }
-
- [JsonIgnore]
- public override bool SupportsPeople => false;
-
- public long InternalId { get; set; }
-
-
- }
-}
diff --git a/MediaBrowser.Controller/Entities/UserRootFolder.cs b/MediaBrowser.Controller/Entities/UserRootFolder.cs
index 8a68f830c..39f4e0b6c 100644
--- a/MediaBrowser.Controller/Entities/UserRootFolder.cs
+++ b/MediaBrowser.Controller/Entities/UserRootFolder.cs
@@ -4,6 +4,7 @@ using System.Linq;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Library;
using MediaBrowser.Model.Querying;
diff --git a/MediaBrowser.Controller/Entities/UserView.cs b/MediaBrowser.Controller/Entities/UserView.cs
index 4ce9ec6f8..ceca77bc0 100644
--- a/MediaBrowser.Controller/Entities/UserView.cs
+++ b/MediaBrowser.Controller/Entities/UserView.cs
@@ -3,8 +3,10 @@ using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
using MediaBrowser.Controller.TV;
using MediaBrowser.Model.Querying;
+using Microsoft.Extensions.Logging;
namespace MediaBrowser.Controller.Entities
{
@@ -66,7 +68,7 @@ namespace MediaBrowser.Controller.Entities
parent = LibraryManager.GetItemById(ParentId) as Folder ?? parent;
}
- return new UserViewBuilder(UserViewManager, LibraryManager, Logger, UserDataManager, TVSeriesManager, ConfigurationManager)
+ return new UserViewBuilder(UserViewManager, LibraryManager, LoggerFactory.CreateLogger<UserViewBuilder>(), UserDataManager, TVSeriesManager, ConfigurationManager)
.GetUserItems(parent, this, CollectionType, query);
}
@@ -110,7 +112,7 @@ namespace MediaBrowser.Controller.Entities
private static string[] UserSpecificViewTypes = new string[]
{
- MediaBrowser.Model.Entities.CollectionType.Playlists
+ Model.Entities.CollectionType.Playlists
};
public static bool IsUserSpecific(Folder folder)
@@ -139,8 +141,8 @@ namespace MediaBrowser.Controller.Entities
private static string[] ViewTypesEligibleForGrouping = new string[]
{
- MediaBrowser.Model.Entities.CollectionType.Movies,
- MediaBrowser.Model.Entities.CollectionType.TvShows,
+ Model.Entities.CollectionType.Movies,
+ Model.Entities.CollectionType.TvShows,
string.Empty
};
@@ -151,12 +153,12 @@ namespace MediaBrowser.Controller.Entities
private static string[] OriginalFolderViewTypes = new string[]
{
- MediaBrowser.Model.Entities.CollectionType.Books,
- MediaBrowser.Model.Entities.CollectionType.MusicVideos,
- MediaBrowser.Model.Entities.CollectionType.HomeVideos,
- MediaBrowser.Model.Entities.CollectionType.Photos,
- MediaBrowser.Model.Entities.CollectionType.Music,
- MediaBrowser.Model.Entities.CollectionType.BoxSets
+ Model.Entities.CollectionType.Books,
+ Model.Entities.CollectionType.MusicVideos,
+ Model.Entities.CollectionType.HomeVideos,
+ Model.Entities.CollectionType.Photos,
+ Model.Entities.CollectionType.Music,
+ Model.Entities.CollectionType.BoxSets
};
public static bool EnableOriginalFolder(string viewType)
diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
index 435a1e8da..bf0e56cf7 100644
--- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs
+++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
@@ -2,14 +2,19 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
+using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities.Movies;
-using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.TV;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
using Microsoft.Extensions.Logging;
+using Episode = MediaBrowser.Controller.Entities.TV.Episode;
+using MetadataProvider = MediaBrowser.Model.Entities.MetadataProvider;
+using Movie = MediaBrowser.Controller.Entities.Movies.Movie;
+using Season = MediaBrowser.Controller.Entities.TV.Season;
+using Series = MediaBrowser.Controller.Entities.TV.Series;
namespace MediaBrowser.Controller.Entities
{
@@ -17,7 +22,7 @@ namespace MediaBrowser.Controller.Entities
{
private readonly IUserViewManager _userViewManager;
private readonly ILibraryManager _libraryManager;
- private readonly ILogger _logger;
+ private readonly ILogger<UserViewBuilder> _logger;
private readonly IUserDataManager _userDataManager;
private readonly ITVSeriesManager _tvSeriesManager;
private readonly IServerConfigurationManager _config;
@@ -25,7 +30,7 @@ namespace MediaBrowser.Controller.Entities
public UserViewBuilder(
IUserViewManager userViewManager,
ILibraryManager libraryManager,
- ILogger logger,
+ ILogger<UserViewBuilder> logger,
IUserDataManager userDataManager,
ITVSeriesManager tvSeriesManager,
IServerConfigurationManager config)
@@ -140,14 +145,15 @@ namespace MediaBrowser.Controller.Entities
return parent.QueryRecursive(query);
}
- var list = new List<BaseItem>();
-
- list.Add(GetUserView(SpecialFolder.MovieResume, "HeaderContinueWatching", "0", parent));
- list.Add(GetUserView(SpecialFolder.MovieLatest, "Latest", "1", parent));
- list.Add(GetUserView(SpecialFolder.MovieMovies, "Movies", "2", parent));
- list.Add(GetUserView(SpecialFolder.MovieCollections, "Collections", "3", parent));
- list.Add(GetUserView(SpecialFolder.MovieFavorites, "Favorites", "4", parent));
- list.Add(GetUserView(SpecialFolder.MovieGenres, "Genres", "5", parent));
+ var list = new List<BaseItem>
+ {
+ GetUserView(SpecialFolder.MovieResume, "HeaderContinueWatching", "0", parent),
+ GetUserView(SpecialFolder.MovieLatest, "Latest", "1", parent),
+ GetUserView(SpecialFolder.MovieMovies, "Movies", "2", parent),
+ GetUserView(SpecialFolder.MovieCollections, "Collections", "3", parent),
+ GetUserView(SpecialFolder.MovieFavorites, "Favorites", "4", parent),
+ GetUserView(SpecialFolder.MovieGenres, "Genres", "5", parent)
+ };
return GetResult(list, parent, query);
}
@@ -293,21 +299,27 @@ namespace MediaBrowser.Controller.Entities
if (query.IncludeItemTypes.Length == 0)
{
- query.IncludeItemTypes = new[] { typeof(Series).Name, typeof(Season).Name, typeof(Episode).Name };
+ query.IncludeItemTypes = new[]
+ {
+ nameof(Series),
+ nameof(Season),
+ nameof(Episode)
+ };
}
return parent.QueryRecursive(query);
}
- var list = new List<BaseItem>();
-
- list.Add(GetUserView(SpecialFolder.TvResume, "HeaderContinueWatching", "0", parent));
- list.Add(GetUserView(SpecialFolder.TvNextUp, "HeaderNextUp", "1", parent));
- list.Add(GetUserView(SpecialFolder.TvLatest, "Latest", "2", parent));
- list.Add(GetUserView(SpecialFolder.TvShowSeries, "Shows", "3", parent));
- list.Add(GetUserView(SpecialFolder.TvFavoriteSeries, "HeaderFavoriteShows", "4", parent));
- list.Add(GetUserView(SpecialFolder.TvFavoriteEpisodes, "HeaderFavoriteEpisodes", "5", parent));
- list.Add(GetUserView(SpecialFolder.TvGenres, "Genres", "6", parent));
+ var list = new List<BaseItem>
+ {
+ GetUserView(SpecialFolder.TvResume, "HeaderContinueWatching", "0", parent),
+ GetUserView(SpecialFolder.TvNextUp, "HeaderNextUp", "1", parent),
+ GetUserView(SpecialFolder.TvLatest, "Latest", "2", parent),
+ GetUserView(SpecialFolder.TvShowSeries, "Shows", "3", parent),
+ GetUserView(SpecialFolder.TvFavoriteSeries, "HeaderFavoriteShows", "4", parent),
+ GetUserView(SpecialFolder.TvFavoriteEpisodes, "HeaderFavoriteEpisodes", "5", parent),
+ GetUserView(SpecialFolder.TvGenres, "Genres", "6", parent)
+ };
return GetResult(list, parent, query);
}
@@ -417,7 +429,8 @@ namespace MediaBrowser.Controller.Entities
};
}
- private QueryResult<BaseItem> GetResult<T>(IEnumerable<T> items,
+ private QueryResult<BaseItem> GetResult<T>(
+ IEnumerable<T> items,
BaseItem queryParent,
InternalItemsQuery query)
where T : BaseItem
@@ -611,7 +624,7 @@ namespace MediaBrowser.Controller.Entities
{
var filterValue = query.HasImdbId.Value;
- var hasValue = !string.IsNullOrEmpty(item.GetProviderId(MetadataProviders.Imdb));
+ var hasValue = !string.IsNullOrEmpty(item.GetProviderId(MetadataProvider.Imdb));
if (hasValue != filterValue)
{
@@ -623,7 +636,7 @@ namespace MediaBrowser.Controller.Entities
{
var filterValue = query.HasTmdbId.Value;
- var hasValue = !string.IsNullOrEmpty(item.GetProviderId(MetadataProviders.Tmdb));
+ var hasValue = !string.IsNullOrEmpty(item.GetProviderId(MetadataProvider.Tmdb));
if (hasValue != filterValue)
{
@@ -635,7 +648,7 @@ namespace MediaBrowser.Controller.Entities
{
var filterValue = query.HasTvdbId.Value;
- var hasValue = !string.IsNullOrEmpty(item.GetProviderId(MetadataProviders.Tvdb));
+ var hasValue = !string.IsNullOrEmpty(item.GetProviderId(MetadataProvider.Tvdb));
if (hasValue != filterValue)
{
diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs
index c3ea7f347..72eb67a06 100644
--- a/MediaBrowser.Controller/Entities/Video.cs
+++ b/MediaBrowser.Controller/Entities/Video.cs
@@ -272,13 +272,13 @@ namespace MediaBrowser.Controller.Entities
{
if (ExtraType.HasValue)
{
- var key = this.GetProviderId(MetadataProviders.Tmdb);
+ var key = this.GetProviderId(MetadataProvider.Tmdb);
if (!string.IsNullOrEmpty(key))
{
list.Insert(0, GetUserDataKey(key));
}
- key = this.GetProviderId(MetadataProviders.Imdb);
+ key = this.GetProviderId(MetadataProvider.Imdb);
if (!string.IsNullOrEmpty(key))
{
list.Insert(0, GetUserDataKey(key));
@@ -286,13 +286,13 @@ namespace MediaBrowser.Controller.Entities
}
else
{
- var key = this.GetProviderId(MetadataProviders.Imdb);
+ var key = this.GetProviderId(MetadataProvider.Imdb);
if (!string.IsNullOrEmpty(key))
{
list.Insert(0, key);
}
- key = this.GetProviderId(MetadataProviders.Tmdb);
+ key = this.GetProviderId(MetadataProvider.Tmdb);
if (!string.IsNullOrEmpty(key))
{
list.Insert(0, key);
diff --git a/MediaBrowser.Controller/IO/FileData.cs b/MediaBrowser.Controller/IO/FileData.cs
index 4bbb60283..666a3f76b 100644
--- a/MediaBrowser.Controller/IO/FileData.cs
+++ b/MediaBrowser.Controller/IO/FileData.cs
@@ -35,7 +35,8 @@ namespace MediaBrowser.Controller.IO
/// <param name="resolveShortcuts">if set to <c>true</c> [resolve shortcuts].</param>
/// <returns>Dictionary{System.StringFileSystemInfo}.</returns>
/// <exception cref="ArgumentNullException">path</exception>
- public static FileSystemMetadata[] GetFilteredFileSystemEntries(IDirectoryService directoryService,
+ public static FileSystemMetadata[] GetFilteredFileSystemEntries(
+ IDirectoryService directoryService,
string path,
IFileSystem fileSystem,
IServerApplicationHost appHost,
diff --git a/MediaBrowser.Controller/Library/IIntroProvider.cs b/MediaBrowser.Controller/Library/IIntroProvider.cs
index d9d1ca8c7..aa7001611 100644
--- a/MediaBrowser.Controller/Library/IIntroProvider.cs
+++ b/MediaBrowser.Controller/Library/IIntroProvider.cs
@@ -15,7 +15,7 @@ namespace MediaBrowser.Controller.Library
/// <param name="item">The item.</param>
/// <param name="user">The user.</param>
/// <returns>IEnumerable{System.String}.</returns>
- Task<IEnumerable<IntroInfo>> GetIntros(BaseItem item, User user);
+ Task<IEnumerable<IntroInfo>> GetIntros(BaseItem item, Jellyfin.Data.Entities.User user);
/// <summary>
/// Gets all intro files.
diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs
index 2e1c97f67..d7237039e 100644
--- a/MediaBrowser.Controller/Library/ILibraryManager.cs
+++ b/MediaBrowser.Controller/Library/ILibraryManager.cs
@@ -2,10 +2,10 @@ using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Controller.Sorting;
@@ -14,6 +14,9 @@ using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Querying;
+using Episode = MediaBrowser.Controller.Entities.TV.Episode;
+using Genre = MediaBrowser.Controller.Entities.Genre;
+using Person = MediaBrowser.Controller.Entities.Person;
namespace MediaBrowser.Controller.Library
{
@@ -27,14 +30,18 @@ namespace MediaBrowser.Controller.Library
/// </summary>
/// <param name="fileInfo">The file information.</param>
/// <param name="parent">The parent.</param>
+ /// <param name="allowIgnorePath">Allow the path to be ignored.</param>
/// <returns>BaseItem.</returns>
- BaseItem ResolvePath(FileSystemMetadata fileInfo,
- Folder parent = null);
+ BaseItem ResolvePath(
+ FileSystemMetadata fileInfo,
+ Folder parent = null,
+ bool allowIgnorePath = true);
/// <summary>
/// Resolves a set of files into a list of BaseItem
/// </summary>
- IEnumerable<BaseItem> ResolvePaths(IEnumerable<FileSystemMetadata> files,
+ IEnumerable<BaseItem> ResolvePaths(
+ IEnumerable<FileSystemMetadata> files,
IDirectoryService directoryService,
Folder parent,
LibraryOptions libraryOptions,
@@ -118,7 +125,7 @@ namespace MediaBrowser.Controller.Library
/// </summary>
void QueueLibraryScan();
- void UpdateImages(BaseItem item);
+ void UpdateImages(BaseItem item, bool forceUpdate = false);
/// <summary>
/// Gets the default view.
@@ -195,6 +202,7 @@ namespace MediaBrowser.Controller.Library
/// Updates the item.
/// </summary>
void UpdateItems(IEnumerable<BaseItem> items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken);
+
void UpdateItem(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken);
/// <summary>
@@ -284,7 +292,8 @@ namespace MediaBrowser.Controller.Library
/// <param name="parentId">The parent identifier.</param>
/// <param name="viewType">Type of the view.</param>
/// <param name="sortName">Name of the sort.</param>
- UserView GetNamedView(User user,
+ UserView GetNamedView(
+ User user,
string name,
Guid parentId,
string viewType,
@@ -297,7 +306,8 @@ namespace MediaBrowser.Controller.Library
/// <param name="name">The name.</param>
/// <param name="viewType">Type of the view.</param>
/// <param name="sortName">Name of the sort.</param>
- UserView GetNamedView(User user,
+ UserView GetNamedView(
+ User user,
string name,
string viewType,
string sortName);
diff --git a/MediaBrowser.Controller/Library/IMediaSourceManager.cs b/MediaBrowser.Controller/Library/IMediaSourceManager.cs
index 0ceabd0e6..94528ff77 100644
--- a/MediaBrowser.Controller/Library/IMediaSourceManager.cs
+++ b/MediaBrowser.Controller/Library/IMediaSourceManager.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Dto;
diff --git a/MediaBrowser.Controller/Library/IMusicManager.cs b/MediaBrowser.Controller/Library/IMusicManager.cs
index 554dd0895..36b250ec9 100644
--- a/MediaBrowser.Controller/Library/IMusicManager.cs
+++ b/MediaBrowser.Controller/Library/IMusicManager.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
diff --git a/MediaBrowser.Controller/Library/IUserDataManager.cs b/MediaBrowser.Controller/Library/IUserDataManager.cs
index eb735d31a..f5ccad671 100644
--- a/MediaBrowser.Controller/Library/IUserDataManager.cs
+++ b/MediaBrowser.Controller/Library/IUserDataManager.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Threading;
+using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Dto;
@@ -27,7 +28,7 @@ namespace MediaBrowser.Controller.Library
/// <param name="reason">The reason.</param>
/// <param name="cancellationToken">The cancellation token.</param>
void SaveUserData(Guid userId, BaseItem item, UserItemData userData, UserDataSaveReason reason, CancellationToken cancellationToken);
- void SaveUserData(User userId, BaseItem item, UserItemData userData, UserDataSaveReason reason, CancellationToken cancellationToken);
+ void SaveUserData(User user, BaseItem item, UserItemData userData, UserDataSaveReason reason, CancellationToken cancellationToken);
UserItemData GetUserData(User user, BaseItem item);
diff --git a/MediaBrowser.Controller/Library/IUserManager.cs b/MediaBrowser.Controller/Library/IUserManager.cs
index ec6cb35eb..b5b2e4729 100644
--- a/MediaBrowser.Controller/Library/IUserManager.cs
+++ b/MediaBrowser.Controller/Library/IUserManager.cs
@@ -1,9 +1,8 @@
using System;
using System.Collections.Generic;
-using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Authentication;
-using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Events;
@@ -17,36 +16,46 @@ namespace MediaBrowser.Controller.Library
public interface IUserManager
{
/// <summary>
- /// Gets the users.
+ /// Occurs when a user is updated.
/// </summary>
- /// <value>The users.</value>
- IEnumerable<User> Users { get; }
+ event EventHandler<GenericEventArgs<User>> OnUserUpdated;
/// <summary>
- /// Gets the user ids.
+ /// Occurs when a user is created.
/// </summary>
- /// <value>The users ids.</value>
- IEnumerable<Guid> UsersIds { get; }
+ event EventHandler<GenericEventArgs<User>> OnUserCreated;
/// <summary>
- /// Occurs when [user updated].
+ /// Occurs when a user is deleted.
/// </summary>
- event EventHandler<GenericEventArgs<User>> UserUpdated;
+ event EventHandler<GenericEventArgs<User>> OnUserDeleted;
/// <summary>
- /// Occurs when [user deleted].
+ /// Occurs when a user's password is changed.
/// </summary>
- event EventHandler<GenericEventArgs<User>> UserDeleted;
+ event EventHandler<GenericEventArgs<User>> OnUserPasswordChanged;
- event EventHandler<GenericEventArgs<User>> UserCreated;
-
- event EventHandler<GenericEventArgs<User>> UserPolicyUpdated;
+ /// <summary>
+ /// Occurs when a user is locked out.
+ /// </summary>
+ event EventHandler<GenericEventArgs<User>> OnUserLockedOut;
- event EventHandler<GenericEventArgs<User>> UserConfigurationUpdated;
+ /// <summary>
+ /// Gets the users.
+ /// </summary>
+ /// <value>The users.</value>
+ IEnumerable<User> Users { get; }
- event EventHandler<GenericEventArgs<User>> UserPasswordChanged;
+ /// <summary>
+ /// Gets the user ids.
+ /// </summary>
+ /// <value>The users ids.</value>
+ IEnumerable<Guid> UsersIds { get; }
- event EventHandler<GenericEventArgs<User>> UserLockedOut;
+ /// <summary>
+ /// Initializes the user manager and ensures that a user exists.
+ /// </summary>
+ void Initialize();
/// <summary>
/// Gets a user by Id.
@@ -64,13 +73,6 @@ namespace MediaBrowser.Controller.Library
User GetUserByName(string name);
/// <summary>
- /// Refreshes metadata for each user
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task RefreshUsersMetadata(CancellationToken cancellationToken);
-
- /// <summary>
/// Renames the user.
/// </summary>
/// <param name="user">The user.</param>
@@ -89,19 +91,27 @@ namespace MediaBrowser.Controller.Library
void UpdateUser(User user);
/// <summary>
- /// Creates the user.
+ /// Updates the user.
/// </summary>
- /// <param name="name">The name.</param>
- /// <returns>User.</returns>
+ /// <param name="user">The user.</param>
+ /// <exception cref="ArgumentNullException">If user is <c>null</c>.</exception>
+ /// <exception cref="ArgumentException">If the provided user doesn't exist.</exception>
+ /// <returns>A task representing the update of the user.</returns>
+ Task UpdateUserAsync(User user);
+
+ /// <summary>
+ /// Creates a user with the specified name.
+ /// </summary>
+ /// <param name="name">The name of the new user.</param>
+ /// <returns>The created user.</returns>
/// <exception cref="ArgumentNullException">name</exception>
/// <exception cref="ArgumentException"></exception>
User CreateUser(string name);
/// <summary>
- /// Deletes the user.
+ /// Deletes the specified user.
/// </summary>
- /// <param name="user">The user.</param>
- /// <returns>Task.</returns>
+ /// <param name="user">The user to be deleted.</param>
void DeleteUser(User user);
/// <summary>
@@ -112,13 +122,6 @@ namespace MediaBrowser.Controller.Library
Task ResetPassword(User user);
/// <summary>
- /// Gets the offline user dto.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <returns>UserDto.</returns>
- UserDto GetOfflineUserDto(User user);
-
- /// <summary>
/// Resets the easy password.
/// </summary>
/// <param name="user">The user.</param>
@@ -144,14 +147,6 @@ namespace MediaBrowser.Controller.Library
UserDto GetUserDto(User user, string remoteEndPoint = null);
/// <summary>
- /// Gets the user public dto.
- /// </summary>
- /// <param name="user">Ther user.</param>\
- /// <param name="remoteEndPoint">The remote end point.</param>
- /// <returns>A public UserDto, aka a UserDto stripped of personal data.</returns>
- PublicUserDto GetPublicUserDto(User user, string remoteEndPoint = null);
-
- /// <summary>
/// Authenticates the user.
/// </summary>
Task<User> AuthenticateUser(string username, string password, string passwordSha1, string remoteEndPoint, bool isUserSession);
@@ -171,47 +166,34 @@ namespace MediaBrowser.Controller.Library
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
Task<PinRedeemResult> RedeemPasswordResetPin(string pin);
- /// <summary>
- /// Gets the user policy.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <returns>UserPolicy.</returns>
- UserPolicy GetUserPolicy(User user);
+ void AddParts(IEnumerable<IAuthenticationProvider> authenticationProviders, IEnumerable<IPasswordResetProvider> passwordResetProviders);
- /// <summary>
- /// Gets the user configuration.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <returns>UserConfiguration.</returns>
- UserConfiguration GetUserConfiguration(User user);
+ NameIdPair[] GetAuthenticationProviders();
+
+ NameIdPair[] GetPasswordResetProviders();
/// <summary>
- /// Updates the configuration.
+ /// This method updates the user's configuration.
+ /// This is only included as a stopgap until the new API, using this internally is not recommended.
+ /// Instead, modify the user object directly, then call <see cref="UpdateUser"/>.
/// </summary>
- /// <param name="userId">The user identifier.</param>
- /// <param name="newConfiguration">The new configuration.</param>
- /// <returns>Task.</returns>
- void UpdateConfiguration(Guid userId, UserConfiguration newConfiguration);
-
- void UpdateConfiguration(User user, UserConfiguration newConfiguration);
+ /// <param name="userId">The user's Id.</param>
+ /// <param name="config">The request containing the new user configuration.</param>
+ void UpdateConfiguration(Guid userId, UserConfiguration config);
/// <summary>
- /// Updates the user policy.
+ /// This method updates the user's policy.
+ /// This is only included as a stopgap until the new API, using this internally is not recommended.
+ /// Instead, modify the user object directly, then call <see cref="UpdateUser"/>.
/// </summary>
- /// <param name="userId">The user identifier.</param>
- /// <param name="userPolicy">The user policy.</param>
- void UpdateUserPolicy(Guid userId, UserPolicy userPolicy);
+ /// <param name="userId">The user's Id.</param>
+ /// <param name="policy">The request containing the new user policy.</param>
+ void UpdatePolicy(Guid userId, UserPolicy policy);
/// <summary>
- /// Makes the valid username.
+ /// Clears the user's profile image.
/// </summary>
- /// <param name="username">The username.</param>
- /// <returns>System.String.</returns>
- string MakeValidUsername(string username);
-
- void AddParts(IEnumerable<IAuthenticationProvider> authenticationProviders, IEnumerable<IPasswordResetProvider> passwordResetProviders);
-
- NameIdPair[] GetAuthenticationProviders();
- NameIdPair[] GetPasswordResetProviders();
+ /// <param name="user">The user.</param>
+ void ClearProfileImage(User user);
}
}
diff --git a/MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs b/MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs
index b0302d04c..b4e205184 100644
--- a/MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs
+++ b/MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Dto;
diff --git a/MediaBrowser.Controller/Library/Profiler.cs b/MediaBrowser.Controller/Library/Profiler.cs
index 46a97d181..0febef3d3 100644
--- a/MediaBrowser.Controller/Library/Profiler.cs
+++ b/MediaBrowser.Controller/Library/Profiler.cs
@@ -21,7 +21,7 @@ namespace MediaBrowser.Controller.Library
/// <summary>
/// The _logger
/// </summary>
- private readonly ILogger _logger;
+ private readonly ILogger<Profiler> _logger;
/// <summary>
/// Initializes a new instance of the <see cref="Profiler" /> class.
diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
index e02c387e4..bc3bf78f0 100644
--- a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
+++ b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs
index 60391bb83..10af98121 100644
--- a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs
+++ b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs
@@ -3,8 +3,8 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text.Json.Serialization;
+using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.LiveTv;
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
index 13df85aed..5b901d223 100644
--- a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
+++ b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
@@ -3,10 +3,10 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text.Json.Serialization;
+using Jellyfin.Data.Enums;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Providers;
@@ -26,13 +26,13 @@ namespace MediaBrowser.Controller.LiveTv
if (!IsSeries)
{
- var key = this.GetProviderId(MetadataProviders.Imdb);
+ var key = this.GetProviderId(MetadataProvider.Imdb);
if (!string.IsNullOrEmpty(key))
{
list.Insert(0, key);
}
- key = this.GetProviderId(MetadataProviders.Tmdb);
+ key = this.GetProviderId(MetadataProvider.Tmdb);
if (!string.IsNullOrEmpty(key))
{
list.Insert(0, key);
@@ -253,7 +253,7 @@ namespace MediaBrowser.Controller.LiveTv
{
var list = base.GetRelatedUrls();
- var imdbId = this.GetProviderId(MetadataProviders.Imdb);
+ var imdbId = this.GetProviderId(MetadataProvider.Imdb);
if (!string.IsNullOrEmpty(imdbId))
{
if (IsMovie)
diff --git a/MediaBrowser.Controller/LiveTv/TimerEventInfo.cs b/MediaBrowser.Controller/LiveTv/TimerEventInfo.cs
index cfec39b4e..1b8f41db6 100644
--- a/MediaBrowser.Controller/LiveTv/TimerEventInfo.cs
+++ b/MediaBrowser.Controller/LiveTv/TimerEventInfo.cs
@@ -1,10 +1,19 @@
+#nullable enable
+#pragma warning disable CS1591
+
using System;
namespace MediaBrowser.Controller.LiveTv
{
public class TimerEventInfo
{
- public string Id { get; set; }
- public Guid ProgramId { get; set; }
+ public TimerEventInfo(string id)
+ {
+ Id = id;
+ }
+
+ public string Id { get; }
+
+ public Guid? ProgramId { get; set; }
}
}
diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
index 223bbe1de..73e966344 100644
--- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj
+++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
@@ -13,8 +13,8 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.4" />
- <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="3.1.4" />
+ <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.5" />
+ <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="3.1.5" />
</ItemGroup>
<ItemGroup>
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index 61a330675..8ce106469 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -5,6 +5,7 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
+using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Extensions;
using MediaBrowser.Model.Configuration;
@@ -1338,7 +1339,7 @@ namespace MediaBrowser.Controller.MediaEncoding
transcoderChannelLimit = 6;
}
- var isTranscodingAudio = !string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase);
+ var isTranscodingAudio = !EncodingHelper.IsCopyCodec(codec);
int? resultChannels = state.GetRequestedAudioChannels(codec);
if (isTranscodingAudio)
@@ -1734,7 +1735,8 @@ namespace MediaBrowser.Controller.MediaEncoding
var hasTextSubs = state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
- if (string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase) || (string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) && !hasTextSubs)
+ if ((string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase)
+ || (string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) && !hasTextSubs))
&& width.HasValue
&& height.HasValue)
{
@@ -1991,7 +1993,7 @@ namespace MediaBrowser.Controller.MediaEncoding
filters.Add("hwupload");
}
- // When the input may or may not be hardware QSV decodable
+ // When the input may or may not be hardware QSV decodable
else if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase))
{
if (!hasTextSubs)
@@ -2147,7 +2149,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var user = state.User;
// If the user doesn't have access to transcoding, then force stream copy, regardless of whether it will be compatible or not
- if (user != null && !user.Policy.EnableVideoPlaybackTranscoding)
+ if (user != null && !user.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding))
{
state.OutputVideoCodec = "copy";
}
@@ -2163,7 +2165,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var user = state.User;
// If the user doesn't have access to transcoding, then force stream copy, regardless of whether it will be compatible or not
- if (user != null && !user.Policy.EnableAudioPlaybackTranscoding)
+ if (user != null && !user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding))
{
state.OutputAudioCodec = "copy";
}
@@ -2248,7 +2250,7 @@ namespace MediaBrowser.Controller.MediaEncoding
flags.Add("+ignidx");
}
- if (state.GenPtsInput || string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
+ if (state.GenPtsInput || EncodingHelper.IsCopyCodec(state.OutputVideoCodec))
{
flags.Add("+genpts");
}
@@ -2511,7 +2513,7 @@ namespace MediaBrowser.Controller.MediaEncoding
/// </summary>
protected string GetHardwareAcceleratedVideoDecoder(EncodingJobInfo state, EncodingOptions encodingOptions)
{
- if (string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
+ if (EncodingHelper.IsCopyCodec(state.OutputVideoCodec))
{
return null;
}
@@ -2799,7 +2801,7 @@ namespace MediaBrowser.Controller.MediaEncoding
args += " -mpegts_m2ts_mode 1";
}
- if (string.Equals(videoCodec, "copy", StringComparison.OrdinalIgnoreCase))
+ if (EncodingHelper.IsCopyCodec(videoCodec))
{
if (state.VideoStream != null
&& string.Equals(state.OutputContainer, "ts", StringComparison.OrdinalIgnoreCase)
@@ -2901,7 +2903,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var args = "-codec:a:0 " + codec;
- if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
+ if (EncodingHelper.IsCopyCodec(codec))
{
return args;
}
@@ -2973,5 +2975,10 @@ namespace MediaBrowser.Controller.MediaEncoding
string.Empty,
string.Empty).Trim();
}
+
+ public static bool IsCopyCodec(string codec)
+ {
+ return string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase);
+ }
}
}
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
index 1127a08de..acf1aae89 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
@@ -2,7 +2,7 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
-using MediaBrowser.Controller.Entities;
+using Jellyfin.Data.Entities;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Dto;
@@ -18,22 +18,37 @@ namespace MediaBrowser.Controller.MediaEncoding
public class EncodingJobInfo
{
public MediaStream VideoStream { get; set; }
+
public VideoType VideoType { get; set; }
+
public Dictionary<string, string> RemoteHttpHeaders { get; set; }
+
public string OutputVideoCodec { get; set; }
+
public MediaProtocol InputProtocol { get; set; }
+
public string MediaPath { get; set; }
+
public bool IsInputVideo { get; set; }
+
public IIsoMount IsoMount { get; set; }
+
public string[] PlayableStreamFileNames { get; set; }
+
public string OutputAudioCodec { get; set; }
+
public int? OutputVideoBitrate { get; set; }
+
public MediaStream SubtitleStream { get; set; }
+
public SubtitleDeliveryMethod SubtitleDeliveryMethod { get; set; }
+
public string[] SupportedSubtitleCodecs { get; set; }
public int InternalSubtitleStreamOffset { get; set; }
+
public MediaSourceInfo MediaSource { get; set; }
+
public User User { get; set; }
public long? RunTimeTicks { get; set; }
@@ -302,7 +317,7 @@ namespace MediaBrowser.Controller.MediaEncoding
return false;
}
- return BaseRequest.BreakOnNonKeyFrames && string.Equals(videoCodec, "copy", StringComparison.OrdinalIgnoreCase);
+ return BaseRequest.BreakOnNonKeyFrames && EncodingHelper.IsCopyCodec(videoCodec);
}
return false;
@@ -367,7 +382,7 @@ namespace MediaBrowser.Controller.MediaEncoding
get
{
if (BaseRequest.Static
- || string.Equals(OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase))
+ || EncodingHelper.IsCopyCodec(OutputAudioCodec))
{
if (AudioStream != null)
{
@@ -390,7 +405,7 @@ namespace MediaBrowser.Controller.MediaEncoding
get
{
if (BaseRequest.Static
- || string.Equals(OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase))
+ || EncodingHelper.IsCopyCodec(OutputAudioCodec))
{
if (AudioStream != null)
{
@@ -409,7 +424,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
get
{
- if (BaseRequest.Static || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
+ if (BaseRequest.Static || EncodingHelper.IsCopyCodec(OutputVideoCodec))
{
return VideoStream?.Level;
}
@@ -433,7 +448,7 @@ namespace MediaBrowser.Controller.MediaEncoding
get
{
if (BaseRequest.Static
- || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
+ || EncodingHelper.IsCopyCodec(OutputVideoCodec))
{
return VideoStream?.BitDepth;
}
@@ -451,7 +466,7 @@ namespace MediaBrowser.Controller.MediaEncoding
get
{
if (BaseRequest.Static
- || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
+ || EncodingHelper.IsCopyCodec(OutputVideoCodec))
{
return VideoStream?.RefFrames;
}
@@ -468,7 +483,7 @@ namespace MediaBrowser.Controller.MediaEncoding
get
{
if (BaseRequest.Static
- || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
+ || EncodingHelper.IsCopyCodec(OutputVideoCodec))
{
return VideoStream == null ? null : (VideoStream.AverageFrameRate ?? VideoStream.RealFrameRate);
}
@@ -499,7 +514,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
get
{
- if (BaseRequest.Static || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
+ if (BaseRequest.Static || EncodingHelper.IsCopyCodec(OutputVideoCodec))
{
return VideoStream?.PacketLength;
}
@@ -515,7 +530,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
get
{
- if (BaseRequest.Static || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
+ if (BaseRequest.Static || EncodingHelper.IsCopyCodec(OutputVideoCodec))
{
return VideoStream?.Profile;
}
@@ -535,7 +550,7 @@ namespace MediaBrowser.Controller.MediaEncoding
get
{
if (BaseRequest.Static
- || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
+ || EncodingHelper.IsCopyCodec(OutputVideoCodec))
{
return VideoStream?.CodecTag;
}
@@ -549,7 +564,7 @@ namespace MediaBrowser.Controller.MediaEncoding
get
{
if (BaseRequest.Static
- || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
+ || EncodingHelper.IsCopyCodec(OutputVideoCodec))
{
return VideoStream?.IsAnamorphic;
}
@@ -562,7 +577,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
get
{
- if (string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
+ if (EncodingHelper.IsCopyCodec(OutputVideoCodec))
{
return VideoStream?.Codec;
}
@@ -575,7 +590,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
get
{
- if (string.Equals(OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase))
+ if (EncodingHelper.IsCopyCodec(OutputAudioCodec))
{
return AudioStream?.Codec;
}
@@ -589,7 +604,7 @@ namespace MediaBrowser.Controller.MediaEncoding
get
{
if (BaseRequest.Static
- || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
+ || EncodingHelper.IsCopyCodec(OutputVideoCodec))
{
return VideoStream?.IsInterlaced;
}
@@ -607,7 +622,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
get
{
- if (BaseRequest.Static || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
+ if (BaseRequest.Static || EncodingHelper.IsCopyCodec(OutputVideoCodec))
{
return VideoStream?.IsAVC;
}
diff --git a/MediaBrowser.Controller/Net/AuthorizationInfo.cs b/MediaBrowser.Controller/Net/AuthorizationInfo.cs
index 3e004763d..4361e253b 100644
--- a/MediaBrowser.Controller/Net/AuthorizationInfo.cs
+++ b/MediaBrowser.Controller/Net/AuthorizationInfo.cs
@@ -1,36 +1,40 @@
using System;
-using MediaBrowser.Controller.Entities;
+using Jellyfin.Data.Entities;
namespace MediaBrowser.Controller.Net
{
public class AuthorizationInfo
{
/// <summary>
- /// Gets or sets the user identifier.
+ /// Gets the user identifier.
/// </summary>
/// <value>The user identifier.</value>
- public Guid UserId => User == null ? Guid.Empty : User.Id;
+ public Guid UserId => User?.Id ?? Guid.Empty;
/// <summary>
/// Gets or sets the device identifier.
/// </summary>
/// <value>The device identifier.</value>
public string DeviceId { get; set; }
+
/// <summary>
/// Gets or sets the device.
/// </summary>
/// <value>The device.</value>
public string Device { get; set; }
+
/// <summary>
/// Gets or sets the client.
/// </summary>
/// <value>The client.</value>
public string Client { get; set; }
+
/// <summary>
/// Gets or sets the version.
/// </summary>
/// <value>The version.</value>
public string Version { get; set; }
+
/// <summary>
/// Gets or sets the token.
/// </summary>
diff --git a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs
index 1162bff13..7dca793c6 100644
--- a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs
+++ b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs
@@ -40,9 +40,9 @@ namespace MediaBrowser.Controller.Net
/// <summary>
/// The logger
/// </summary>
- protected ILogger Logger;
+ protected ILogger<BasePeriodicWebSocketListener<TReturnDataType, TStateType>> Logger;
- protected BasePeriodicWebSocketListener(ILogger logger)
+ protected BasePeriodicWebSocketListener(ILogger<BasePeriodicWebSocketListener<TReturnDataType, TStateType>> logger)
{
if (logger == null)
{
@@ -104,7 +104,7 @@ namespace MediaBrowser.Controller.Net
}
}
- protected void SendData(bool force)
+ protected async Task SendData(bool force)
{
Tuple<IWebSocketConnection, CancellationTokenSource, TStateType>[] tuples;
@@ -128,13 +128,18 @@ namespace MediaBrowser.Controller.Net
.ToArray();
}
- foreach (var tuple in tuples)
+ IEnumerable<Task> GetTasks()
{
- SendData(tuple);
+ foreach (var tuple in tuples)
+ {
+ yield return SendData(tuple);
+ }
}
+
+ await Task.WhenAll(GetTasks()).ConfigureAwait(false);
}
- private async void SendData(Tuple<IWebSocketConnection, CancellationTokenSource, TStateType> tuple)
+ private async Task SendData(Tuple<IWebSocketConnection, CancellationTokenSource, TStateType> tuple)
{
var connection = tuple.Item1;
@@ -148,11 +153,13 @@ namespace MediaBrowser.Controller.Net
if (data != null)
{
- await connection.SendAsync(new WebSocketMessage<TReturnDataType>
- {
- MessageType = Name,
- Data = data
- }, cancellationToken).ConfigureAwait(false);
+ await connection.SendAsync(
+ new WebSocketMessage<TReturnDataType>
+ {
+ MessageType = Name,
+ Data = data
+ },
+ cancellationToken).ConfigureAwait(false);
state.DateLastSendUtc = DateTime.UtcNow;
}
diff --git a/MediaBrowser.Controller/Net/IAuthService.cs b/MediaBrowser.Controller/Net/IAuthService.cs
index 9132404a0..d8f6d19da 100644
--- a/MediaBrowser.Controller/Net/IAuthService.cs
+++ b/MediaBrowser.Controller/Net/IAuthService.cs
@@ -1,6 +1,6 @@
#nullable enable
-using MediaBrowser.Controller.Entities;
+using Jellyfin.Data.Entities;
using MediaBrowser.Model.Services;
using Microsoft.AspNetCore.Http;
@@ -9,6 +9,7 @@ namespace MediaBrowser.Controller.Net
public interface IAuthService
{
void Authenticate(IRequest request, IAuthenticationAttributes authAttribtues);
+
User? Authenticate(HttpRequest request, IAuthenticationAttributes authAttribtues);
}
}
diff --git a/MediaBrowser.Controller/Net/ISessionContext.cs b/MediaBrowser.Controller/Net/ISessionContext.cs
index 5c3c19f6b..421ac3fe2 100644
--- a/MediaBrowser.Controller/Net/ISessionContext.cs
+++ b/MediaBrowser.Controller/Net/ISessionContext.cs
@@ -1,4 +1,4 @@
-using MediaBrowser.Controller.Entities;
+using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Services;
diff --git a/MediaBrowser.Controller/Net/IWebSocketConnection.cs b/MediaBrowser.Controller/Net/IWebSocketConnection.cs
index 09e43c683..3ef8e5f6d 100644
--- a/MediaBrowser.Controller/Net/IWebSocketConnection.cs
+++ b/MediaBrowser.Controller/Net/IWebSocketConnection.cs
@@ -24,6 +24,12 @@ namespace MediaBrowser.Controller.Net
DateTime LastActivityDate { get; }
/// <summary>
+ /// Gets or sets the date of last Keeplive received.
+ /// </summary>
+ /// <value>The date of last Keeplive received.</value>
+ DateTime LastKeepAliveDate { get; set; }
+
+ /// <summary>
/// Gets or sets the query string.
/// </summary>
/// <value>The query string.</value>
diff --git a/MediaBrowser.Controller/Notifications/INotificationService.cs b/MediaBrowser.Controller/Notifications/INotificationService.cs
index 8c6019923..ab5eb13cd 100644
--- a/MediaBrowser.Controller/Notifications/INotificationService.cs
+++ b/MediaBrowser.Controller/Notifications/INotificationService.cs
@@ -1,6 +1,6 @@
using System.Threading;
using System.Threading.Tasks;
-using MediaBrowser.Controller.Entities;
+using Jellyfin.Data.Entities;
namespace MediaBrowser.Controller.Notifications
{
diff --git a/MediaBrowser.Controller/Notifications/UserNotification.cs b/MediaBrowser.Controller/Notifications/UserNotification.cs
index 3f46468b3..a1029589b 100644
--- a/MediaBrowser.Controller/Notifications/UserNotification.cs
+++ b/MediaBrowser.Controller/Notifications/UserNotification.cs
@@ -1,5 +1,5 @@
using System;
-using MediaBrowser.Controller.Entities;
+using Jellyfin.Data.Entities;
using MediaBrowser.Model.Notifications;
namespace MediaBrowser.Controller.Notifications
diff --git a/MediaBrowser.Controller/Persistence/IUserRepository.cs b/MediaBrowser.Controller/Persistence/IUserRepository.cs
deleted file mode 100644
index cd23e5223..000000000
--- a/MediaBrowser.Controller/Persistence/IUserRepository.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-using System.Collections.Generic;
-using MediaBrowser.Controller.Entities;
-
-namespace MediaBrowser.Controller.Persistence
-{
- /// <summary>
- /// Provides an interface to implement a User repository
- /// </summary>
- public interface IUserRepository : IRepository
- {
- /// <summary>
- /// Deletes the user.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <returns>Task.</returns>
- void DeleteUser(User user);
-
- /// <summary>
- /// Retrieves all users.
- /// </summary>
- /// <returns>IEnumerable{User}.</returns>
- List<User> RetrieveAllUsers();
-
- void CreateUser(User user);
- void UpdateUser(User user);
- }
-}
diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs
index 3b08e72b9..b1a638883 100644
--- a/MediaBrowser.Controller/Playlists/Playlist.cs
+++ b/MediaBrowser.Controller/Playlists/Playlist.cs
@@ -5,6 +5,7 @@ using System.Linq;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
@@ -241,15 +242,7 @@ namespace MediaBrowser.Controller.Playlists
}
var userId = user.Id.ToString("N", CultureInfo.InvariantCulture);
- foreach (var share in shares)
- {
- if (string.Equals(share.UserId, userId, StringComparison.OrdinalIgnoreCase))
- {
- return true;
- }
- }
-
- return false;
+ return shares.Any(share => string.Equals(share.UserId, userId, StringComparison.OrdinalIgnoreCase));
}
public override bool IsVisibleStandalone(User user)
diff --git a/MediaBrowser.Controller/Providers/IProviderManager.cs b/MediaBrowser.Controller/Providers/IProviderManager.cs
index 254b27460..955db0278 100644
--- a/MediaBrowser.Controller/Providers/IProviderManager.cs
+++ b/MediaBrowser.Controller/Providers/IProviderManager.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
@@ -70,6 +71,8 @@ namespace MediaBrowser.Controller.Providers
/// <returns>Task.</returns>
Task SaveImage(BaseItem item, string source, string mimeType, ImageType type, int? imageIndex, bool? saveLocallyWithMedia, CancellationToken cancellationToken);
+ Task SaveImage(User user, Stream source, string mimeType, string path);
+
/// <summary>
/// Adds the metadata providers.
/// </summary>
diff --git a/MediaBrowser.Controller/Session/ISessionManager.cs b/MediaBrowser.Controller/Session/ISessionManager.cs
index 771027103..1fdb588eb 100644
--- a/MediaBrowser.Controller/Session/ISessionManager.cs
+++ b/MediaBrowser.Controller/Session/ISessionManager.cs
@@ -3,12 +3,12 @@ using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Authentication;
-using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Security;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Session;
+using MediaBrowser.Model.SyncPlay;
namespace MediaBrowser.Controller.Session
{
@@ -74,7 +74,7 @@ namespace MediaBrowser.Controller.Session
/// <param name="deviceName">Name of the device.</param>
/// <param name="remoteEndPoint">The remote end point.</param>
/// <param name="user">The user.</param>
- SessionInfo LogSessionActivity(string appName, string appVersion, string deviceId, string deviceName, string remoteEndPoint, User user);
+ SessionInfo LogSessionActivity(string appName, string appVersion, string deviceId, string deviceName, string remoteEndPoint, Jellyfin.Data.Entities.User user);
void UpdateDeviceName(string sessionId, string reportedDeviceName);
@@ -141,6 +141,24 @@ namespace MediaBrowser.Controller.Session
Task SendPlayCommand(string controllingSessionId, string sessionId, PlayRequest command, CancellationToken cancellationToken);
/// <summary>
+ /// Sends the SyncPlayCommand.
+ /// </summary>
+ /// <param name="sessionId">The session id.</param>
+ /// <param name="command">The command.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ Task SendSyncPlayCommand(string sessionId, SendCommand command, CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Sends the SyncPlayGroupUpdate.
+ /// </summary>
+ /// <param name="sessionId">The session id.</param>
+ /// <param name="command">The group update.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ Task SendSyncPlayGroupUpdate<T>(string sessionId, GroupUpdate<T> command, CancellationToken cancellationToken);
+
+ /// <summary>
/// Sends the browse command.
/// </summary>
/// <param name="controllingSessionId">The controlling session identifier.</param>
diff --git a/MediaBrowser.Controller/Sorting/IUserBaseItemComparer.cs b/MediaBrowser.Controller/Sorting/IUserBaseItemComparer.cs
index 1e2df37bf..6f75d16de 100644
--- a/MediaBrowser.Controller/Sorting/IUserBaseItemComparer.cs
+++ b/MediaBrowser.Controller/Sorting/IUserBaseItemComparer.cs
@@ -1,4 +1,3 @@
-using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
namespace MediaBrowser.Controller.Sorting
@@ -12,7 +11,7 @@ namespace MediaBrowser.Controller.Sorting
/// Gets or sets the user.
/// </summary>
/// <value>The user.</value>
- User User { get; set; }
+ Jellyfin.Data.Entities.User User { get; set; }
/// <summary>
/// Gets or sets the user manager.
diff --git a/MediaBrowser.Controller/SyncPlay/GroupInfo.cs b/MediaBrowser.Controller/SyncPlay/GroupInfo.cs
new file mode 100644
index 000000000..28a3ac505
--- /dev/null
+++ b/MediaBrowser.Controller/SyncPlay/GroupInfo.cs
@@ -0,0 +1,169 @@
+using System;
+using System.Collections.Generic;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Session;
+
+namespace MediaBrowser.Controller.SyncPlay
+{
+ /// <summary>
+ /// Class GroupInfo.
+ /// </summary>
+ /// <remarks>
+ /// Class is not thread-safe, external locking is required when accessing methods.
+ /// </remarks>
+ public class GroupInfo
+ {
+ /// <summary>
+ /// Default ping value used for sessions.
+ /// </summary>
+ public long DefaulPing { get; } = 500;
+
+ /// <summary>
+ /// Gets or sets the group identifier.
+ /// </summary>
+ /// <value>The group identifier.</value>
+ public Guid GroupId { get; } = Guid.NewGuid();
+
+ /// <summary>
+ /// Gets or sets the playing item.
+ /// </summary>
+ /// <value>The playing item.</value>
+ public BaseItem PlayingItem { get; set; }
+
+ /// <summary>
+ /// Gets or sets whether playback is paused.
+ /// </summary>
+ /// <value>Playback is paused.</value>
+ public bool IsPaused { get; set; }
+
+ /// <summary>
+ /// Gets or sets the position ticks.
+ /// </summary>
+ /// <value>The position ticks.</value>
+ public long PositionTicks { get; set; }
+
+ /// <summary>
+ /// Gets or sets the last activity.
+ /// </summary>
+ /// <value>The last activity.</value>
+ public DateTime LastActivity { get; set; }
+
+ /// <summary>
+ /// Gets the participants.
+ /// </summary>
+ /// <value>The participants, or members of the group.</value>
+ public Dictionary<string, GroupMember> Participants { get; } =
+ new Dictionary<string, GroupMember>(StringComparer.OrdinalIgnoreCase);
+
+ /// <summary>
+ /// Checks if a session is in this group.
+ /// </summary>
+ /// <value><c>true</c> if the session is in this group; <c>false</c> otherwise.</value>
+ public bool ContainsSession(string sessionId)
+ {
+ return Participants.ContainsKey(sessionId);
+ }
+
+ /// <summary>
+ /// Adds the session to the group.
+ /// </summary>
+ /// <param name="session">The session.</param>
+ public void AddSession(SessionInfo session)
+ {
+ if (ContainsSession(session.Id.ToString()))
+ {
+ return;
+ }
+
+ var member = new GroupMember();
+ member.Session = session;
+ member.Ping = DefaulPing;
+ member.IsBuffering = false;
+ Participants[session.Id.ToString()] = member;
+ }
+
+ /// <summary>
+ /// Removes the session from the group.
+ /// </summary>
+ /// <param name="session">The session.</param>
+ public void RemoveSession(SessionInfo session)
+ {
+ if (!ContainsSession(session.Id.ToString()))
+ {
+ return;
+ }
+
+ Participants.Remove(session.Id.ToString(), out _);
+ }
+
+ /// <summary>
+ /// Updates the ping of a session.
+ /// </summary>
+ /// <param name="session">The session.</param>
+ /// <param name="ping">The ping.</param>
+ public void UpdatePing(SessionInfo session, long ping)
+ {
+ if (!ContainsSession(session.Id.ToString()))
+ {
+ return;
+ }
+
+ Participants[session.Id.ToString()].Ping = ping;
+ }
+
+ /// <summary>
+ /// Gets the highest ping in the group.
+ /// </summary>
+ /// <value name="session">The highest ping in the group.</value>
+ public long GetHighestPing()
+ {
+ long max = Int64.MinValue;
+ foreach (var session in Participants.Values)
+ {
+ max = Math.Max(max, session.Ping);
+ }
+ return max;
+ }
+
+ /// <summary>
+ /// Sets the session's buffering state.
+ /// </summary>
+ /// <param name="session">The session.</param>
+ /// <param name="isBuffering">The state.</param>
+ public void SetBuffering(SessionInfo session, bool isBuffering)
+ {
+ if (!ContainsSession(session.Id.ToString()))
+ {
+ return;
+ }
+
+ Participants[session.Id.ToString()].IsBuffering = isBuffering;
+ }
+
+ /// <summary>
+ /// Gets the group buffering state.
+ /// </summary>
+ /// <value><c>true</c> if there is a session buffering in the group; <c>false</c> otherwise.</value>
+ public bool IsBuffering()
+ {
+ foreach (var session in Participants.Values)
+ {
+ if (session.IsBuffering)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /// <summary>
+ /// Checks if the group is empty.
+ /// </summary>
+ /// <value><c>true</c> if the group is empty; <c>false</c> otherwise.</value>
+ public bool IsEmpty()
+ {
+ return Participants.Count == 0;
+ }
+ }
+}
diff --git a/MediaBrowser.Controller/SyncPlay/GroupMember.cs b/MediaBrowser.Controller/SyncPlay/GroupMember.cs
new file mode 100644
index 000000000..a3975c334
--- /dev/null
+++ b/MediaBrowser.Controller/SyncPlay/GroupMember.cs
@@ -0,0 +1,28 @@
+using MediaBrowser.Controller.Session;
+
+namespace MediaBrowser.Controller.SyncPlay
+{
+ /// <summary>
+ /// Class GroupMember.
+ /// </summary>
+ public class GroupMember
+ {
+ /// <summary>
+ /// Gets or sets whether this member is buffering.
+ /// </summary>
+ /// <value><c>true</c> if member is buffering; <c>false</c> otherwise.</value>
+ public bool IsBuffering { get; set; }
+
+ /// <summary>
+ /// Gets or sets the session.
+ /// </summary>
+ /// <value>The session.</value>
+ public SessionInfo Session { get; set; }
+
+ /// <summary>
+ /// Gets or sets the ping.
+ /// </summary>
+ /// <value>The ping.</value>
+ public long Ping { get; set; }
+ }
+}
diff --git a/MediaBrowser.Controller/SyncPlay/ISyncPlayController.cs b/MediaBrowser.Controller/SyncPlay/ISyncPlayController.cs
new file mode 100644
index 000000000..de1fcd259
--- /dev/null
+++ b/MediaBrowser.Controller/SyncPlay/ISyncPlayController.cs
@@ -0,0 +1,67 @@
+using System;
+using System.Threading;
+using MediaBrowser.Controller.Session;
+using MediaBrowser.Model.SyncPlay;
+
+namespace MediaBrowser.Controller.SyncPlay
+{
+ /// <summary>
+ /// Interface ISyncPlayController.
+ /// </summary>
+ public interface ISyncPlayController
+ {
+ /// <summary>
+ /// Gets the group id.
+ /// </summary>
+ /// <value>The group id.</value>
+ Guid GetGroupId();
+
+ /// <summary>
+ /// Gets the playing item id.
+ /// </summary>
+ /// <value>The playing item id.</value>
+ Guid GetPlayingItemId();
+
+ /// <summary>
+ /// Checks if the group is empty.
+ /// </summary>
+ /// <value>If the group is empty.</value>
+ bool IsGroupEmpty();
+
+ /// <summary>
+ /// Initializes the group with the session's info.
+ /// </summary>
+ /// <param name="session">The session.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ void InitGroup(SessionInfo session, CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Adds the session to the group.
+ /// </summary>
+ /// <param name="session">The session.</param>
+ /// <param name="request">The request.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ void SessionJoin(SessionInfo session, JoinGroupRequest request, CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Removes the session from the group.
+ /// </summary>
+ /// <param name="session">The session.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ void SessionLeave(SessionInfo session, CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Handles the requested action by the session.
+ /// </summary>
+ /// <param name="session">The session.</param>
+ /// <param name="request">The requested action.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ void HandleRequest(SessionInfo session, PlaybackRequest request, CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Gets the info about the group for the clients.
+ /// </summary>
+ /// <value>The group info for the clients.</value>
+ GroupInfoView GetInfo();
+ }
+} \ No newline at end of file
diff --git a/MediaBrowser.Controller/SyncPlay/ISyncPlayManager.cs b/MediaBrowser.Controller/SyncPlay/ISyncPlayManager.cs
new file mode 100644
index 000000000..006fb687b
--- /dev/null
+++ b/MediaBrowser.Controller/SyncPlay/ISyncPlayManager.cs
@@ -0,0 +1,69 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using MediaBrowser.Controller.Session;
+using MediaBrowser.Model.SyncPlay;
+
+namespace MediaBrowser.Controller.SyncPlay
+{
+ /// <summary>
+ /// Interface ISyncPlayManager.
+ /// </summary>
+ public interface ISyncPlayManager
+ {
+ /// <summary>
+ /// Creates a new group.
+ /// </summary>
+ /// <param name="session">The session that's creating the group.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ void NewGroup(SessionInfo session, CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Adds the session to a group.
+ /// </summary>
+ /// <param name="session">The session.</param>
+ /// <param name="groupId">The group id.</param>
+ /// <param name="request">The request.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ void JoinGroup(SessionInfo session, Guid groupId, JoinGroupRequest request, CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Removes the session from a group.
+ /// </summary>
+ /// <param name="session">The session.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ void LeaveGroup(SessionInfo session, CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Gets list of available groups for a session.
+ /// </summary>
+ /// <param name="session">The session.</param>
+ /// <param name="filterItemId">The item id to filter by.</param>
+ /// <value>The list of available groups.</value>
+ List<GroupInfoView> ListGroups(SessionInfo session, Guid filterItemId);
+
+ /// <summary>
+ /// Handle a request by a session in a group.
+ /// </summary>
+ /// <param name="session">The session.</param>
+ /// <param name="request">The request.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ void HandleRequest(SessionInfo session, PlaybackRequest request, CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Maps a session to a group.
+ /// </summary>
+ /// <param name="session">The session.</param>
+ /// <param name="group">The group.</param>
+ /// <exception cref="InvalidOperationException"></exception>
+ void AddSessionToGroup(SessionInfo session, ISyncPlayController group);
+
+ /// <summary>
+ /// Unmaps a session from a group.
+ /// </summary>
+ /// <param name="session">The session.</param>
+ /// <param name="group">The group.</param>
+ /// <exception cref="InvalidOperationException"></exception>
+ void RemoveSessionFromGroup(SessionInfo session, ISyncPlayController group);
+ }
+}
diff --git a/MediaBrowser.LocalMetadata/Images/InternalMetadataFolderImageProvider.cs b/MediaBrowser.LocalMetadata/Images/InternalMetadataFolderImageProvider.cs
index 795933ce9..5137e4c68 100644
--- a/MediaBrowser.LocalMetadata/Images/InternalMetadataFolderImageProvider.cs
+++ b/MediaBrowser.LocalMetadata/Images/InternalMetadataFolderImageProvider.cs
@@ -13,7 +13,7 @@ namespace MediaBrowser.LocalMetadata.Images
{
private readonly IServerConfigurationManager _config;
private readonly IFileSystem _fileSystem;
- private readonly ILogger _logger;
+ private readonly ILogger<InternalMetadataFolderImageProvider> _logger;
public InternalMetadataFolderImageProvider(
IServerConfigurationManager config,
diff --git a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs
index d4b98182f..4f1112396 100644
--- a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs
+++ b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs
@@ -23,7 +23,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
/// <summary>
/// The logger
/// </summary>
- protected ILogger Logger { get; private set; }
+ protected ILogger<BaseItemXmlParser<T>> Logger { get; private set; }
protected IProviderManager ProviderManager { get; private set; }
private Dictionary<string, string> _validProviderIds;
@@ -32,7 +32,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
/// Initializes a new instance of the <see cref="BaseItemXmlParser{T}" /> class.
/// </summary>
/// <param name="logger">The logger.</param>
- public BaseItemXmlParser(ILogger logger, IProviderManager providerManager)
+ public BaseItemXmlParser(ILogger<BaseItemXmlParser<T>> logger, IProviderManager providerManager)
{
Logger = logger;
ProviderManager = providerManager;
@@ -249,9 +249,9 @@ namespace MediaBrowser.LocalMetadata.Parsers
{
item.LockedFields = val.Split('|').Select(i =>
{
- if (Enum.TryParse(i, true, out MetadataFields field))
+ if (Enum.TryParse(i, true, out MetadataField field))
{
- return (MetadataFields?)field;
+ return (MetadataField?)field;
}
return null;
@@ -543,7 +543,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
var tmdbCollection = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(tmdbCollection))
{
- item.SetProviderId(MetadataProviders.TmdbCollection, tmdbCollection);
+ item.SetProviderId(MetadataProvider.TmdbCollection, tmdbCollection);
}
break;
diff --git a/MediaBrowser.LocalMetadata/Parsers/BoxSetXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/BoxSetXmlParser.cs
index 127334625..ca11a079d 100644
--- a/MediaBrowser.LocalMetadata/Parsers/BoxSetXmlParser.cs
+++ b/MediaBrowser.LocalMetadata/Parsers/BoxSetXmlParser.cs
@@ -85,7 +85,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
item.Item.LinkedChildren = list.ToArray();
}
- public BoxSetXmlParser(ILogger logger, IProviderManager providerManager)
+ public BoxSetXmlParser(ILogger<BoxSetXmlParser> logger, IProviderManager providerManager)
: base(logger, providerManager)
{
}
diff --git a/MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs
index 5608a0be9..54710cd82 100644
--- a/MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs
+++ b/MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs
@@ -93,7 +93,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
item.LinkedChildren = list.ToArray();
}
- public PlaylistXmlParser(ILogger logger, IProviderManager providerManager)
+ public PlaylistXmlParser(ILogger<PlaylistXmlParser> logger, IProviderManager providerManager)
: base(logger, providerManager)
{
}
diff --git a/MediaBrowser.LocalMetadata/Providers/BoxSetXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/BoxSetXmlProvider.cs
index b2e3bc9e2..2d115a591 100644
--- a/MediaBrowser.LocalMetadata/Providers/BoxSetXmlProvider.cs
+++ b/MediaBrowser.LocalMetadata/Providers/BoxSetXmlProvider.cs
@@ -13,10 +13,10 @@ namespace MediaBrowser.LocalMetadata.Providers
/// </summary>
public class BoxSetXmlProvider : BaseXmlProvider<BoxSet>
{
- private readonly ILogger _logger;
+ private readonly ILogger<BoxSetXmlParser> _logger;
private readonly IProviderManager _providerManager;
- public BoxSetXmlProvider(IFileSystem fileSystem, ILogger<BoxSetXmlProvider> logger, IProviderManager providerManager)
+ public BoxSetXmlProvider(IFileSystem fileSystem, ILogger<BoxSetXmlParser> logger, IProviderManager providerManager)
: base(fileSystem)
{
_logger = logger;
diff --git a/MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs
index df8107bad..d4e2bc8e5 100644
--- a/MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs
+++ b/MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs
@@ -10,12 +10,12 @@ namespace MediaBrowser.LocalMetadata.Providers
{
public class PlaylistXmlProvider : BaseXmlProvider<Playlist>
{
- private readonly ILogger _logger;
+ private readonly ILogger<PlaylistXmlParser> _logger;
private readonly IProviderManager _providerManager;
public PlaylistXmlProvider(
IFileSystem fileSystem,
- ILogger<PlaylistXmlProvider> logger,
+ ILogger<PlaylistXmlParser> logger,
IProviderManager providerManager)
: base(fileSystem)
{
diff --git a/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs
index ba1d850e3..071902393 100644
--- a/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs
+++ b/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs
@@ -21,7 +21,7 @@ namespace MediaBrowser.LocalMetadata.Savers
{
private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
- public BaseXmlSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger)
+ public BaseXmlSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger<BaseXmlSaver> logger)
{
FileSystem = fileSystem;
ConfigurationManager = configurationManager;
@@ -36,7 +36,7 @@ namespace MediaBrowser.LocalMetadata.Savers
protected ILibraryManager LibraryManager { get; private set; }
protected IUserManager UserManager { get; private set; }
protected IUserDataManager UserDataManager { get; private set; }
- protected ILogger Logger { get; private set; }
+ protected ILogger<BaseXmlSaver> Logger { get; private set; }
public string Name => XmlProviderUtils.Name;
diff --git a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs
index 3f177a9fa..a82f2108f 100644
--- a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs
+++ b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs
@@ -21,7 +21,7 @@ namespace MediaBrowser.MediaEncoding.Attachments
{
public class AttachmentExtractor : IAttachmentExtractor, IDisposable
{
- private readonly ILogger _logger;
+ private readonly ILogger<AttachmentExtractor> _logger;
private readonly IApplicationPaths _appPaths;
private readonly IFileSystem _fileSystem;
private readonly IMediaEncoder _mediaEncoder;
diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
index 1377502dd..26bd202ba 100644
--- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
@@ -34,7 +34,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
/// </summary>
internal const int DefaultImageExtractionTimeout = 5000;
- private readonly ILogger _logger;
+ private readonly ILogger<MediaEncoder> _logger;
private readonly IServerConfigurationManager _configurationManager;
private readonly IFileSystem _fileSystem;
private readonly ILocalizationManager _localization;
diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
index af8bee301..aeb4dbe73 100644
--- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
+++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
@@ -23,7 +23,7 @@
<ItemGroup>
<PackageReference Include="BDInfo" Version="0.7.6.1" />
- <PackageReference Include="System.Text.Encoding.CodePages" Version="4.7.0" />
+ <PackageReference Include="System.Text.Encoding.CodePages" Version="4.7.1" />
<PackageReference Include="UTF.Unknown" Version="2.3.0" />
</ItemGroup>
diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
index d3f8094b9..9386d6524 100644
--- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
+++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
@@ -1057,23 +1057,23 @@ namespace MediaBrowser.MediaEncoding.Probing
// 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"));
- audio.SetProviderId(MetadataProviders.MusicBrainzAlbumArtist, mb);
+ audio.SetProviderId(MetadataProvider.MusicBrainzAlbumArtist, mb);
mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Artist Id"));
if (mb == null) mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ARTISTID"));
- audio.SetProviderId(MetadataProviders.MusicBrainzArtist, mb);
+ audio.SetProviderId(MetadataProvider.MusicBrainzArtist, mb);
mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Album Id"));
if (mb == null) mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ALBUMID"));
- audio.SetProviderId(MetadataProviders.MusicBrainzAlbum, mb);
+ audio.SetProviderId(MetadataProvider.MusicBrainzAlbum, mb);
mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Release Group Id"));
if (mb == null) mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_RELEASEGROUPID"));
- audio.SetProviderId(MetadataProviders.MusicBrainzReleaseGroup, mb);
+ audio.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, mb);
mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Release Track Id"));
if (mb == null) mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_RELEASETRACKID"));
- audio.SetProviderId(MetadataProviders.MusicBrainzTrack, mb);
+ audio.SetProviderId(MetadataProvider.MusicBrainzTrack, mb);
}
private string GetMultipleMusicBrainzId(string value)
diff --git a/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs b/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs
index 293cf5ea5..f44cf1452 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs
@@ -33,10 +33,12 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
continue;
}
+
if (line.StartsWith("["))
+ {
break;
- if (string.IsNullOrEmpty(line))
- continue;
+ }
+
var subEvent = new SubtitleTrackEvent { Id = eventIndex.ToString(_usCulture) };
eventIndex++;
var sections = line.Substring(10).Split(',');
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
index ba171295e..f08af6045 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
@@ -25,7 +25,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
public class SubtitleEncoder : ISubtitleEncoder
{
private readonly ILibraryManager _libraryManager;
- private readonly ILogger _logger;
+ private readonly ILogger<SubtitleEncoder> _logger;
private readonly IApplicationPaths _appPaths;
private readonly IFileSystem _fileSystem;
private readonly IMediaEncoder _mediaEncoder;
diff --git a/MediaBrowser.Model/Activity/ActivityLogEntry.cs b/MediaBrowser.Model/Activity/ActivityLogEntry.cs
index 5ab904394..1d47ef9f6 100644
--- a/MediaBrowser.Model/Activity/ActivityLogEntry.cs
+++ b/MediaBrowser.Model/Activity/ActivityLogEntry.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/ApiClient/ServerDiscoveryInfo.cs b/MediaBrowser.Model/ApiClient/ServerDiscoveryInfo.cs
index bb203f895..fcc90a1f7 100644
--- a/MediaBrowser.Model/ApiClient/ServerDiscoveryInfo.cs
+++ b/MediaBrowser.Model/ApiClient/ServerDiscoveryInfo.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
namespace MediaBrowser.Model.ApiClient
diff --git a/MediaBrowser.Model/Branding/BrandingOptions.cs b/MediaBrowser.Model/Branding/BrandingOptions.cs
index 8ab268a64..5ddf1e7e6 100644
--- a/MediaBrowser.Model/Branding/BrandingOptions.cs
+++ b/MediaBrowser.Model/Branding/BrandingOptions.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
namespace MediaBrowser.Model.Branding
diff --git a/MediaBrowser.Model/Channels/ChannelFeatures.cs b/MediaBrowser.Model/Channels/ChannelFeatures.cs
index c4e97ffe5..496102d83 100644
--- a/MediaBrowser.Model/Channels/ChannelFeatures.cs
+++ b/MediaBrowser.Model/Channels/ChannelFeatures.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/Channels/ChannelInfo.cs b/MediaBrowser.Model/Channels/ChannelInfo.cs
index bfb34db55..f2432aaeb 100644
--- a/MediaBrowser.Model/Channels/ChannelInfo.cs
+++ b/MediaBrowser.Model/Channels/ChannelInfo.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
namespace MediaBrowser.Model.Channels
diff --git a/MediaBrowser.Model/Channels/ChannelQuery.cs b/MediaBrowser.Model/Channels/ChannelQuery.cs
index 88fc94a6f..d11260039 100644
--- a/MediaBrowser.Model/Channels/ChannelQuery.cs
+++ b/MediaBrowser.Model/Channels/ChannelQuery.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
@@ -13,8 +14,11 @@ namespace MediaBrowser.Model.Channels
/// </summary>
/// <value>The fields.</value>
public ItemFields[] Fields { get; set; }
+
public bool? EnableImages { get; set; }
+
public int? ImageTypeLimit { get; set; }
+
public ImageType[] EnableImageTypes { get; set; }
/// <summary>
@@ -48,7 +52,9 @@ namespace MediaBrowser.Model.Channels
/// </summary>
/// <value><c>null</c> if [is favorite] contains no value, <c>true</c> if [is favorite]; otherwise, <c>false</c>.</value>
public bool? IsFavorite { get; set; }
+
public bool? IsRecordingsFolder { get; set; }
+
public bool RefreshLatestChannelItems { get; set; }
}
}
diff --git a/MediaBrowser.Model/Configuration/AccessSchedule.cs b/MediaBrowser.Model/Configuration/AccessSchedule.cs
index 120c47dbc..7bd355449 100644
--- a/MediaBrowser.Model/Configuration/AccessSchedule.cs
+++ b/MediaBrowser.Model/Configuration/AccessSchedule.cs
@@ -1,3 +1,5 @@
+using Jellyfin.Data.Enums;
+
#pragma warning disable CS1591
namespace MediaBrowser.Model.Configuration
diff --git a/MediaBrowser.Model/Configuration/BaseApplicationConfiguration.cs b/MediaBrowser.Model/Configuration/BaseApplicationConfiguration.cs
index cc2541f74..cdd322c94 100644
--- a/MediaBrowser.Model/Configuration/BaseApplicationConfiguration.cs
+++ b/MediaBrowser.Model/Configuration/BaseApplicationConfiguration.cs
@@ -1,3 +1,4 @@
+#nullable disable
using System;
using System.Xml.Serialization;
diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs
index 648568fd7..0c0e01f11 100644
--- a/MediaBrowser.Model/Configuration/EncodingOptions.cs
+++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
namespace MediaBrowser.Model.Configuration
@@ -5,10 +6,15 @@ namespace MediaBrowser.Model.Configuration
public class EncodingOptions
{
public int EncodingThreadCount { get; set; }
+
public string TranscodingTempPath { get; set; }
+
public double DownMixAudioBoost { get; set; }
+
public bool EnableThrottling { get; set; }
+
public int ThrottleDelaySeconds { get; set; }
+
public string HardwareAccelerationType { get; set; }
/// <summary>
@@ -20,12 +26,19 @@ namespace MediaBrowser.Model.Configuration
/// The current FFmpeg path being used by the system and displayed on the transcode page.
/// </summary>
public string EncoderAppPathDisplay { get; set; }
+
public string VaapiDevice { get; set; }
+
public int H264Crf { get; set; }
+
public int H265Crf { get; set; }
+
public string EncoderPreset { get; set; }
+
public string DeinterlaceMethod { get; set; }
+
public bool EnableHardwareEncoding { get; set; }
+
public bool EnableSubtitleExtraction { get; set; }
public string[] HardwareDecodingCodecs { get; set; }
diff --git a/MediaBrowser.Model/Configuration/LibraryOptions.cs b/MediaBrowser.Model/Configuration/LibraryOptions.cs
index 4342ccd8a..4229a4335 100644
--- a/MediaBrowser.Model/Configuration/LibraryOptions.cs
+++ b/MediaBrowser.Model/Configuration/LibraryOptions.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/Configuration/MetadataOptions.cs b/MediaBrowser.Model/Configuration/MetadataOptions.cs
index 625054b9e..e7dc3da3c 100644
--- a/MediaBrowser.Model/Configuration/MetadataOptions.cs
+++ b/MediaBrowser.Model/Configuration/MetadataOptions.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
@@ -12,12 +13,15 @@ namespace MediaBrowser.Model.Configuration
public string ItemType { get; set; }
public string[] DisabledMetadataSavers { get; set; }
+
public string[] LocalMetadataReaderOrder { get; set; }
public string[] DisabledMetadataFetchers { get; set; }
+
public string[] MetadataFetcherOrder { get; set; }
public string[] DisabledImageFetchers { get; set; }
+
public string[] ImageFetcherOrder { get; set; }
public MetadataOptions()
diff --git a/MediaBrowser.Model/Configuration/MetadataPlugin.cs b/MediaBrowser.Model/Configuration/MetadataPlugin.cs
index c2b47eb9b..db8cd1875 100644
--- a/MediaBrowser.Model/Configuration/MetadataPlugin.cs
+++ b/MediaBrowser.Model/Configuration/MetadataPlugin.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
namespace MediaBrowser.Model.Configuration
diff --git a/MediaBrowser.Model/Configuration/MetadataPluginSummary.cs b/MediaBrowser.Model/Configuration/MetadataPluginSummary.cs
index 53063810b..0c197ee02 100644
--- a/MediaBrowser.Model/Configuration/MetadataPluginSummary.cs
+++ b/MediaBrowser.Model/Configuration/MetadataPluginSummary.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
index 1f5981f10..afbe02dd3 100644
--- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs
+++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
@@ -240,11 +241,13 @@ namespace MediaBrowser.Model.Configuration
public bool EnableNewOmdbSupport { get; set; }
public string[] RemoteIPFilter { get; set; }
+
public bool IsRemoteIPFilterBlacklist { get; set; }
public int ImageExtractionTimeoutMs { get; set; }
public PathSubstitution[] PathSubstitutions { get; set; }
+
public bool EnableSimpleArtistDetection { get; set; }
public string[] UninstalledPlugins { get; set; }
@@ -313,24 +316,24 @@ namespace MediaBrowser.Model.Configuration
new MetadataOptions
{
ItemType = "MusicVideo",
- DisabledMetadataFetchers = new [] { "The Open Movie Database" },
- DisabledImageFetchers = new [] { "The Open Movie Database" }
+ DisabledMetadataFetchers = new[] { "The Open Movie Database" },
+ DisabledImageFetchers = new[] { "The Open Movie Database" }
},
new MetadataOptions
{
ItemType = "Series",
- DisabledMetadataFetchers = new [] { "TheMovieDb" },
- DisabledImageFetchers = new [] { "TheMovieDb" }
+ DisabledMetadataFetchers = new[] { "TheMovieDb" },
+ DisabledImageFetchers = new[] { "TheMovieDb" }
},
new MetadataOptions
{
ItemType = "MusicAlbum",
- DisabledMetadataFetchers = new [] { "TheAudioDB" }
+ DisabledMetadataFetchers = new[] { "TheAudioDB" }
},
new MetadataOptions
{
ItemType = "MusicArtist",
- DisabledMetadataFetchers = new [] { "TheAudioDB" }
+ DisabledMetadataFetchers = new[] { "TheAudioDB" }
},
new MetadataOptions
{
@@ -339,13 +342,13 @@ namespace MediaBrowser.Model.Configuration
new MetadataOptions
{
ItemType = "Season",
- DisabledMetadataFetchers = new [] { "TheMovieDb" },
+ DisabledMetadataFetchers = new[] { "TheMovieDb" },
},
new MetadataOptions
{
ItemType = "Episode",
- DisabledMetadataFetchers = new [] { "The Open Movie Database", "TheMovieDb" },
- DisabledImageFetchers = new [] { "The Open Movie Database", "TheMovieDb" }
+ DisabledMetadataFetchers = new[] { "The Open Movie Database", "TheMovieDb" },
+ DisabledImageFetchers = new[] { "The Open Movie Database", "TheMovieDb" }
}
};
}
@@ -354,6 +357,7 @@ namespace MediaBrowser.Model.Configuration
public class PathSubstitution
{
public string From { get; set; }
+
public string To { get; set; }
}
}
diff --git a/MediaBrowser.Model/Configuration/UserConfiguration.cs b/MediaBrowser.Model/Configuration/UserConfiguration.cs
index a475c9910..85d864eec 100644
--- a/MediaBrowser.Model/Configuration/UserConfiguration.cs
+++ b/MediaBrowser.Model/Configuration/UserConfiguration.cs
@@ -1,6 +1,8 @@
+#nullable disable
#pragma warning disable CS1591
using System;
+using Jellyfin.Data.Enums;
namespace MediaBrowser.Model.Configuration
{
diff --git a/MediaBrowser.Model/Configuration/XbmcMetadataOptions.cs b/MediaBrowser.Model/Configuration/XbmcMetadataOptions.cs
index d6c1295f4..c48a38192 100644
--- a/MediaBrowser.Model/Configuration/XbmcMetadataOptions.cs
+++ b/MediaBrowser.Model/Configuration/XbmcMetadataOptions.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
namespace MediaBrowser.Model.Configuration
diff --git a/MediaBrowser.Model/Cryptography/ICryptoProvider.cs b/MediaBrowser.Model/Cryptography/ICryptoProvider.cs
index 656c04f46..d8b7d848a 100644
--- a/MediaBrowser.Model/Cryptography/ICryptoProvider.cs
+++ b/MediaBrowser.Model/Cryptography/ICryptoProvider.cs
@@ -10,7 +10,7 @@ namespace MediaBrowser.Model.Cryptography
IEnumerable<string> GetSupportedHashMethods();
- byte[] ComputeHash(string HashMethod, byte[] bytes, byte[] salt);
+ byte[] ComputeHash(string hashMethod, byte[] bytes, byte[] salt);
byte[] ComputeHashWithDefaultMethod(byte[] bytes, byte[] salt);
diff --git a/MediaBrowser.Model/Devices/ContentUploadHistory.cs b/MediaBrowser.Model/Devices/ContentUploadHistory.cs
deleted file mode 100644
index c493760d5..000000000
--- a/MediaBrowser.Model/Devices/ContentUploadHistory.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-#pragma warning disable CS1591
-
-namespace MediaBrowser.Model.Devices
-{
- public class ContentUploadHistory
- {
- public string DeviceId { get; set; }
- public LocalFileInfo[] FilesUploaded { get; set; }
-
- public ContentUploadHistory()
- {
- FilesUploaded = new LocalFileInfo[] { };
- }
- }
-}
diff --git a/MediaBrowser.Model/Devices/DeviceInfo.cs b/MediaBrowser.Model/Devices/DeviceInfo.cs
index d2563d1d0..0cccf931c 100644
--- a/MediaBrowser.Model/Devices/DeviceInfo.cs
+++ b/MediaBrowser.Model/Devices/DeviceInfo.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/Devices/DeviceOptions.cs b/MediaBrowser.Model/Devices/DeviceOptions.cs
index 8b77fd7fc..037ffeb5e 100644
--- a/MediaBrowser.Model/Devices/DeviceOptions.cs
+++ b/MediaBrowser.Model/Devices/DeviceOptions.cs
@@ -4,6 +4,6 @@ namespace MediaBrowser.Model.Devices
{
public class DeviceOptions
{
- public string CustomName { get; set; }
+ public string? CustomName { get; set; }
}
}
diff --git a/MediaBrowser.Model/Devices/LocalFileInfo.cs b/MediaBrowser.Model/Devices/LocalFileInfo.cs
deleted file mode 100644
index 63a8dc2aa..000000000
--- a/MediaBrowser.Model/Devices/LocalFileInfo.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-#pragma warning disable CS1591
-
-namespace MediaBrowser.Model.Devices
-{
- public class LocalFileInfo
- {
- public string Name { get; set; }
- public string Id { get; set; }
- public string Album { get; set; }
- public string MimeType { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Dlna/AudioOptions.cs b/MediaBrowser.Model/Dlna/AudioOptions.cs
index 40081b282..fc555c5f7 100644
--- a/MediaBrowser.Model/Dlna/AudioOptions.cs
+++ b/MediaBrowser.Model/Dlna/AudioOptions.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
@@ -19,12 +20,17 @@ namespace MediaBrowser.Model.Dlna
}
public bool EnableDirectPlay { get; set; }
+
public bool EnableDirectStream { get; set; }
+
public bool ForceDirectPlay { get; set; }
+
public bool ForceDirectStream { get; set; }
public Guid ItemId { get; set; }
+
public MediaSourceInfo[] MediaSources { get; set; }
+
public DeviceProfile Profile { get; set; }
/// <summary>
diff --git a/MediaBrowser.Model/Dlna/CodecProfile.cs b/MediaBrowser.Model/Dlna/CodecProfile.cs
index 7bb961deb..d4fd3e673 100644
--- a/MediaBrowser.Model/Dlna/CodecProfile.cs
+++ b/MediaBrowser.Model/Dlna/CodecProfile.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/Dlna/ConditionProcessor.cs b/MediaBrowser.Model/Dlna/ConditionProcessor.cs
index 0c3bd8882..faf1ee41b 100644
--- a/MediaBrowser.Model/Dlna/ConditionProcessor.cs
+++ b/MediaBrowser.Model/Dlna/ConditionProcessor.cs
@@ -15,7 +15,7 @@ namespace MediaBrowser.Model.Dlna
int? height,
int? videoBitDepth,
int? videoBitrate,
- string videoProfile,
+ string? videoProfile,
double? videoLevel,
float? videoFramerate,
int? packetLength,
@@ -25,7 +25,7 @@ namespace MediaBrowser.Model.Dlna
int? refFrames,
int? numVideoStreams,
int? numAudioStreams,
- string videoCodecTag,
+ string? videoCodecTag,
bool? isAvc)
{
switch (condition.Property)
@@ -103,7 +103,7 @@ namespace MediaBrowser.Model.Dlna
int? audioBitrate,
int? audioSampleRate,
int? audioBitDepth,
- string audioProfile,
+ string? audioProfile,
bool? isSecondaryTrack)
{
switch (condition.Property)
@@ -154,7 +154,7 @@ namespace MediaBrowser.Model.Dlna
return false;
}
- private static bool IsConditionSatisfied(ProfileCondition condition, string currentValue)
+ private static bool IsConditionSatisfied(ProfileCondition condition, string? currentValue)
{
if (string.IsNullOrEmpty(currentValue))
{
@@ -201,34 +201,6 @@ namespace MediaBrowser.Model.Dlna
return false;
}
- private static bool IsConditionSatisfied(ProfileCondition condition, float currentValue)
- {
- if (currentValue <= 0)
- {
- // If the value is unknown, it satisfies if not marked as required
- return !condition.IsRequired;
- }
-
- if (float.TryParse(condition.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out var expected))
- {
- switch (condition.Condition)
- {
- case ProfileConditionType.Equals:
- return currentValue.Equals(expected);
- case ProfileConditionType.GreaterThanEqual:
- return currentValue >= expected;
- case ProfileConditionType.LessThanEqual:
- return currentValue <= expected;
- case ProfileConditionType.NotEquals:
- return !currentValue.Equals(expected);
- default:
- throw new InvalidOperationException("Unexpected ProfileConditionType: " + condition.Condition);
- }
- }
-
- return false;
- }
-
private static bool IsConditionSatisfied(ProfileCondition condition, double? currentValue)
{
if (!currentValue.HasValue)
diff --git a/MediaBrowser.Model/Dlna/ContainerProfile.cs b/MediaBrowser.Model/Dlna/ContainerProfile.cs
index cc2417a70..f77d9b267 100644
--- a/MediaBrowser.Model/Dlna/ContainerProfile.cs
+++ b/MediaBrowser.Model/Dlna/ContainerProfile.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
@@ -10,6 +11,7 @@ namespace MediaBrowser.Model.Dlna
{
[XmlAttribute("type")]
public DlnaProfileType Type { get; set; }
+
public ProfileCondition[] Conditions { get; set; }
[XmlAttribute("container")]
diff --git a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs
index a20f11503..b055ad41a 100644
--- a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs
+++ b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
@@ -32,7 +33,10 @@ namespace MediaBrowser.Model.Dlna
DlnaFlags.InteractiveTransferMode |
DlnaFlags.DlnaV15;
- string dlnaflags = string.Format(";DLNA.ORG_FLAGS={0}", DlnaMaps.FlagsToString(flagValue));
+ string dlnaflags = string.Format(
+ CultureInfo.InvariantCulture,
+ ";DLNA.ORG_FLAGS={0}",
+ DlnaMaps.FlagsToString(flagValue));
ResponseProfile mediaProfile = _profile.GetImageMediaProfile(container,
width,
diff --git a/MediaBrowser.Model/Dlna/DeviceIdentification.cs b/MediaBrowser.Model/Dlna/DeviceIdentification.cs
index f1699d930..85cc9e3c1 100644
--- a/MediaBrowser.Model/Dlna/DeviceIdentification.cs
+++ b/MediaBrowser.Model/Dlna/DeviceIdentification.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/Dlna/DeviceProfile.cs b/MediaBrowser.Model/Dlna/DeviceProfile.cs
index 3813ac5eb..6462ffdc1 100644
--- a/MediaBrowser.Model/Dlna/DeviceProfile.cs
+++ b/MediaBrowser.Model/Dlna/DeviceProfile.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/Dlna/DeviceProfileInfo.cs b/MediaBrowser.Model/Dlna/DeviceProfileInfo.cs
index 347583965..74c32c523 100644
--- a/MediaBrowser.Model/Dlna/DeviceProfileInfo.cs
+++ b/MediaBrowser.Model/Dlna/DeviceProfileInfo.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
namespace MediaBrowser.Model.Dlna
diff --git a/MediaBrowser.Model/Dlna/DirectPlayProfile.cs b/MediaBrowser.Model/Dlna/DirectPlayProfile.cs
index b43f8633e..88cb83991 100644
--- a/MediaBrowser.Model/Dlna/DirectPlayProfile.cs
+++ b/MediaBrowser.Model/Dlna/DirectPlayProfile.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System.Xml.Serialization;
diff --git a/MediaBrowser.Model/Dlna/HttpHeaderInfo.cs b/MediaBrowser.Model/Dlna/HttpHeaderInfo.cs
index f23a24084..17c4dffcc 100644
--- a/MediaBrowser.Model/Dlna/HttpHeaderInfo.cs
+++ b/MediaBrowser.Model/Dlna/HttpHeaderInfo.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System.Xml.Serialization;
diff --git a/MediaBrowser.Model/Dlna/ITranscoderSupport.cs b/MediaBrowser.Model/Dlna/ITranscoderSupport.cs
index 7e35cc85b..d9bd094d9 100644
--- a/MediaBrowser.Model/Dlna/ITranscoderSupport.cs
+++ b/MediaBrowser.Model/Dlna/ITranscoderSupport.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
namespace MediaBrowser.Model.Dlna
@@ -5,7 +6,9 @@ namespace MediaBrowser.Model.Dlna
public interface ITranscoderSupport
{
bool CanEncodeToAudioCodec(string codec);
+
bool CanEncodeToSubtitleCodec(string codec);
+
bool CanExtractSubtitles(string codec);
}
@@ -15,10 +18,12 @@ namespace MediaBrowser.Model.Dlna
{
return true;
}
+
public bool CanEncodeToSubtitleCodec(string codec)
{
return true;
}
+
public bool CanExtractSubtitles(string codec)
{
return true;
diff --git a/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs b/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs
index 4cd318abb..10e9179c0 100644
--- a/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs
+++ b/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
@@ -21,13 +22,13 @@ namespace MediaBrowser.Model.Dlna
if (string.Equals(container, "asf", StringComparison.OrdinalIgnoreCase))
{
MediaFormatProfile? val = ResolveVideoASFFormat(videoCodec, audioCodec, width, height);
- return val.HasValue ? new MediaFormatProfile[] { val.Value } : new MediaFormatProfile[] { };
+ return val.HasValue ? new MediaFormatProfile[] { val.Value } : Array.Empty<MediaFormatProfile>();
}
if (string.Equals(container, "mp4", StringComparison.OrdinalIgnoreCase))
{
MediaFormatProfile? val = ResolveVideoMP4Format(videoCodec, audioCodec, width, height);
- return val.HasValue ? new MediaFormatProfile[] { val.Value } : new MediaFormatProfile[] { };
+ return val.HasValue ? new MediaFormatProfile[] { val.Value } : Array.Empty<MediaFormatProfile>();
}
if (string.Equals(container, "avi", StringComparison.OrdinalIgnoreCase))
@@ -61,18 +62,18 @@ namespace MediaBrowser.Model.Dlna
if (string.Equals(container, "3gp", StringComparison.OrdinalIgnoreCase))
{
MediaFormatProfile? val = ResolveVideo3GPFormat(videoCodec, audioCodec);
- return val.HasValue ? new MediaFormatProfile[] { val.Value } : new MediaFormatProfile[] { };
+ return val.HasValue ? new MediaFormatProfile[] { val.Value } : Array.Empty<MediaFormatProfile>();
}
if (string.Equals(container, "ogv", StringComparison.OrdinalIgnoreCase) || string.Equals(container, "ogg", StringComparison.OrdinalIgnoreCase))
return new MediaFormatProfile[] { MediaFormatProfile.OGV };
- return new MediaFormatProfile[] { };
+ return Array.Empty<MediaFormatProfile>();
}
private MediaFormatProfile[] ResolveVideoMPEG2TSFormat(string videoCodec, string audioCodec, int? width, int? height, TransportStreamTimestamp timestampType)
{
- string suffix = "";
+ string suffix = string.Empty;
switch (timestampType)
{
@@ -92,16 +93,18 @@ namespace MediaBrowser.Model.Dlna
if (string.Equals(videoCodec, "mpeg2video", StringComparison.OrdinalIgnoreCase))
{
- var list = new List<MediaFormatProfile>();
-
- list.Add(ValueOf("MPEG_TS_SD_NA" + suffix));
- list.Add(ValueOf("MPEG_TS_SD_EU" + suffix));
- list.Add(ValueOf("MPEG_TS_SD_KO" + suffix));
+ var list = new List<MediaFormatProfile>
+ {
+ ValueOf("MPEG_TS_SD_NA" + suffix),
+ ValueOf("MPEG_TS_SD_EU" + suffix),
+ ValueOf("MPEG_TS_SD_KO" + suffix)
+ };
if ((timestampType == TransportStreamTimestamp.Valid) && string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase))
{
list.Add(MediaFormatProfile.MPEG_TS_JP_T);
}
+
return list.ToArray();
}
if (string.Equals(videoCodec, "h264", StringComparison.OrdinalIgnoreCase))
@@ -115,6 +118,7 @@ namespace MediaBrowser.Model.Dlna
{
return new MediaFormatProfile[] { MediaFormatProfile.AVC_TS_HD_DTS_ISO };
}
+
return new MediaFormatProfile[] { MediaFormatProfile.AVC_TS_HD_DTS_T };
}
diff --git a/MediaBrowser.Model/Dlna/ProfileCondition.cs b/MediaBrowser.Model/Dlna/ProfileCondition.cs
index 2021038d8..f8b5dee81 100644
--- a/MediaBrowser.Model/Dlna/ProfileCondition.cs
+++ b/MediaBrowser.Model/Dlna/ProfileCondition.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System.Xml.Serialization;
@@ -6,18 +7,6 @@ namespace MediaBrowser.Model.Dlna
{
public class ProfileCondition
{
- [XmlAttribute("condition")]
- public ProfileConditionType Condition { get; set; }
-
- [XmlAttribute("property")]
- public ProfileConditionValue Property { get; set; }
-
- [XmlAttribute("value")]
- public string Value { get; set; }
-
- [XmlAttribute("isRequired")]
- public bool IsRequired { get; set; }
-
public ProfileCondition()
{
IsRequired = true;
@@ -36,5 +25,17 @@ namespace MediaBrowser.Model.Dlna
Value = value;
IsRequired = isRequired;
}
+
+ [XmlAttribute("condition")]
+ public ProfileConditionType Condition { get; set; }
+
+ [XmlAttribute("property")]
+ public ProfileConditionValue Property { get; set; }
+
+ [XmlAttribute("value")]
+ public string Value { get; set; }
+
+ [XmlAttribute("isRequired")]
+ public bool IsRequired { get; set; }
}
}
diff --git a/MediaBrowser.Model/Dlna/ResolutionConfiguration.cs b/MediaBrowser.Model/Dlna/ResolutionConfiguration.cs
index c26eeec77..30c44fbe0 100644
--- a/MediaBrowser.Model/Dlna/ResolutionConfiguration.cs
+++ b/MediaBrowser.Model/Dlna/ResolutionConfiguration.cs
@@ -5,6 +5,7 @@ namespace MediaBrowser.Model.Dlna
public class ResolutionConfiguration
{
public int MaxWidth { get; set; }
+
public int MaxBitrate { get; set; }
public ResolutionConfiguration(int maxWidth, int maxBitrate)
diff --git a/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs b/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs
index 8235b72d1..102db3b44 100644
--- a/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs
+++ b/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
@@ -17,7 +18,8 @@ namespace MediaBrowser.Model.Dlna
new ResolutionConfiguration(3840, 35000000)
};
- public static ResolutionOptions Normalize(int? inputBitrate,
+ public static ResolutionOptions Normalize(
+ int? inputBitrate,
int? unused1,
int? unused2,
int outputBitrate,
@@ -83,6 +85,7 @@ namespace MediaBrowser.Model.Dlna
{
return .5;
}
+
return 1;
}
diff --git a/MediaBrowser.Model/Dlna/ResolutionOptions.cs b/MediaBrowser.Model/Dlna/ResolutionOptions.cs
index 5ea0252cb..774592abc 100644
--- a/MediaBrowser.Model/Dlna/ResolutionOptions.cs
+++ b/MediaBrowser.Model/Dlna/ResolutionOptions.cs
@@ -5,6 +5,7 @@ namespace MediaBrowser.Model.Dlna
public class ResolutionOptions
{
public int? MaxWidth { get; set; }
+
public int? MaxHeight { get; set; }
}
}
diff --git a/MediaBrowser.Model/Dlna/ResponseProfile.cs b/MediaBrowser.Model/Dlna/ResponseProfile.cs
index c264cb936..48f53f06c 100644
--- a/MediaBrowser.Model/Dlna/ResponseProfile.cs
+++ b/MediaBrowser.Model/Dlna/ResponseProfile.cs
@@ -1,5 +1,7 @@
+#nullable disable
#pragma warning disable CS1591
+using System;
using System.Xml.Serialization;
namespace MediaBrowser.Model.Dlna
@@ -28,7 +30,7 @@ namespace MediaBrowser.Model.Dlna
public ResponseProfile()
{
- Conditions = new ProfileCondition[] { };
+ Conditions = Array.Empty<ProfileCondition>();
}
public string[] GetContainers()
diff --git a/MediaBrowser.Model/Dlna/SearchCriteria.cs b/MediaBrowser.Model/Dlna/SearchCriteria.cs
index 394fb9af9..94f5bd3db 100644
--- a/MediaBrowser.Model/Dlna/SearchCriteria.cs
+++ b/MediaBrowser.Model/Dlna/SearchCriteria.cs
@@ -34,9 +34,9 @@ namespace MediaBrowser.Model.Dlna
public SearchCriteria(string search)
{
- if (string.IsNullOrEmpty(search))
+ if (search.Length == 0)
{
- throw new ArgumentNullException(nameof(search));
+ throw new ArgumentException("String can't be empty.", nameof(search));
}
SearchType = SearchType.Unknown;
@@ -48,11 +48,10 @@ namespace MediaBrowser.Model.Dlna
if (subFactors.Length == 3)
{
-
if (string.Equals("upnp:class", subFactors[0], StringComparison.OrdinalIgnoreCase) &&
- (string.Equals("=", subFactors[1]) || string.Equals("derivedfrom", subFactors[1], StringComparison.OrdinalIgnoreCase)))
+ (string.Equals("=", subFactors[1], StringComparison.Ordinal) || string.Equals("derivedfrom", subFactors[1], StringComparison.OrdinalIgnoreCase)))
{
- if (string.Equals("\"object.item.imageItem\"", subFactors[2]) || string.Equals("\"object.item.imageItem.photo\"", subFactors[2], StringComparison.OrdinalIgnoreCase))
+ if (string.Equals("\"object.item.imageItem\"", subFactors[2], StringComparison.Ordinal) || string.Equals("\"object.item.imageItem.photo\"", subFactors[2], StringComparison.OrdinalIgnoreCase))
{
SearchType = SearchType.Image;
}
diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs
index 58755b171..a18ad36c5 100644
--- a/MediaBrowser.Model/Dlna/StreamBuilder.cs
+++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
@@ -339,6 +340,7 @@ namespace MediaBrowser.Model.Dlna
{
transcodeReasons.Add(transcodeReason.Value);
}
+
all = false;
break;
}
diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs
index c9fe679e1..244463803 100644
--- a/MediaBrowser.Model/Dlna/StreamInfo.cs
+++ b/MediaBrowser.Model/Dlna/StreamInfo.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/Dlna/SubtitleProfile.cs b/MediaBrowser.Model/Dlna/SubtitleProfile.cs
index 9c28019aa..01e3c696b 100644
--- a/MediaBrowser.Model/Dlna/SubtitleProfile.cs
+++ b/MediaBrowser.Model/Dlna/SubtitleProfile.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/Dlna/SubtitleStreamInfo.cs b/MediaBrowser.Model/Dlna/SubtitleStreamInfo.cs
index 02b3a198c..2f01836bd 100644
--- a/MediaBrowser.Model/Dlna/SubtitleStreamInfo.cs
+++ b/MediaBrowser.Model/Dlna/SubtitleStreamInfo.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
namespace MediaBrowser.Model.Dlna
@@ -5,13 +6,21 @@ namespace MediaBrowser.Model.Dlna
public class SubtitleStreamInfo
{
public string Url { get; set; }
+
public string Language { get; set; }
+
public string Name { get; set; }
+
public bool IsForced { get; set; }
+
public string Format { get; set; }
+
public string DisplayTitle { get; set; }
+
public int Index { get; set; }
+
public SubtitleDeliveryMethod DeliveryMethod { get; set; }
+
public bool IsExternalUrl { get; set; }
}
}
diff --git a/MediaBrowser.Model/Dlna/TranscodingProfile.cs b/MediaBrowser.Model/Dlna/TranscodingProfile.cs
index 570ee7baa..f05e31047 100644
--- a/MediaBrowser.Model/Dlna/TranscodingProfile.cs
+++ b/MediaBrowser.Model/Dlna/TranscodingProfile.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System.Xml.Serialization;
diff --git a/MediaBrowser.Model/Dlna/UpnpDeviceInfo.cs b/MediaBrowser.Model/Dlna/UpnpDeviceInfo.cs
index 3dc1fca36..d71013f01 100644
--- a/MediaBrowser.Model/Dlna/UpnpDeviceInfo.cs
+++ b/MediaBrowser.Model/Dlna/UpnpDeviceInfo.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
@@ -9,8 +10,11 @@ namespace MediaBrowser.Model.Dlna
public class UpnpDeviceInfo
{
public Uri Location { get; set; }
+
public Dictionary<string, string> Headers { get; set; }
+
public IPAddress LocalIpAddress { get; set; }
+
public int LocalPort { get; set; }
}
}
diff --git a/MediaBrowser.Model/Dlna/VideoOptions.cs b/MediaBrowser.Model/Dlna/VideoOptions.cs
index 5b12fff1c..4194f17c6 100644
--- a/MediaBrowser.Model/Dlna/VideoOptions.cs
+++ b/MediaBrowser.Model/Dlna/VideoOptions.cs
@@ -8,6 +8,7 @@ namespace MediaBrowser.Model.Dlna
public class VideoOptions : AudioOptions
{
public int? AudioStreamIndex { get; set; }
+
public int? SubtitleStreamIndex { get; set; }
}
}
diff --git a/MediaBrowser.Model/Dlna/XmlAttribute.cs b/MediaBrowser.Model/Dlna/XmlAttribute.cs
index 31603a754..3a8939a79 100644
--- a/MediaBrowser.Model/Dlna/XmlAttribute.cs
+++ b/MediaBrowser.Model/Dlna/XmlAttribute.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System.Xml.Serialization;
diff --git a/MediaBrowser.Model/Drawing/DrawingUtils.cs b/MediaBrowser.Model/Drawing/DrawingUtils.cs
index 0be30b0ba..1512c5233 100644
--- a/MediaBrowser.Model/Drawing/DrawingUtils.cs
+++ b/MediaBrowser.Model/Drawing/DrawingUtils.cs
@@ -16,7 +16,8 @@ namespace MediaBrowser.Model.Drawing
/// <param name="maxWidth">A max fixed width, if desired.</param>
/// <param name="maxHeight">A max fixed height, if desired.</param>
/// <returns>A new size object.</returns>
- public static ImageDimensions Resize(ImageDimensions size,
+ public static ImageDimensions Resize(
+ ImageDimensions size,
int width,
int height,
int maxWidth,
@@ -62,7 +63,7 @@ namespace MediaBrowser.Model.Drawing
/// <param name="currentHeight">Height of the current.</param>
/// <param name="currentWidth">Width of the current.</param>
/// <param name="newHeight">The new height.</param>
- /// <returns>the new width</returns>
+ /// <returns>The new width.</returns>
private static int GetNewWidth(int currentHeight, int currentWidth, int newHeight)
=> Convert.ToInt32((double)newHeight / currentHeight * currentWidth);
diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs
index 607355d8d..c7f8f0584 100644
--- a/MediaBrowser.Model/Dto/BaseItemDto.cs
+++ b/MediaBrowser.Model/Dto/BaseItemDto.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
@@ -511,6 +512,13 @@ namespace MediaBrowser.Model.Dto
public string SeriesThumbImageTag { get; set; }
/// <summary>
+ /// Gets or sets the blurhashes for the image tags.
+ /// Maps image type to dictionary mapping image tag to blurhash value.
+ /// </summary>
+ /// <value>The blurhashes.</value>
+ public Dictionary<ImageType, Dictionary<string, string>> ImageBlurHashes { get; set; }
+
+ /// <summary>
/// Gets or sets the series studio.
/// </summary>
/// <value>The series studio.</value>
@@ -574,7 +582,7 @@ namespace MediaBrowser.Model.Dto
/// Gets or sets the locked fields.
/// </summary>
/// <value>The locked fields.</value>
- public MetadataFields[] LockedFields { get; set; }
+ public MetadataField[] LockedFields { get; set; }
/// <summary>
/// Gets or sets the trailer count.
diff --git a/MediaBrowser.Model/Dto/BaseItemPerson.cs b/MediaBrowser.Model/Dto/BaseItemPerson.cs
index 5b7eefd70..b080f3e4a 100644
--- a/MediaBrowser.Model/Dto/BaseItemPerson.cs
+++ b/MediaBrowser.Model/Dto/BaseItemPerson.cs
@@ -1,3 +1,4 @@
+#nullable disable
using System.Text.Json.Serialization;
namespace MediaBrowser.Model.Dto
diff --git a/MediaBrowser.Model/Dto/IHasServerId.cs b/MediaBrowser.Model/Dto/IHasServerId.cs
index 8c9798c5c..c754d276c 100644
--- a/MediaBrowser.Model/Dto/IHasServerId.cs
+++ b/MediaBrowser.Model/Dto/IHasServerId.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
namespace MediaBrowser.Model.Dto
diff --git a/MediaBrowser.Model/Dto/ImageByNameInfo.cs b/MediaBrowser.Model/Dto/ImageByNameInfo.cs
index d2e43634d..06cc3e73c 100644
--- a/MediaBrowser.Model/Dto/ImageByNameInfo.cs
+++ b/MediaBrowser.Model/Dto/ImageByNameInfo.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
namespace MediaBrowser.Model.Dto
diff --git a/MediaBrowser.Model/Dto/ImageInfo.cs b/MediaBrowser.Model/Dto/ImageInfo.cs
index 57942ac23..2e4b15a18 100644
--- a/MediaBrowser.Model/Dto/ImageInfo.cs
+++ b/MediaBrowser.Model/Dto/ImageInfo.cs
@@ -1,3 +1,4 @@
+#nullable disable
using MediaBrowser.Model.Entities;
namespace MediaBrowser.Model.Dto
@@ -20,9 +21,9 @@ namespace MediaBrowser.Model.Dto
public int? ImageIndex { get; set; }
/// <summary>
- /// The image tag
+ /// Gets or sets the image tag.
/// </summary>
- public string ImageTag;
+ public string ImageTag { get; set; }
/// <summary>
/// Gets or sets the path.
@@ -31,6 +32,12 @@ namespace MediaBrowser.Model.Dto
public string Path { get; set; }
/// <summary>
+ /// Gets or sets the blurhash.
+ /// </summary>
+ /// <value>The blurhash.</value>
+ public string BlurHash { get; set; }
+
+ /// <summary>
/// Gets or sets the height.
/// </summary>
/// <value>The height.</value>
diff --git a/MediaBrowser.Model/Dto/ImageOptions.cs b/MediaBrowser.Model/Dto/ImageOptions.cs
index 4e672a007..158e622a8 100644
--- a/MediaBrowser.Model/Dto/ImageOptions.cs
+++ b/MediaBrowser.Model/Dto/ImageOptions.cs
@@ -1,3 +1,4 @@
+#nullable disable
using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Entities;
@@ -9,6 +10,14 @@ namespace MediaBrowser.Model.Dto
public class ImageOptions
{
/// <summary>
+ /// Initializes a new instance of the <see cref="ImageOptions" /> class.
+ /// </summary>
+ public ImageOptions()
+ {
+ EnableImageEnhancers = true;
+ }
+
+ /// <summary>
/// Gets or sets the type of the image.
/// </summary>
/// <value>The type of the image.</value>
@@ -98,13 +107,5 @@ namespace MediaBrowser.Model.Dto
/// </summary>
/// <value>The color of the background.</value>
public string BackgroundColor { get; set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ImageOptions" /> class.
- /// </summary>
- public ImageOptions()
- {
- EnableImageEnhancers = true;
- }
}
}
diff --git a/MediaBrowser.Model/Dto/ItemIndex.cs b/MediaBrowser.Model/Dto/ItemIndex.cs
deleted file mode 100644
index 525576d61..000000000
--- a/MediaBrowser.Model/Dto/ItemIndex.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-namespace MediaBrowser.Model.Dto
-{
- /// <summary>
- /// Class ItemIndex.
- /// </summary>
- public class ItemIndex
- {
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the item count.
- /// </summary>
- /// <value>The item count.</value>
- public int ItemCount { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Dto/MediaSourceInfo.cs b/MediaBrowser.Model/Dto/MediaSourceInfo.cs
index 29613adbf..74c2cb4f4 100644
--- a/MediaBrowser.Model/Dto/MediaSourceInfo.cs
+++ b/MediaBrowser.Model/Dto/MediaSourceInfo.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/Dto/MetadataEditorInfo.cs b/MediaBrowser.Model/Dto/MetadataEditorInfo.cs
index 21d8a31f2..1d840a300 100644
--- a/MediaBrowser.Model/Dto/MetadataEditorInfo.cs
+++ b/MediaBrowser.Model/Dto/MetadataEditorInfo.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/Dto/NameIdPair.cs b/MediaBrowser.Model/Dto/NameIdPair.cs
index 1b4800863..efb2c157c 100644
--- a/MediaBrowser.Model/Dto/NameIdPair.cs
+++ b/MediaBrowser.Model/Dto/NameIdPair.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/Dto/NameValuePair.cs b/MediaBrowser.Model/Dto/NameValuePair.cs
index 74040c2cb..e71ff3c21 100644
--- a/MediaBrowser.Model/Dto/NameValuePair.cs
+++ b/MediaBrowser.Model/Dto/NameValuePair.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
namespace MediaBrowser.Model.Dto
@@ -6,7 +7,6 @@ namespace MediaBrowser.Model.Dto
{
public NameValuePair()
{
-
}
public NameValuePair(string name, string value)
diff --git a/MediaBrowser.Model/Dto/PublicUserDto.cs b/MediaBrowser.Model/Dto/PublicUserDto.cs
deleted file mode 100644
index b6bfaf2e9..000000000
--- a/MediaBrowser.Model/Dto/PublicUserDto.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-using System;
-
-namespace MediaBrowser.Model.Dto
-{
- /// <summary>
- /// Class PublicUserDto. Its goal is to show only public information about a user
- /// </summary>
- public class PublicUserDto : IItemDto
- {
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the primary image tag.
- /// </summary>
- /// <value>The primary image tag.</value>
- public string PrimaryImageTag { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance has password.
- /// </summary>
- /// <value><c>true</c> if this instance has password; otherwise, <c>false</c>.</value>
- public bool HasPassword { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance has configured password.
- /// Note that in this case this method should not be here, but it is necessary when changing password at the
- /// first login.
- /// </summary>
- /// <value><c>true</c> if this instance has configured password; otherwise, <c>false</c>.</value>
- public bool HasConfiguredPassword { get; set; }
-
- /// <summary>
- /// Gets or sets the primary image aspect ratio.
- /// </summary>
- /// <value>The primary image aspect ratio.</value>
- public double? PrimaryImageAspectRatio { get; set; }
-
- /// <inheritdoc />
- public override string ToString()
- {
- return Name ?? base.ToString();
- }
- }
-}
diff --git a/MediaBrowser.Model/Dto/RecommendationDto.cs b/MediaBrowser.Model/Dto/RecommendationDto.cs
index bc97dd6f1..107f41540 100644
--- a/MediaBrowser.Model/Dto/RecommendationDto.cs
+++ b/MediaBrowser.Model/Dto/RecommendationDto.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/Dto/UserDto.cs b/MediaBrowser.Model/Dto/UserDto.cs
index d36706c38..40222c9dc 100644
--- a/MediaBrowser.Model/Dto/UserDto.cs
+++ b/MediaBrowser.Model/Dto/UserDto.cs
@@ -1,3 +1,4 @@
+#nullable disable
using System;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Users;
diff --git a/MediaBrowser.Model/Dto/UserItemDataDto.cs b/MediaBrowser.Model/Dto/UserItemDataDto.cs
index 92f06c973..adb2cd2ab 100644
--- a/MediaBrowser.Model/Dto/UserItemDataDto.cs
+++ b/MediaBrowser.Model/Dto/UserItemDataDto.cs
@@ -1,3 +1,4 @@
+#nullable disable
using System;
namespace MediaBrowser.Model.Dto
diff --git a/MediaBrowser.Model/Entities/ChapterInfo.cs b/MediaBrowser.Model/Entities/ChapterInfo.cs
index bea7ec1db..45554c3dc 100644
--- a/MediaBrowser.Model/Entities/ChapterInfo.cs
+++ b/MediaBrowser.Model/Entities/ChapterInfo.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/Entities/DisplayPreferences.cs b/MediaBrowser.Model/Entities/DisplayPreferences.cs
index 2cd8bd306..0e5db01dd 100644
--- a/MediaBrowser.Model/Entities/DisplayPreferences.cs
+++ b/MediaBrowser.Model/Entities/DisplayPreferences.cs
@@ -1,3 +1,4 @@
+#nullable disable
using System.Collections.Generic;
namespace MediaBrowser.Model.Entities
diff --git a/MediaBrowser.Model/Entities/IHasProviderIds.cs b/MediaBrowser.Model/Entities/IHasProviderIds.cs
index c117efde9..1310f68ae 100644
--- a/MediaBrowser.Model/Entities/IHasProviderIds.cs
+++ b/MediaBrowser.Model/Entities/IHasProviderIds.cs
@@ -3,7 +3,7 @@ using System.Collections.Generic;
namespace MediaBrowser.Model.Entities
{
/// <summary>
- /// Since BaseItem and DTOBaseItem both have ProviderIds, this interface helps avoid code repition by using extension methods.
+ /// Since BaseItem and DTOBaseItem both have ProviderIds, this interface helps avoid code repetition by using extension methods.
/// </summary>
public interface IHasProviderIds
{
diff --git a/MediaBrowser.Model/Entities/ImageType.cs b/MediaBrowser.Model/Entities/ImageType.cs
index d89a4b3ad..6ea9ee419 100644
--- a/MediaBrowser.Model/Entities/ImageType.cs
+++ b/MediaBrowser.Model/Entities/ImageType.cs
@@ -63,6 +63,11 @@ namespace MediaBrowser.Model.Entities
/// <summary>
/// The box rear.
/// </summary>
- BoxRear = 11
+ BoxRear = 11,
+
+ /// <summary>
+ /// The user profile image.
+ /// </summary>
+ Profile = 12
}
}
diff --git a/MediaBrowser.Model/Entities/LibraryUpdateInfo.cs b/MediaBrowser.Model/Entities/LibraryUpdateInfo.cs
index b98c00240..6dd6653dc 100644
--- a/MediaBrowser.Model/Entities/LibraryUpdateInfo.cs
+++ b/MediaBrowser.Model/Entities/LibraryUpdateInfo.cs
@@ -5,15 +5,29 @@ using System;
namespace MediaBrowser.Model.Entities
{
/// <summary>
- /// Class LibraryUpdateInfo
+ /// Class LibraryUpdateInfo.
/// </summary>
public class LibraryUpdateInfo
{
/// <summary>
+ /// Initializes a new instance of the <see cref="LibraryUpdateInfo"/> class.
+ /// </summary>
+ public LibraryUpdateInfo()
+ {
+ FoldersAddedTo = Array.Empty<string>();
+ FoldersRemovedFrom = Array.Empty<string>();
+ ItemsAdded = Array.Empty<string>();
+ ItemsRemoved = Array.Empty<string>();
+ ItemsUpdated = Array.Empty<string>();
+ CollectionFolders = Array.Empty<string>();
+ }
+
+ /// <summary>
/// Gets or sets the folders added to.
/// </summary>
/// <value>The folders added to.</value>
public string[] FoldersAddedTo { get; set; }
+
/// <summary>
/// Gets or sets the folders removed from.
/// </summary>
@@ -41,18 +55,5 @@ namespace MediaBrowser.Model.Entities
public string[] CollectionFolders { get; set; }
public bool IsEmpty => FoldersAddedTo.Length == 0 && FoldersRemovedFrom.Length == 0 && ItemsAdded.Length == 0 && ItemsRemoved.Length == 0 && ItemsUpdated.Length == 0 && CollectionFolders.Length == 0;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="LibraryUpdateInfo"/> class.
- /// </summary>
- public LibraryUpdateInfo()
- {
- FoldersAddedTo = Array.Empty<string>();
- FoldersRemovedFrom = Array.Empty<string>();
- ItemsAdded = Array.Empty<string>();
- ItemsRemoved = Array.Empty<string>();
- ItemsUpdated = Array.Empty<string>();
- CollectionFolders = Array.Empty<string>();
- }
}
}
diff --git a/MediaBrowser.Model/Entities/MediaAttachment.cs b/MediaBrowser.Model/Entities/MediaAttachment.cs
index 167be18c9..34e3eabc9 100644
--- a/MediaBrowser.Model/Entities/MediaAttachment.cs
+++ b/MediaBrowser.Model/Entities/MediaAttachment.cs
@@ -1,3 +1,4 @@
+#nullable disable
namespace MediaBrowser.Model.Entities
{
/// <summary>
diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs
index ac33f1da4..fa09cc513 100644
--- a/MediaBrowser.Model/Entities/MediaStream.cs
+++ b/MediaBrowser.Model/Entities/MediaStream.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/Entities/MediaUrl.cs b/MediaBrowser.Model/Entities/MediaUrl.cs
index e44143755..74f982437 100644
--- a/MediaBrowser.Model/Entities/MediaUrl.cs
+++ b/MediaBrowser.Model/Entities/MediaUrl.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
namespace MediaBrowser.Model.Entities
diff --git a/MediaBrowser.Model/Entities/MetadataFields.cs b/MediaBrowser.Model/Entities/MetadataFields.cs
index d64d4f4da..2cc6c8e33 100644
--- a/MediaBrowser.Model/Entities/MetadataFields.cs
+++ b/MediaBrowser.Model/Entities/MetadataFields.cs
@@ -3,7 +3,7 @@ namespace MediaBrowser.Model.Entities
/// <summary>
/// Enum MetadataFields.
/// </summary>
- public enum MetadataFields
+ public enum MetadataField
{
/// <summary>
/// The cast.
diff --git a/MediaBrowser.Model/Entities/MetadataProviders.cs b/MediaBrowser.Model/Entities/MetadataProvider.cs
index 1a44a1661..bcc2b48e7 100644
--- a/MediaBrowser.Model/Entities/MetadataProviders.cs
+++ b/MediaBrowser.Model/Entities/MetadataProvider.cs
@@ -5,7 +5,7 @@ namespace MediaBrowser.Model.Entities
/// <summary>
/// Enum MetadataProviders
/// </summary>
- public enum MetadataProviders
+ public enum MetadataProvider
{
/// <summary>
/// The imdb
diff --git a/MediaBrowser.Model/Entities/PackageReviewInfo.cs b/MediaBrowser.Model/Entities/PackageReviewInfo.cs
index a034de8ba..1ebbc3323 100644
--- a/MediaBrowser.Model/Entities/PackageReviewInfo.cs
+++ b/MediaBrowser.Model/Entities/PackageReviewInfo.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
@@ -7,32 +8,32 @@ namespace MediaBrowser.Model.Entities
public class PackageReviewInfo
{
/// <summary>
- /// The package id (database key) for this review
+ /// Gets or sets the package id (database key) for this review.
/// </summary>
public int id { get; set; }
/// <summary>
- /// The rating value
+ /// Gets or sets the rating value.
/// </summary>
public int rating { get; set; }
/// <summary>
- /// Whether or not this review recommends this item
+ /// Gets or sets whether or not this review recommends this item.
/// </summary>
public bool recommend { get; set; }
/// <summary>
- /// A short description of the review
+ /// Gets or sets a short description of the review.
/// </summary>
public string title { get; set; }
/// <summary>
- /// A full review
+ /// Gets or sets the full review.
/// </summary>
public string review { get; set; }
/// <summary>
- /// Time of review
+ /// Gets or sets the time of review.
/// </summary>
public DateTime timestamp { get; set; }
diff --git a/MediaBrowser.Model/Entities/ParentalRating.cs b/MediaBrowser.Model/Entities/ParentalRating.cs
index 4b37bd64a..17b2868a3 100644
--- a/MediaBrowser.Model/Entities/ParentalRating.cs
+++ b/MediaBrowser.Model/Entities/ParentalRating.cs
@@ -1,12 +1,23 @@
+#nullable disable
#pragma warning disable CS1591
namespace MediaBrowser.Model.Entities
{
/// <summary>
- /// Class ParentalRating
+ /// Class ParentalRating.
/// </summary>
public class ParentalRating
{
+ public ParentalRating()
+ {
+ }
+
+ public ParentalRating(string name, int value)
+ {
+ Name = name;
+ Value = value;
+ }
+
/// <summary>
/// Gets or sets the name.
/// </summary>
@@ -18,16 +29,5 @@ namespace MediaBrowser.Model.Entities
/// </summary>
/// <value>The value.</value>
public int Value { get; set; }
-
- public ParentalRating()
- {
-
- }
-
- public ParentalRating(string name, int value)
- {
- Name = name;
- Value = value;
- }
}
}
diff --git a/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs b/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs
index 922eb4ca7..9c11fe0ad 100644
--- a/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs
+++ b/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs
@@ -14,7 +14,7 @@ namespace MediaBrowser.Model.Entities
/// <param name="instance">The instance.</param>
/// <param name="provider">The provider.</param>
/// <returns><c>true</c> if [has provider identifier] [the specified instance]; otherwise, <c>false</c>.</returns>
- public static bool HasProviderId(this IHasProviderIds instance, MetadataProviders provider)
+ public static bool HasProviderId(this IHasProviderIds instance, MetadataProvider provider)
{
return !string.IsNullOrEmpty(instance.GetProviderId(provider.ToString()));
}
@@ -25,7 +25,7 @@ namespace MediaBrowser.Model.Entities
/// <param name="instance">The instance.</param>
/// <param name="provider">The provider.</param>
/// <returns>System.String.</returns>
- public static string GetProviderId(this IHasProviderIds instance, MetadataProviders provider)
+ public static string? GetProviderId(this IHasProviderIds instance, MetadataProvider provider)
{
return instance.GetProviderId(provider.ToString());
}
@@ -36,7 +36,7 @@ namespace MediaBrowser.Model.Entities
/// <param name="instance">The instance.</param>
/// <param name="name">The name.</param>
/// <returns>System.String.</returns>
- public static string GetProviderId(this IHasProviderIds instance, string name)
+ public static string? GetProviderId(this IHasProviderIds instance, string name)
{
if (instance == null)
{
@@ -94,7 +94,7 @@ namespace MediaBrowser.Model.Entities
/// <param name="instance">The instance.</param>
/// <param name="provider">The provider.</param>
/// <param name="value">The value.</param>
- public static void SetProviderId(this IHasProviderIds instance, MetadataProviders provider, string value)
+ public static void SetProviderId(this IHasProviderIds instance, MetadataProvider provider, string value)
{
instance.SetProviderId(provider.ToString(), value);
}
diff --git a/MediaBrowser.Model/Entities/VirtualFolderInfo.cs b/MediaBrowser.Model/Entities/VirtualFolderInfo.cs
index dd30c9c84..2de02e403 100644
--- a/MediaBrowser.Model/Entities/VirtualFolderInfo.cs
+++ b/MediaBrowser.Model/Entities/VirtualFolderInfo.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/Events/GenericEventArgs.cs b/MediaBrowser.Model/Events/GenericEventArgs.cs
index 1ef0b25c9..44f60f811 100644
--- a/MediaBrowser.Model/Events/GenericEventArgs.cs
+++ b/MediaBrowser.Model/Events/GenericEventArgs.cs
@@ -22,12 +22,5 @@ namespace MediaBrowser.Model.Events
{
Argument = arg;
}
-
- /// <summary>
- /// Initializes a new instance of the <see cref="GenericEventArgs{T}"/> class.
- /// </summary>
- public GenericEventArgs()
- {
- }
}
}
diff --git a/MediaBrowser.Model/Extensions/ListHelper.cs b/MediaBrowser.Model/Extensions/ListHelper.cs
new file mode 100644
index 000000000..b893a3509
--- /dev/null
+++ b/MediaBrowser.Model/Extensions/ListHelper.cs
@@ -0,0 +1,29 @@
+#nullable disable
+#pragma warning disable CS1591
+
+using System;
+
+namespace MediaBrowser.Model.Extensions
+{
+ // TODO: @bond remove
+ public static class ListHelper
+ {
+ public static bool ContainsIgnoreCase(string[] list, string value)
+ {
+ if (value == null)
+ {
+ throw new ArgumentNullException(nameof(value));
+ }
+
+ foreach (var item in list)
+ {
+ if (string.Equals(item, value, StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/MediaBrowser.Model/Extensions/StringHelper.cs b/MediaBrowser.Model/Extensions/StringHelper.cs
index f819a295c..8ffa3c4ba 100644
--- a/MediaBrowser.Model/Extensions/StringHelper.cs
+++ b/MediaBrowser.Model/Extensions/StringHelper.cs
@@ -12,9 +12,9 @@ namespace MediaBrowser.Model.Extensions
/// <returns>The string with the first character as uppercase.</returns>
public static string FirstToUpper(string str)
{
- if (string.IsNullOrEmpty(str))
+ if (str.Length == 0)
{
- return string.Empty;
+ return str;
}
if (char.IsUpper(str[0]))
diff --git a/MediaBrowser.Model/Globalization/CountryInfo.cs b/MediaBrowser.Model/Globalization/CountryInfo.cs
index 72362f4f3..6f6979316 100644
--- a/MediaBrowser.Model/Globalization/CountryInfo.cs
+++ b/MediaBrowser.Model/Globalization/CountryInfo.cs
@@ -1,3 +1,4 @@
+#nullable disable
namespace MediaBrowser.Model.Globalization
{
/// <summary>
diff --git a/MediaBrowser.Model/Globalization/CultureDto.cs b/MediaBrowser.Model/Globalization/CultureDto.cs
index f415840b0..6af4a872c 100644
--- a/MediaBrowser.Model/Globalization/CultureDto.cs
+++ b/MediaBrowser.Model/Globalization/CultureDto.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/Globalization/ILocalizationManager.cs b/MediaBrowser.Model/Globalization/ILocalizationManager.cs
index 613bfca69..baefeb39c 100644
--- a/MediaBrowser.Model/Globalization/ILocalizationManager.cs
+++ b/MediaBrowser.Model/Globalization/ILocalizationManager.cs
@@ -1,3 +1,4 @@
+#nullable disable
using System.Collections.Generic;
using System.Globalization;
using MediaBrowser.Model.Entities;
diff --git a/MediaBrowser.Model/Globalization/LocalizationOption.cs b/MediaBrowser.Model/Globalization/LocalizationOption.cs
index 00caf5e11..81f47d978 100644
--- a/MediaBrowser.Model/Globalization/LocalizationOption.cs
+++ b/MediaBrowser.Model/Globalization/LocalizationOption.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
namespace MediaBrowser.Model.Globalization
@@ -11,6 +12,7 @@ namespace MediaBrowser.Model.Globalization
}
public string Name { get; set; }
+
public string Value { get; set; }
}
}
diff --git a/MediaBrowser.Model/IO/FileSystemEntryInfo.cs b/MediaBrowser.Model/IO/FileSystemEntryInfo.cs
index a197f0fbe..36ff5d041 100644
--- a/MediaBrowser.Model/IO/FileSystemEntryInfo.cs
+++ b/MediaBrowser.Model/IO/FileSystemEntryInfo.cs
@@ -1,26 +1,39 @@
namespace MediaBrowser.Model.IO
{
/// <summary>
- /// Class FileSystemEntryInfo
+ /// Class FileSystemEntryInfo.
/// </summary>
public class FileSystemEntryInfo
{
/// <summary>
- /// Gets or sets the name.
+ /// Initializes a new instance of the <see cref="FileSystemEntryInfo" /> class.
+ /// </summary>
+ /// <param name="name">The filename.</param>
+ /// <param name="path">The file path.</param>
+ /// <param name="type">The file type.</param>
+ public FileSystemEntryInfo(string name, string path, FileSystemEntryType type)
+ {
+ Name = name;
+ Path = path;
+ Type = type;
+ }
+
+ /// <summary>
+ /// Gets the name.
/// </summary>
/// <value>The name.</value>
- public string Name { get; set; }
+ public string Name { get; }
/// <summary>
- /// Gets or sets the path.
+ /// Gets the path.
/// </summary>
/// <value>The path.</value>
- public string Path { get; set; }
+ public string Path { get; }
/// <summary>
- /// Gets or sets the type.
+ /// Gets the type.
/// </summary>
/// <value>The type.</value>
- public FileSystemEntryType Type { get; set; }
+ public FileSystemEntryType Type { get; }
}
}
diff --git a/MediaBrowser.Model/IO/FileSystemMetadata.cs b/MediaBrowser.Model/IO/FileSystemMetadata.cs
index 4b9102392..b23119d08 100644
--- a/MediaBrowser.Model/IO/FileSystemMetadata.cs
+++ b/MediaBrowser.Model/IO/FileSystemMetadata.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/IO/IFileSystem.cs b/MediaBrowser.Model/IO/IFileSystem.cs
index 53f23a8e0..bba69d4b4 100644
--- a/MediaBrowser.Model/IO/IFileSystem.cs
+++ b/MediaBrowser.Model/IO/IFileSystem.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/IO/IIsoManager.cs b/MediaBrowser.Model/IO/IIsoManager.cs
index 8b6af019d..299bb0a21 100644
--- a/MediaBrowser.Model/IO/IIsoManager.cs
+++ b/MediaBrowser.Model/IO/IIsoManager.cs
@@ -16,7 +16,6 @@ namespace MediaBrowser.Model.IO
/// <param name="isoPath">The iso path.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>IsoMount.</returns>
- /// <exception cref="ArgumentNullException">isoPath</exception>
/// <exception cref="IOException">Unable to create mount.</exception>
Task<IIsoMount> Mount(string isoPath, CancellationToken cancellationToken);
diff --git a/MediaBrowser.Model/IO/IIsoMount.cs b/MediaBrowser.Model/IO/IIsoMount.cs
index 72ec673ee..ea65d976a 100644
--- a/MediaBrowser.Model/IO/IIsoMount.cs
+++ b/MediaBrowser.Model/IO/IIsoMount.cs
@@ -8,7 +8,7 @@ namespace MediaBrowser.Model.IO
public interface IIsoMount : IDisposable
{
/// <summary>
- /// Gets or sets the iso path.
+ /// Gets the iso path.
/// </summary>
/// <value>The iso path.</value>
string IsoPath { get; }
diff --git a/MediaBrowser.Model/IO/IIsoMounter.cs b/MediaBrowser.Model/IO/IIsoMounter.cs
index 83fdb5fd6..0d257395a 100644
--- a/MediaBrowser.Model/IO/IIsoMounter.cs
+++ b/MediaBrowser.Model/IO/IIsoMounter.cs
@@ -10,6 +10,12 @@ namespace MediaBrowser.Model.IO
public interface IIsoMounter
{
/// <summary>
+ /// Gets the name.
+ /// </summary>
+ /// <value>The name.</value>
+ string Name { get; }
+
+ /// <summary>
/// Mounts the specified iso path.
/// </summary>
/// <param name="isoPath">The iso path.</param>
@@ -25,11 +31,5 @@ namespace MediaBrowser.Model.IO
/// <param name="path">The path.</param>
/// <returns><c>true</c> if this instance can mount the specified path; otherwise, <c>false</c>.</returns>
bool CanMount(string path);
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- string Name { get; }
}
}
diff --git a/MediaBrowser.Model/IO/IStreamHelper.cs b/MediaBrowser.Model/IO/IStreamHelper.cs
index e348cd725..af5ba5b17 100644
--- a/MediaBrowser.Model/IO/IStreamHelper.cs
+++ b/MediaBrowser.Model/IO/IStreamHelper.cs
@@ -14,6 +14,7 @@ namespace MediaBrowser.Model.IO
Task CopyToAsync(Stream source, Stream destination, int bufferSize, int emptyReadLimit, CancellationToken cancellationToken);
Task<int> CopyToAsync(Stream source, Stream destination, CancellationToken cancellationToken);
+
Task CopyToAsync(Stream source, Stream destination, long copyLength, CancellationToken cancellationToken);
Task CopyUntilCancelled(Stream source, Stream target, int bufferSize, CancellationToken cancellationToken);
diff --git a/MediaBrowser.Model/Library/UserViewQuery.cs b/MediaBrowser.Model/Library/UserViewQuery.cs
index a538efd25..8a49b6863 100644
--- a/MediaBrowser.Model/Library/UserViewQuery.cs
+++ b/MediaBrowser.Model/Library/UserViewQuery.cs
@@ -6,6 +6,12 @@ namespace MediaBrowser.Model.Library
{
public class UserViewQuery
{
+ public UserViewQuery()
+ {
+ IncludeExternalContent = true;
+ PresetViews = Array.Empty<string>();
+ }
+
/// <summary>
/// Gets or sets the user identifier.
/// </summary>
@@ -25,11 +31,5 @@ namespace MediaBrowser.Model.Library
public bool IncludeHidden { get; set; }
public string[] PresetViews { get; set; }
-
- public UserViewQuery()
- {
- IncludeExternalContent = true;
- PresetViews = Array.Empty<string>();
- }
}
}
diff --git a/MediaBrowser.Model/LiveTv/BaseTimerInfoDto.cs b/MediaBrowser.Model/LiveTv/BaseTimerInfoDto.cs
index 064ce6520..45970cf6b 100644
--- a/MediaBrowser.Model/LiveTv/BaseTimerInfoDto.cs
+++ b/MediaBrowser.Model/LiveTv/BaseTimerInfoDto.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/LiveTv/GuideInfo.cs b/MediaBrowser.Model/LiveTv/GuideInfo.cs
index a224d73b7..b1cc8cfdf 100644
--- a/MediaBrowser.Model/LiveTv/GuideInfo.cs
+++ b/MediaBrowser.Model/LiveTv/GuideInfo.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs b/MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs
index 8154fbd0e..d1a94d8b3 100644
--- a/MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs
+++ b/MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/LiveTv/LiveTvInfo.cs b/MediaBrowser.Model/LiveTv/LiveTvInfo.cs
index 85b77af24..9767509d0 100644
--- a/MediaBrowser.Model/LiveTv/LiveTvInfo.cs
+++ b/MediaBrowser.Model/LiveTv/LiveTvInfo.cs
@@ -6,6 +6,12 @@ namespace MediaBrowser.Model.LiveTv
{
public class LiveTvInfo
{
+ public LiveTvInfo()
+ {
+ Services = Array.Empty<LiveTvServiceInfo>();
+ EnabledUsers = Array.Empty<string>();
+ }
+
/// <summary>
/// Gets or sets the services.
/// </summary>
@@ -23,11 +29,5 @@ namespace MediaBrowser.Model.LiveTv
/// </summary>
/// <value>The enabled users.</value>
public string[] EnabledUsers { get; set; }
-
- public LiveTvInfo()
- {
- Services = Array.Empty<LiveTvServiceInfo>();
- EnabledUsers = Array.Empty<string>();
- }
}
}
diff --git a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs
index dc8e0f91b..69c43efd4 100644
--- a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs
+++ b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/LiveTv/LiveTvServiceInfo.cs b/MediaBrowser.Model/LiveTv/LiveTvServiceInfo.cs
index 09e900643..856f638c5 100644
--- a/MediaBrowser.Model/LiveTv/LiveTvServiceInfo.cs
+++ b/MediaBrowser.Model/LiveTv/LiveTvServiceInfo.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/LiveTv/RecordingQuery.cs b/MediaBrowser.Model/LiveTv/RecordingQuery.cs
index c75092b79..264982930 100644
--- a/MediaBrowser.Model/LiveTv/RecordingQuery.cs
+++ b/MediaBrowser.Model/LiveTv/RecordingQuery.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs b/MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs
index 29f417489..90422d19c 100644
--- a/MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs
+++ b/MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/LiveTv/SeriesTimerQuery.cs b/MediaBrowser.Model/LiveTv/SeriesTimerQuery.cs
index bb553a576..bda46dd2b 100644
--- a/MediaBrowser.Model/LiveTv/SeriesTimerQuery.cs
+++ b/MediaBrowser.Model/LiveTv/SeriesTimerQuery.cs
@@ -10,7 +10,7 @@ namespace MediaBrowser.Model.LiveTv
/// Gets or sets the sort by - SortName, Priority
/// </summary>
/// <value>The sort by.</value>
- public string SortBy { get; set; }
+ public string? SortBy { get; set; }
/// <summary>
/// Gets or sets the sort order.
diff --git a/MediaBrowser.Model/LiveTv/TimerInfoDto.cs b/MediaBrowser.Model/LiveTv/TimerInfoDto.cs
index a1fbc5177..19039d448 100644
--- a/MediaBrowser.Model/LiveTv/TimerInfoDto.cs
+++ b/MediaBrowser.Model/LiveTv/TimerInfoDto.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using MediaBrowser.Model.Dto;
diff --git a/MediaBrowser.Model/LiveTv/TimerQuery.cs b/MediaBrowser.Model/LiveTv/TimerQuery.cs
index 1ef6dd67e..367c45b9d 100644
--- a/MediaBrowser.Model/LiveTv/TimerQuery.cs
+++ b/MediaBrowser.Model/LiveTv/TimerQuery.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
namespace MediaBrowser.Model.LiveTv
diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj
index 461f59672..83bd0c07e 100644
--- a/MediaBrowser.Model/MediaBrowser.Model.csproj
+++ b/MediaBrowser.Model/MediaBrowser.Model.csproj
@@ -17,11 +17,13 @@
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<TreatWarningsAsErrors Condition=" '$(Configuration)' == 'Release' ">true</TreatWarningsAsErrors>
+ <Nullable>enable</Nullable>
+ <LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" />
- <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="3.1.4" />
+ <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="3.1.5" />
<PackageReference Include="System.Globalization" Version="4.3.0" />
<PackageReference Include="System.Text.Json" Version="4.7.2" />
</ItemGroup>
diff --git a/MediaBrowser.Model/MediaInfo/AudioCodec.cs b/MediaBrowser.Model/MediaInfo/AudioCodec.cs
index dcb6fa270..8b17757b8 100644
--- a/MediaBrowser.Model/MediaInfo/AudioCodec.cs
+++ b/MediaBrowser.Model/MediaInfo/AudioCodec.cs
@@ -10,9 +10,9 @@ namespace MediaBrowser.Model.MediaInfo
public static string GetFriendlyName(string codec)
{
- if (string.IsNullOrEmpty(codec))
+ if (codec.Length == 0)
{
- return string.Empty;
+ return codec;
}
switch (codec.ToLowerInvariant())
diff --git a/MediaBrowser.Model/MediaInfo/BlurayDiscInfo.cs b/MediaBrowser.Model/MediaInfo/BlurayDiscInfo.cs
index 29ba10dbb..83f982a5c 100644
--- a/MediaBrowser.Model/MediaInfo/BlurayDiscInfo.cs
+++ b/MediaBrowser.Model/MediaInfo/BlurayDiscInfo.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using MediaBrowser.Model.Entities;
diff --git a/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs b/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs
index 52348f802..cce508809 100644
--- a/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs
+++ b/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
@@ -7,21 +8,6 @@ namespace MediaBrowser.Model.MediaInfo
{
public class LiveStreamRequest
{
- public string OpenToken { get; set; }
- public Guid UserId { get; set; }
- public string PlaySessionId { get; set; }
- public long? MaxStreamingBitrate { get; set; }
- public long? StartTimeTicks { get; set; }
- public int? AudioStreamIndex { get; set; }
- public int? SubtitleStreamIndex { get; set; }
- public int? MaxAudioChannels { get; set; }
- public Guid ItemId { get; set; }
- public DeviceProfile DeviceProfile { get; set; }
-
- public bool EnableDirectPlay { get; set; }
- public bool EnableDirectStream { get; set; }
- public MediaProtocol[] DirectPlayProtocols { get; set; }
-
public LiveStreamRequest()
{
EnableDirectPlay = true;
@@ -38,12 +24,26 @@ namespace MediaBrowser.Model.MediaInfo
DirectPlayProtocols = new MediaProtocol[] { MediaProtocol.Http };
- var videoOptions = options as VideoOptions;
- if (videoOptions != null)
+ if (options is VideoOptions videoOptions)
{
AudioStreamIndex = videoOptions.AudioStreamIndex;
SubtitleStreamIndex = videoOptions.SubtitleStreamIndex;
}
}
+
+ public string OpenToken { get; set; }
+ public Guid UserId { get; set; }
+ public string PlaySessionId { get; set; }
+ public long? MaxStreamingBitrate { get; set; }
+ public long? StartTimeTicks { get; set; }
+ public int? AudioStreamIndex { get; set; }
+ public int? SubtitleStreamIndex { get; set; }
+ public int? MaxAudioChannels { get; set; }
+ public Guid ItemId { get; set; }
+ public DeviceProfile DeviceProfile { get; set; }
+
+ public bool EnableDirectPlay { get; set; }
+ public bool EnableDirectStream { get; set; }
+ public MediaProtocol[] DirectPlayProtocols { get; set; }
}
}
diff --git a/MediaBrowser.Model/MediaInfo/LiveStreamResponse.cs b/MediaBrowser.Model/MediaInfo/LiveStreamResponse.cs
index 45b8fcce9..f017c1a11 100644
--- a/MediaBrowser.Model/MediaInfo/LiveStreamResponse.cs
+++ b/MediaBrowser.Model/MediaInfo/LiveStreamResponse.cs
@@ -6,6 +6,11 @@ namespace MediaBrowser.Model.MediaInfo
{
public class LiveStreamResponse
{
- public MediaSourceInfo MediaSource { get; set; }
+ public LiveStreamResponse(MediaSourceInfo mediaSource)
+ {
+ MediaSource = mediaSource;
+ }
+
+ public MediaSourceInfo MediaSource { get; }
}
}
diff --git a/MediaBrowser.Model/MediaInfo/MediaInfo.cs b/MediaBrowser.Model/MediaInfo/MediaInfo.cs
index ad174f15d..97b979935 100644
--- a/MediaBrowser.Model/MediaInfo/MediaInfo.cs
+++ b/MediaBrowser.Model/MediaInfo/MediaInfo.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs b/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs
index a2f163422..82e13e0eb 100644
--- a/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs
+++ b/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/MediaInfo/PlaybackInfoResponse.cs b/MediaBrowser.Model/MediaInfo/PlaybackInfoResponse.cs
index 440818c3e..273350182 100644
--- a/MediaBrowser.Model/MediaInfo/PlaybackInfoResponse.cs
+++ b/MediaBrowser.Model/MediaInfo/PlaybackInfoResponse.cs
@@ -20,7 +20,7 @@ namespace MediaBrowser.Model.MediaInfo
/// Gets or sets the play session identifier.
/// </summary>
/// <value>The play session identifier.</value>
- public string PlaySessionId { get; set; }
+ public string? PlaySessionId { get; set; }
/// <summary>
/// Gets or sets the error code.
diff --git a/MediaBrowser.Model/MediaInfo/SubtitleTrackEvent.cs b/MediaBrowser.Model/MediaInfo/SubtitleTrackEvent.cs
index 5b0ccb28a..72bb3d9c6 100644
--- a/MediaBrowser.Model/MediaInfo/SubtitleTrackEvent.cs
+++ b/MediaBrowser.Model/MediaInfo/SubtitleTrackEvent.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
namespace MediaBrowser.Model.MediaInfo
@@ -5,8 +6,11 @@ namespace MediaBrowser.Model.MediaInfo
public class SubtitleTrackEvent
{
public string Id { get; set; }
+
public string Text { get; set; }
+
public long StartPositionTicks { get; set; }
+
public long EndPositionTicks { get; set; }
}
}
diff --git a/MediaBrowser.Model/Net/EndPointInfo.cs b/MediaBrowser.Model/Net/EndPointInfo.cs
index f5ac3d169..034734a9e 100644
--- a/MediaBrowser.Model/Net/EndPointInfo.cs
+++ b/MediaBrowser.Model/Net/EndPointInfo.cs
@@ -5,6 +5,7 @@ namespace MediaBrowser.Model.Net
public class EndPointInfo
{
public bool IsLocal { get; set; }
+
public bool IsInNetwork { get; set; }
}
}
diff --git a/MediaBrowser.Model/Net/ISocket.cs b/MediaBrowser.Model/Net/ISocket.cs
index 2bfbfcb20..5b6ed92df 100644
--- a/MediaBrowser.Model/Net/ISocket.cs
+++ b/MediaBrowser.Model/Net/ISocket.cs
@@ -17,6 +17,7 @@ namespace MediaBrowser.Model.Net
Task<SocketReceiveResult> ReceiveAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken);
IAsyncResult BeginReceive(byte[] buffer, int offset, int count, AsyncCallback callback);
+
SocketReceiveResult EndReceive(IAsyncResult result);
/// <summary>
diff --git a/MediaBrowser.Model/Net/MimeTypes.cs b/MediaBrowser.Model/Net/MimeTypes.cs
index b6d7b4245..771ca84f7 100644
--- a/MediaBrowser.Model/Net/MimeTypes.cs
+++ b/MediaBrowser.Model/Net/MimeTypes.cs
@@ -8,12 +8,12 @@ using System.Linq;
namespace MediaBrowser.Model.Net
{
/// <summary>
- /// Class MimeTypes
+ /// Class MimeTypes.
/// </summary>
public static class MimeTypes
{
/// <summary>
- /// Any extension in this list is considered a video file
+ /// Any extension in this list is considered a video file.
/// </summary>
private static readonly HashSet<string> _videoFileExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
@@ -163,16 +163,16 @@ namespace MediaBrowser.Model.Net
return dict;
}
- public static string GetMimeType(string path) => GetMimeType(path, true);
+ public static string? GetMimeType(string path) => GetMimeType(path, true);
/// <summary>
/// Gets the type of the MIME.
/// </summary>
- public static string GetMimeType(string path, bool enableStreamDefault)
+ public static string? GetMimeType(string path, bool enableStreamDefault)
{
- if (string.IsNullOrEmpty(path))
+ if (path.Length == 0)
{
- throw new ArgumentNullException(nameof(path));
+ throw new ArgumentException("String can't be empty.", nameof(path));
}
var ext = Path.GetExtension(path);
@@ -210,11 +210,11 @@ namespace MediaBrowser.Model.Net
return enableStreamDefault ? "application/octet-stream" : null;
}
- public static string ToExtension(string mimeType)
+ public static string? ToExtension(string mimeType)
{
- if (string.IsNullOrEmpty(mimeType))
+ if (mimeType.Length == 0)
{
- throw new ArgumentNullException(nameof(mimeType));
+ throw new ArgumentException("String can't be empty.", nameof(mimeType));
}
// handle text/html; charset=UTF-8
diff --git a/MediaBrowser.Model/Net/NetworkShare.cs b/MediaBrowser.Model/Net/NetworkShare.cs
index 744c6ec14..a40cf73e4 100644
--- a/MediaBrowser.Model/Net/NetworkShare.cs
+++ b/MediaBrowser.Model/Net/NetworkShare.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
namespace MediaBrowser.Model.Net
diff --git a/MediaBrowser.Model/Net/SocketReceiveResult.cs b/MediaBrowser.Model/Net/SocketReceiveResult.cs
index 141ae1608..54139fe9c 100644
--- a/MediaBrowser.Model/Net/SocketReceiveResult.cs
+++ b/MediaBrowser.Model/Net/SocketReceiveResult.cs
@@ -1,4 +1,4 @@
-#pragma warning disable CS1591
+#nullable disable
using System.Net;
@@ -10,12 +10,12 @@ namespace MediaBrowser.Model.Net
public sealed class SocketReceiveResult
{
/// <summary>
- /// The buffer to place received data into.
+ /// Gets or sets the buffer to place received data into.
/// </summary>
public byte[] Buffer { get; set; }
/// <summary>
- /// The number of bytes received.
+ /// Gets or sets the number of bytes received.
/// </summary>
public int ReceivedBytes { get; set; }
@@ -23,6 +23,10 @@ namespace MediaBrowser.Model.Net
/// The <see cref="IPEndPoint"/> the data was received from.
/// </summary>
public IPEndPoint RemoteEndPoint { get; set; }
+
+ /// <summary>
+ /// The local <see cref="IPAddress"/>.
+ /// </summary>
public IPAddress LocalIPAddress { get; set; }
}
}
diff --git a/MediaBrowser.Model/Net/WebSocketMessage.cs b/MediaBrowser.Model/Net/WebSocketMessage.cs
index 03f03e4cc..660eebeda 100644
--- a/MediaBrowser.Model/Net/WebSocketMessage.cs
+++ b/MediaBrowser.Model/Net/WebSocketMessage.cs
@@ -1,4 +1,4 @@
-
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/Notifications/NotificationOption.cs b/MediaBrowser.Model/Notifications/NotificationOption.cs
index 4fb724515..144949a3b 100644
--- a/MediaBrowser.Model/Notifications/NotificationOption.cs
+++ b/MediaBrowser.Model/Notifications/NotificationOption.cs
@@ -6,6 +6,15 @@ namespace MediaBrowser.Model.Notifications
{
public class NotificationOption
{
+ public NotificationOption(string type)
+ {
+ Type = type;
+
+ DisabledServices = Array.Empty<string>();
+ DisabledMonitorUsers = Array.Empty<string>();
+ SendToUsers = Array.Empty<string>();
+ }
+
public string Type { get; set; }
/// <summary>
@@ -35,12 +44,5 @@ namespace MediaBrowser.Model.Notifications
/// </summary>
/// <value>The send to user mode.</value>
public SendToUserType SendToUserMode { get; set; }
-
- public NotificationOption()
- {
- DisabledServices = Array.Empty<string>();
- DisabledMonitorUsers = Array.Empty<string>();
- SendToUsers = Array.Empty<string>();
- }
}
}
diff --git a/MediaBrowser.Model/Notifications/NotificationOptions.cs b/MediaBrowser.Model/Notifications/NotificationOptions.cs
index 9c54bd70e..239a3777e 100644
--- a/MediaBrowser.Model/Notifications/NotificationOptions.cs
+++ b/MediaBrowser.Model/Notifications/NotificationOptions.cs
@@ -1,7 +1,11 @@
+#nullable disable
#pragma warning disable CS1591
using System;
+using Jellyfin.Data.Enums;
+using MediaBrowser.Model.Extensions;
using System.Linq;
+using Jellyfin.Data.Entities;
using MediaBrowser.Model.Users;
namespace MediaBrowser.Model.Notifications
@@ -14,63 +18,53 @@ namespace MediaBrowser.Model.Notifications
{
Options = new[]
{
- new NotificationOption
+ new NotificationOption(NotificationType.TaskFailed.ToString())
{
- Type = NotificationType.TaskFailed.ToString(),
Enabled = true,
SendToUserMode = SendToUserType.Admins
},
- new NotificationOption
+ new NotificationOption(NotificationType.ServerRestartRequired.ToString())
{
- Type = NotificationType.ServerRestartRequired.ToString(),
Enabled = true,
SendToUserMode = SendToUserType.Admins
},
- new NotificationOption
+ new NotificationOption(NotificationType.ApplicationUpdateAvailable.ToString())
{
- Type = NotificationType.ApplicationUpdateAvailable.ToString(),
Enabled = true,
SendToUserMode = SendToUserType.Admins
},
- new NotificationOption
+ new NotificationOption(NotificationType.ApplicationUpdateInstalled.ToString())
{
- Type = NotificationType.ApplicationUpdateInstalled.ToString(),
Enabled = true,
SendToUserMode = SendToUserType.Admins
},
- new NotificationOption
+ new NotificationOption(NotificationType.PluginUpdateInstalled.ToString())
{
- Type = NotificationType.PluginUpdateInstalled.ToString(),
Enabled = true,
SendToUserMode = SendToUserType.Admins
},
- new NotificationOption
+ new NotificationOption(NotificationType.PluginUninstalled.ToString())
{
- Type = NotificationType.PluginUninstalled.ToString(),
Enabled = true,
SendToUserMode = SendToUserType.Admins
},
- new NotificationOption
+ new NotificationOption(NotificationType.InstallationFailed.ToString())
{
- Type = NotificationType.InstallationFailed.ToString(),
Enabled = true,
SendToUserMode = SendToUserType.Admins
},
- new NotificationOption
+ new NotificationOption(NotificationType.PluginInstalled.ToString())
{
- Type = NotificationType.PluginInstalled.ToString(),
Enabled = true,
SendToUserMode = SendToUserType.Admins
},
- new NotificationOption
+ new NotificationOption(NotificationType.PluginError.ToString())
{
- Type = NotificationType.PluginError.ToString(),
Enabled = true,
SendToUserMode = SendToUserType.Admins
},
- new NotificationOption
+ new NotificationOption(NotificationType.UserLockedOut.ToString())
{
- Type = NotificationType.UserLockedOut.ToString(),
Enabled = true,
SendToUserMode = SendToUserType.Admins
}
@@ -113,7 +107,7 @@ namespace MediaBrowser.Model.Notifications
!opt.DisabledMonitorUsers.Contains(userId.ToString(""), StringComparer.OrdinalIgnoreCase);
}
- public bool IsEnabledToSendToUser(string type, string userId, UserPolicy userPolicy)
+ public bool IsEnabledToSendToUser(string type, string userId, User user)
{
NotificationOption opt = GetOptions(type);
@@ -124,7 +118,7 @@ namespace MediaBrowser.Model.Notifications
return true;
}
- if (opt.SendToUserMode == SendToUserType.Admins && userPolicy.IsAdministrator)
+ if (opt.SendToUserMode == SendToUserType.Admins && user.HasPermission(PermissionKind.IsAdministrator))
{
return true;
}
diff --git a/MediaBrowser.Model/Notifications/NotificationRequest.cs b/MediaBrowser.Model/Notifications/NotificationRequest.cs
index ffcfab24f..febc2bc09 100644
--- a/MediaBrowser.Model/Notifications/NotificationRequest.cs
+++ b/MediaBrowser.Model/Notifications/NotificationRequest.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/Notifications/NotificationTypeInfo.cs b/MediaBrowser.Model/Notifications/NotificationTypeInfo.cs
index bfa163b40..402fbe81a 100644
--- a/MediaBrowser.Model/Notifications/NotificationTypeInfo.cs
+++ b/MediaBrowser.Model/Notifications/NotificationTypeInfo.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
namespace MediaBrowser.Model.Notifications
diff --git a/MediaBrowser.Model/Playlists/PlaylistCreationRequest.cs b/MediaBrowser.Model/Playlists/PlaylistCreationRequest.cs
index b7003c4c8..ef435b21e 100644
--- a/MediaBrowser.Model/Playlists/PlaylistCreationRequest.cs
+++ b/MediaBrowser.Model/Playlists/PlaylistCreationRequest.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/Playlists/PlaylistCreationResult.cs b/MediaBrowser.Model/Playlists/PlaylistCreationResult.cs
index 4f2067b98..f3a1518ed 100644
--- a/MediaBrowser.Model/Playlists/PlaylistCreationResult.cs
+++ b/MediaBrowser.Model/Playlists/PlaylistCreationResult.cs
@@ -4,6 +4,11 @@ namespace MediaBrowser.Model.Playlists
{
public class PlaylistCreationResult
{
- public string Id { get; set; }
+ public PlaylistCreationResult(string id)
+ {
+ Id = id;
+ }
+
+ public string Id { get; }
}
}
diff --git a/MediaBrowser.Model/Playlists/PlaylistItemQuery.cs b/MediaBrowser.Model/Playlists/PlaylistItemQuery.cs
deleted file mode 100644
index 324a38e70..000000000
--- a/MediaBrowser.Model/Playlists/PlaylistItemQuery.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-#pragma warning disable CS1591
-
-using MediaBrowser.Model.Querying;
-
-namespace MediaBrowser.Model.Playlists
-{
- public class PlaylistItemQuery
- {
- /// <summary>
- /// Gets or sets the identifier.
- /// </summary>
- /// <value>The identifier.</value>
- public string Id { get; set; }
-
- /// <summary>
- /// Gets or sets the user identifier.
- /// </summary>
- /// <value>The user identifier.</value>
- public string UserId { get; set; }
-
- /// <summary>
- /// Gets or sets the start index.
- /// </summary>
- /// <value>The start index.</value>
- public int? StartIndex { get; set; }
-
- /// <summary>
- /// Gets or sets the limit.
- /// </summary>
- /// <value>The limit.</value>
- public int? Limit { get; set; }
-
- /// <summary>
- /// Gets or sets the fields.
- /// </summary>
- /// <value>The fields.</value>
- public ItemFields[] Fields { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Plugins/PluginInfo.cs b/MediaBrowser.Model/Plugins/PluginInfo.cs
index 9ff9ea457..c13f1a89f 100644
--- a/MediaBrowser.Model/Plugins/PluginInfo.cs
+++ b/MediaBrowser.Model/Plugins/PluginInfo.cs
@@ -1,3 +1,4 @@
+#nullable disable
namespace MediaBrowser.Model.Plugins
{
/// <summary>
diff --git a/MediaBrowser.Model/Plugins/PluginPageInfo.cs b/MediaBrowser.Model/Plugins/PluginPageInfo.cs
index eb6a1527d..ca72e19ee 100644
--- a/MediaBrowser.Model/Plugins/PluginPageInfo.cs
+++ b/MediaBrowser.Model/Plugins/PluginPageInfo.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
namespace MediaBrowser.Model.Plugins
diff --git a/MediaBrowser.Model/Providers/ExternalIdInfo.cs b/MediaBrowser.Model/Providers/ExternalIdInfo.cs
index 2b481ad7e..f2e6d8ef3 100644
--- a/MediaBrowser.Model/Providers/ExternalIdInfo.cs
+++ b/MediaBrowser.Model/Providers/ExternalIdInfo.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
namespace MediaBrowser.Model.Providers
diff --git a/MediaBrowser.Model/Providers/ExternalUrl.cs b/MediaBrowser.Model/Providers/ExternalUrl.cs
index d4f4fa840..9467a2b00 100644
--- a/MediaBrowser.Model/Providers/ExternalUrl.cs
+++ b/MediaBrowser.Model/Providers/ExternalUrl.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
namespace MediaBrowser.Model.Providers
diff --git a/MediaBrowser.Model/Providers/ImageProviderInfo.cs b/MediaBrowser.Model/Providers/ImageProviderInfo.cs
index a22ec3c07..19af81c85 100644
--- a/MediaBrowser.Model/Providers/ImageProviderInfo.cs
+++ b/MediaBrowser.Model/Providers/ImageProviderInfo.cs
@@ -1,6 +1,3 @@
-#pragma warning disable CS1591
-
-using System;
using MediaBrowser.Model.Entities;
namespace MediaBrowser.Model.Providers
@@ -11,16 +8,25 @@ namespace MediaBrowser.Model.Providers
public class ImageProviderInfo
{
/// <summary>
- /// Gets or sets the name.
+ /// Initializes a new instance of the <see cref="ImageProviderInfo" /> class.
/// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
-
- public ImageType[] SupportedImages { get; set; }
-
- public ImageProviderInfo()
+ /// <param name="name">The name of the image provider.</param>
+ /// <param name="supportedImages">The image types supported by the image provider.</param>
+ public ImageProviderInfo(string name, ImageType[] supportedImages)
{
- SupportedImages = Array.Empty<ImageType>();
+ Name = name;
+ SupportedImages = supportedImages;
}
+
+ /// <summary>
+ /// Gets the name.
+ /// </summary>
+ /// <value>The name.</value>
+ public string Name { get; }
+
+ /// <summary>
+ /// Gets the supported image types.
+ /// </summary>
+ public ImageType[] SupportedImages { get; }
}
}
diff --git a/MediaBrowser.Model/Providers/RemoteImageInfo.cs b/MediaBrowser.Model/Providers/RemoteImageInfo.cs
index ee2b9d8fd..78ab6c706 100644
--- a/MediaBrowser.Model/Providers/RemoteImageInfo.cs
+++ b/MediaBrowser.Model/Providers/RemoteImageInfo.cs
@@ -1,3 +1,4 @@
+#nullable disable
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
diff --git a/MediaBrowser.Model/Providers/RemoteImageQuery.cs b/MediaBrowser.Model/Providers/RemoteImageQuery.cs
index 2873c1003..b7fad87ab 100644
--- a/MediaBrowser.Model/Providers/RemoteImageQuery.cs
+++ b/MediaBrowser.Model/Providers/RemoteImageQuery.cs
@@ -6,7 +6,12 @@ namespace MediaBrowser.Model.Providers
{
public class RemoteImageQuery
{
- public string ProviderName { get; set; }
+ public RemoteImageQuery(string providerName)
+ {
+ ProviderName = providerName;
+ }
+
+ public string ProviderName { get; }
public ImageType? ImageType { get; set; }
diff --git a/MediaBrowser.Model/Providers/RemoteImageResult.cs b/MediaBrowser.Model/Providers/RemoteImageResult.cs
index 5ca00f770..e6067ee6e 100644
--- a/MediaBrowser.Model/Providers/RemoteImageResult.cs
+++ b/MediaBrowser.Model/Providers/RemoteImageResult.cs
@@ -1,3 +1,4 @@
+#nullable disable
namespace MediaBrowser.Model.Providers
{
/// <summary>
diff --git a/MediaBrowser.Model/Providers/RemoteSearchResult.cs b/MediaBrowser.Model/Providers/RemoteSearchResult.cs
index 161e04821..c96eb0b59 100644
--- a/MediaBrowser.Model/Providers/RemoteSearchResult.cs
+++ b/MediaBrowser.Model/Providers/RemoteSearchResult.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
@@ -8,23 +9,34 @@ namespace MediaBrowser.Model.Providers
{
public class RemoteSearchResult : IHasProviderIds
{
+ public RemoteSearchResult()
+ {
+ ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+ Artists = Array.Empty<RemoteSearchResult>();
+ }
+
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
public string Name { get; set; }
+
/// <summary>
/// Gets or sets the provider ids.
/// </summary>
/// <value>The provider ids.</value>
public Dictionary<string, string> ProviderIds { get; set; }
+
/// <summary>
/// Gets or sets the year.
/// </summary>
/// <value>The year.</value>
public int? ProductionYear { get; set; }
+
public int? IndexNumber { get; set; }
+
public int? IndexNumberEnd { get; set; }
+
public int? ParentIndexNumber { get; set; }
public DateTime? PremiereDate { get; set; }
@@ -32,15 +44,13 @@ namespace MediaBrowser.Model.Providers
public string ImageUrl { get; set; }
public string SearchProviderName { get; set; }
+
public string Overview { get; set; }
public RemoteSearchResult AlbumArtist { get; set; }
+
public RemoteSearchResult[] Artists { get; set; }
- public RemoteSearchResult()
- {
- ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- Artists = new RemoteSearchResult[] { };
- }
+
}
}
diff --git a/MediaBrowser.Model/Providers/RemoteSubtitleInfo.cs b/MediaBrowser.Model/Providers/RemoteSubtitleInfo.cs
index 06f29df3f..d9f7a852c 100644
--- a/MediaBrowser.Model/Providers/RemoteSubtitleInfo.cs
+++ b/MediaBrowser.Model/Providers/RemoteSubtitleInfo.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/Providers/SubtitleOptions.cs b/MediaBrowser.Model/Providers/SubtitleOptions.cs
index 9e6049246..c07379570 100644
--- a/MediaBrowser.Model/Providers/SubtitleOptions.cs
+++ b/MediaBrowser.Model/Providers/SubtitleOptions.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/Providers/SubtitleProviderInfo.cs b/MediaBrowser.Model/Providers/SubtitleProviderInfo.cs
index fca93d176..ee25be4b6 100644
--- a/MediaBrowser.Model/Providers/SubtitleProviderInfo.cs
+++ b/MediaBrowser.Model/Providers/SubtitleProviderInfo.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
namespace MediaBrowser.Model.Providers
diff --git a/MediaBrowser.Model/Querying/AllThemeMediaResult.cs b/MediaBrowser.Model/Querying/AllThemeMediaResult.cs
index a264c6178..6b503ba6b 100644
--- a/MediaBrowser.Model/Querying/AllThemeMediaResult.cs
+++ b/MediaBrowser.Model/Querying/AllThemeMediaResult.cs
@@ -1,15 +1,10 @@
+#nullable disable
#pragma warning disable CS1591
namespace MediaBrowser.Model.Querying
{
public class AllThemeMediaResult
{
- public ThemeMediaResult ThemeVideosResult { get; set; }
-
- public ThemeMediaResult ThemeSongsResult { get; set; }
-
- public ThemeMediaResult SoundtrackSongsResult { get; set; }
-
public AllThemeMediaResult()
{
ThemeVideosResult = new ThemeMediaResult();
@@ -18,5 +13,11 @@ namespace MediaBrowser.Model.Querying
SoundtrackSongsResult = new ThemeMediaResult();
}
+
+ public ThemeMediaResult ThemeVideosResult { get; set; }
+
+ public ThemeMediaResult ThemeSongsResult { get; set; }
+
+ public ThemeMediaResult SoundtrackSongsResult { get; set; }
}
}
diff --git a/MediaBrowser.Model/Querying/EpisodeQuery.cs b/MediaBrowser.Model/Querying/EpisodeQuery.cs
index 6fb4df676..13b1a0dcb 100644
--- a/MediaBrowser.Model/Querying/EpisodeQuery.cs
+++ b/MediaBrowser.Model/Querying/EpisodeQuery.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/Querying/ItemCountsQuery.cs b/MediaBrowser.Model/Querying/ItemCountsQuery.cs
deleted file mode 100644
index f113cf380..000000000
--- a/MediaBrowser.Model/Querying/ItemCountsQuery.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-namespace MediaBrowser.Model.Querying
-{
- /// <summary>
- /// Class ItemCountsQuery.
- /// </summary>
- public class ItemCountsQuery
- {
- /// <summary>
- /// Gets or sets the user id.
- /// </summary>
- /// <value>The user id.</value>
- public string UserId { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is favorite.
- /// </summary>
- /// <value><c>null</c> if [is favorite] contains no value, <c>true</c> if [is favorite]; otherwise, <c>false</c>.</value>
- public bool? IsFavorite { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Querying/ItemSortBy.cs b/MediaBrowser.Model/Querying/ItemSortBy.cs
index 15b60ad84..edf71c1a7 100644
--- a/MediaBrowser.Model/Querying/ItemSortBy.cs
+++ b/MediaBrowser.Model/Querying/ItemSortBy.cs
@@ -8,73 +8,99 @@ namespace MediaBrowser.Model.Querying
public static class ItemSortBy
{
public const string AiredEpisodeOrder = "AiredEpisodeOrder";
+
/// <summary>
- /// The album
+ /// The album.
/// </summary>
public const string Album = "Album";
+
/// <summary>
- /// The album artist
+ /// The album artist.
/// </summary>
public const string AlbumArtist = "AlbumArtist";
+
/// <summary>
- /// The artist
+ /// The artist.
/// </summary>
public const string Artist = "Artist";
+
/// <summary>
- /// The date created
+ /// The date created.
/// </summary>
public const string DateCreated = "DateCreated";
+
/// <summary>
- /// The official rating
+ /// The official rating.
/// </summary>
public const string OfficialRating = "OfficialRating";
+
/// <summary>
- /// The date played
+ /// The date played.
/// </summary>
public const string DatePlayed = "DatePlayed";
+
/// <summary>
- /// The premiere date
+ /// The premiere date.
/// </summary>
public const string PremiereDate = "PremiereDate";
+
public const string StartDate = "StartDate";
+
/// <summary>
- /// The sort name
+ /// The sort name.
/// </summary>
public const string SortName = "SortName";
+
public const string Name = "Name";
+
/// <summary>
- /// The random
+ /// The random.
/// </summary>
public const string Random = "Random";
+
/// <summary>
- /// The runtime
+ /// The runtime.
/// </summary>
public const string Runtime = "Runtime";
+
/// <summary>
- /// The community rating
+ /// The community rating.
/// </summary>
public const string CommunityRating = "CommunityRating";
+
/// <summary>
- /// The production year
+ /// The production year.
/// </summary>
public const string ProductionYear = "ProductionYear";
+
/// <summary>
- /// The play count
+ /// The play count.
/// </summary>
public const string PlayCount = "PlayCount";
+
/// <summary>
- /// The critic rating
+ /// The critic rating.
/// </summary>
public const string CriticRating = "CriticRating";
+
public const string IsFolder = "IsFolder";
+
public const string IsUnplayed = "IsUnplayed";
+
public const string IsPlayed = "IsPlayed";
+
public const string SeriesSortName = "SeriesSortName";
+
public const string VideoBitRate = "VideoBitRate";
+
public const string AirTime = "AirTime";
+
public const string Studio = "Studio";
+
public const string IsFavoriteOrLiked = "IsFavoriteOrLiked";
+
public const string DateLastContentAdded = "DateLastContentAdded";
+
public const string SeriesDatePlayed = "SeriesDatePlayed";
}
}
diff --git a/MediaBrowser.Model/Querying/LatestItemsQuery.cs b/MediaBrowser.Model/Querying/LatestItemsQuery.cs
index 84e29e76a..7954ef4b4 100644
--- a/MediaBrowser.Model/Querying/LatestItemsQuery.cs
+++ b/MediaBrowser.Model/Querying/LatestItemsQuery.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
@@ -7,8 +8,13 @@ namespace MediaBrowser.Model.Querying
{
public class LatestItemsQuery
{
+ public LatestItemsQuery()
+ {
+ EnableImageTypes = Array.Empty<ImageType>();
+ }
+
/// <summary>
- /// The user to localize search results for
+ /// The user to localize search results for.
/// </summary>
/// <value>The user id.</value>
public Guid UserId { get; set; }
@@ -26,13 +32,13 @@ namespace MediaBrowser.Model.Querying
public int? StartIndex { get; set; }
/// <summary>
- /// The maximum number of items to return
+ /// The maximum number of items to return.
/// </summary>
/// <value>The limit.</value>
public int? Limit { get; set; }
/// <summary>
- /// Fields to return within the items, in addition to basic information
+ /// Fields to return within the items, in addition to basic information.
/// </summary>
/// <value>The fields.</value>
public ItemFields[] Fields { get; set; }
@@ -54,25 +60,23 @@ namespace MediaBrowser.Model.Querying
/// </summary>
/// <value><c>true</c> if [group items]; otherwise, <c>false</c>.</value>
public bool GroupItems { get; set; }
+
/// <summary>
/// Gets or sets a value indicating whether [enable images].
/// </summary>
/// <value><c>null</c> if [enable images] contains no value, <c>true</c> if [enable images]; otherwise, <c>false</c>.</value>
public bool? EnableImages { get; set; }
+
/// <summary>
/// Gets or sets the image type limit.
/// </summary>
/// <value>The image type limit.</value>
public int? ImageTypeLimit { get; set; }
+
/// <summary>
/// Gets or sets the enable image types.
/// </summary>
/// <value>The enable image types.</value>
public ImageType[] EnableImageTypes { get; set; }
-
- public LatestItemsQuery()
- {
- EnableImageTypes = new ImageType[] { };
- }
}
}
diff --git a/MediaBrowser.Model/Querying/MovieRecommendationQuery.cs b/MediaBrowser.Model/Querying/MovieRecommendationQuery.cs
index 93de0a8cd..1c8875890 100644
--- a/MediaBrowser.Model/Querying/MovieRecommendationQuery.cs
+++ b/MediaBrowser.Model/Querying/MovieRecommendationQuery.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/Querying/NextUpQuery.cs b/MediaBrowser.Model/Querying/NextUpQuery.cs
index 1543aea16..0df86cb22 100644
--- a/MediaBrowser.Model/Querying/NextUpQuery.cs
+++ b/MediaBrowser.Model/Querying/NextUpQuery.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/Querying/QueryFilters.cs b/MediaBrowser.Model/Querying/QueryFilters.cs
index 8d879c174..e04208f76 100644
--- a/MediaBrowser.Model/Querying/QueryFilters.cs
+++ b/MediaBrowser.Model/Querying/QueryFilters.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/Querying/QueryResult.cs b/MediaBrowser.Model/Querying/QueryResult.cs
index 266f1c7e6..42586243d 100644
--- a/MediaBrowser.Model/Querying/QueryResult.cs
+++ b/MediaBrowser.Model/Querying/QueryResult.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/Querying/ThemeMediaResult.cs b/MediaBrowser.Model/Querying/ThemeMediaResult.cs
index bae954d78..5afedeeaf 100644
--- a/MediaBrowser.Model/Querying/ThemeMediaResult.cs
+++ b/MediaBrowser.Model/Querying/ThemeMediaResult.cs
@@ -4,7 +4,7 @@ using MediaBrowser.Model.Dto;
namespace MediaBrowser.Model.Querying
{
/// <summary>
- /// Class ThemeMediaResult
+ /// Class ThemeMediaResult.
/// </summary>
public class ThemeMediaResult : QueryResult<BaseItemDto>
{
diff --git a/MediaBrowser.Model/Querying/UpcomingEpisodesQuery.cs b/MediaBrowser.Model/Querying/UpcomingEpisodesQuery.cs
index 123d0fad2..ed1aa7ac6 100644
--- a/MediaBrowser.Model/Querying/UpcomingEpisodesQuery.cs
+++ b/MediaBrowser.Model/Querying/UpcomingEpisodesQuery.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using MediaBrowser.Model.Entities;
diff --git a/MediaBrowser.Model/Search/SearchHint.cs b/MediaBrowser.Model/Search/SearchHint.cs
index 6e52314fa..c7a721df6 100644
--- a/MediaBrowser.Model/Search/SearchHint.cs
+++ b/MediaBrowser.Model/Search/SearchHint.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/Search/SearchHintResult.cs b/MediaBrowser.Model/Search/SearchHintResult.cs
index 3c4fbec9e..92ba4139e 100644
--- a/MediaBrowser.Model/Search/SearchHintResult.cs
+++ b/MediaBrowser.Model/Search/SearchHintResult.cs
@@ -1,3 +1,4 @@
+#nullable disable
namespace MediaBrowser.Model.Search
{
/// <summary>
diff --git a/MediaBrowser.Model/Search/SearchQuery.cs b/MediaBrowser.Model/Search/SearchQuery.cs
index 8a018312e..4470f1ad9 100644
--- a/MediaBrowser.Model/Search/SearchQuery.cs
+++ b/MediaBrowser.Model/Search/SearchQuery.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/Serialization/IJsonSerializer.cs b/MediaBrowser.Model/Serialization/IJsonSerializer.cs
index 6223bb559..09b6ff9b5 100644
--- a/MediaBrowser.Model/Serialization/IJsonSerializer.cs
+++ b/MediaBrowser.Model/Serialization/IJsonSerializer.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/Serialization/IXmlSerializer.cs b/MediaBrowser.Model/Serialization/IXmlSerializer.cs
index 1edd98fad..16d126ac7 100644
--- a/MediaBrowser.Model/Serialization/IXmlSerializer.cs
+++ b/MediaBrowser.Model/Serialization/IXmlSerializer.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/Services/ApiMemberAttribute.cs b/MediaBrowser.Model/Services/ApiMemberAttribute.cs
index 8e50836f4..7c23eee44 100644
--- a/MediaBrowser.Model/Services/ApiMemberAttribute.cs
+++ b/MediaBrowser.Model/Services/ApiMemberAttribute.cs
@@ -1,3 +1,4 @@
+#nullable disable
using System;
namespace MediaBrowser.Model.Services
diff --git a/MediaBrowser.Model/Services/IHasRequestFilter.cs b/MediaBrowser.Model/Services/IHasRequestFilter.cs
index 3e49e9872..b83d3b075 100644
--- a/MediaBrowser.Model/Services/IHasRequestFilter.cs
+++ b/MediaBrowser.Model/Services/IHasRequestFilter.cs
@@ -7,7 +7,7 @@ namespace MediaBrowser.Model.Services
public interface IHasRequestFilter
{
/// <summary>
- /// Order in which Request Filters are executed.
+ /// Gets the order in which Request Filters are executed.
/// &lt;0 Executed before global request filters.
/// &gt;0 Executed after global request filters.
/// </summary>
diff --git a/MediaBrowser.Model/Services/IHttpRequest.cs b/MediaBrowser.Model/Services/IHttpRequest.cs
index 4dccd2d68..3ea65195c 100644
--- a/MediaBrowser.Model/Services/IHttpRequest.cs
+++ b/MediaBrowser.Model/Services/IHttpRequest.cs
@@ -5,12 +5,12 @@ namespace MediaBrowser.Model.Services
public interface IHttpRequest : IRequest
{
/// <summary>
- /// The HTTP Verb
+ /// Gets the HTTP Verb.
/// </summary>
string HttpMethod { get; }
/// <summary>
- /// The value of the Accept HTTP Request Header
+ /// Gets the value of the Accept HTTP Request Header.
/// </summary>
string Accept { get; }
}
diff --git a/MediaBrowser.Model/Services/IHttpResult.cs b/MediaBrowser.Model/Services/IHttpResult.cs
index b153f15ec..abc581d8e 100644
--- a/MediaBrowser.Model/Services/IHttpResult.cs
+++ b/MediaBrowser.Model/Services/IHttpResult.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System.Net;
@@ -7,27 +8,27 @@ namespace MediaBrowser.Model.Services
public interface IHttpResult : IHasHeaders
{
/// <summary>
- /// The HTTP Response Status
+ /// The HTTP Response Status.
/// </summary>
int Status { get; set; }
/// <summary>
- /// The HTTP Response Status Code
+ /// The HTTP Response Status Code.
/// </summary>
HttpStatusCode StatusCode { get; set; }
/// <summary>
- /// The HTTP Response ContentType
+ /// The HTTP Response ContentType.
/// </summary>
string ContentType { get; set; }
/// <summary>
- /// Response DTO
+ /// Response DTO.
/// </summary>
object Response { get; set; }
/// <summary>
- /// Holds the request call context
+ /// Holds the request call context.
/// </summary>
IRequest RequestContext { get; set; }
}
diff --git a/MediaBrowser.Model/Services/IRequest.cs b/MediaBrowser.Model/Services/IRequest.cs
index 3f4edced6..f413f1e17 100644
--- a/MediaBrowser.Model/Services/IRequest.cs
+++ b/MediaBrowser.Model/Services/IRequest.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/Services/QueryParamCollection.cs b/MediaBrowser.Model/Services/QueryParamCollection.cs
index 19e9e53e7..d07ff1548 100644
--- a/MediaBrowser.Model/Services/QueryParamCollection.cs
+++ b/MediaBrowser.Model/Services/QueryParamCollection.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
@@ -19,11 +20,6 @@ namespace MediaBrowser.Model.Services
return StringComparison.OrdinalIgnoreCase;
}
- private static StringComparer GetStringComparer()
- {
- return StringComparer.OrdinalIgnoreCase;
- }
-
/// <summary>
/// Adds a new query parameter.
/// </summary>
diff --git a/MediaBrowser.Model/Services/RouteAttribute.cs b/MediaBrowser.Model/Services/RouteAttribute.cs
index 197ba05e5..162576aa7 100644
--- a/MediaBrowser.Model/Services/RouteAttribute.cs
+++ b/MediaBrowser.Model/Services/RouteAttribute.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/Session/BrowseRequest.cs b/MediaBrowser.Model/Session/BrowseRequest.cs
index f485d680e..1c997d584 100644
--- a/MediaBrowser.Model/Session/BrowseRequest.cs
+++ b/MediaBrowser.Model/Session/BrowseRequest.cs
@@ -1,3 +1,4 @@
+#nullable disable
namespace MediaBrowser.Model.Session
{
/// <summary>
diff --git a/MediaBrowser.Model/Session/ClientCapabilities.cs b/MediaBrowser.Model/Session/ClientCapabilities.cs
index 5da4998e8..51db66d21 100644
--- a/MediaBrowser.Model/Session/ClientCapabilities.cs
+++ b/MediaBrowser.Model/Session/ClientCapabilities.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/Session/GeneralCommand.cs b/MediaBrowser.Model/Session/GeneralCommand.cs
index 980e1f88b..9794bd292 100644
--- a/MediaBrowser.Model/Session/GeneralCommand.cs
+++ b/MediaBrowser.Model/Session/GeneralCommand.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/Session/MessageCommand.cs b/MediaBrowser.Model/Session/MessageCommand.cs
index 473a7bccc..09abfbb3f 100644
--- a/MediaBrowser.Model/Session/MessageCommand.cs
+++ b/MediaBrowser.Model/Session/MessageCommand.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
namespace MediaBrowser.Model.Session
diff --git a/MediaBrowser.Model/Session/PlayRequest.cs b/MediaBrowser.Model/Session/PlayRequest.cs
index bdb2b2439..62b68b49e 100644
--- a/MediaBrowser.Model/Session/PlayRequest.cs
+++ b/MediaBrowser.Model/Session/PlayRequest.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/Session/PlaybackProgressInfo.cs b/MediaBrowser.Model/Session/PlaybackProgressInfo.cs
index 5687ba84b..6b4cfe4f0 100644
--- a/MediaBrowser.Model/Session/PlaybackProgressInfo.cs
+++ b/MediaBrowser.Model/Session/PlaybackProgressInfo.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/Session/PlaybackStopInfo.cs b/MediaBrowser.Model/Session/PlaybackStopInfo.cs
index f8cfacc20..b0827ac99 100644
--- a/MediaBrowser.Model/Session/PlaybackStopInfo.cs
+++ b/MediaBrowser.Model/Session/PlaybackStopInfo.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/Session/PlayerStateInfo.cs b/MediaBrowser.Model/Session/PlayerStateInfo.cs
index 0f9956873..0f10605ea 100644
--- a/MediaBrowser.Model/Session/PlayerStateInfo.cs
+++ b/MediaBrowser.Model/Session/PlayerStateInfo.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
namespace MediaBrowser.Model.Session
diff --git a/MediaBrowser.Model/Session/PlaystateRequest.cs b/MediaBrowser.Model/Session/PlaystateRequest.cs
index 493a8063a..ba2c024b7 100644
--- a/MediaBrowser.Model/Session/PlaystateRequest.cs
+++ b/MediaBrowser.Model/Session/PlaystateRequest.cs
@@ -12,6 +12,6 @@ namespace MediaBrowser.Model.Session
/// Gets or sets the controlling user identifier.
/// </summary>
/// <value>The controlling user identifier.</value>
- public string ControllingUserId { get; set; }
+ public string? ControllingUserId { get; set; }
}
}
diff --git a/MediaBrowser.Model/Session/SessionUserInfo.cs b/MediaBrowser.Model/Session/SessionUserInfo.cs
index 42a56b92b..4d6f35efc 100644
--- a/MediaBrowser.Model/Session/SessionUserInfo.cs
+++ b/MediaBrowser.Model/Session/SessionUserInfo.cs
@@ -1,3 +1,4 @@
+#nullable disable
using System;
namespace MediaBrowser.Model.Session
@@ -12,6 +13,7 @@ namespace MediaBrowser.Model.Session
/// </summary>
/// <value>The user identifier.</value>
public Guid UserId { get; set; }
+
/// <summary>
/// Gets or sets the name of the user.
/// </summary>
diff --git a/MediaBrowser.Model/Session/TranscodingInfo.cs b/MediaBrowser.Model/Session/TranscodingInfo.cs
index 8f4e688f0..d6dc83413 100644
--- a/MediaBrowser.Model/Session/TranscodingInfo.cs
+++ b/MediaBrowser.Model/Session/TranscodingInfo.cs
@@ -1,5 +1,8 @@
+#nullable disable
#pragma warning disable CS1591
+using System;
+
namespace MediaBrowser.Model.Session
{
public class TranscodingInfo
@@ -22,7 +25,7 @@ namespace MediaBrowser.Model.Session
public TranscodingInfo()
{
- TranscodeReasons = new TranscodeReason[] { };
+ TranscodeReasons = Array.Empty<TranscodeReason>();
}
}
diff --git a/MediaBrowser.Model/Session/UserDataChangeInfo.cs b/MediaBrowser.Model/Session/UserDataChangeInfo.cs
index 0872eb4b1..0fd24edcc 100644
--- a/MediaBrowser.Model/Session/UserDataChangeInfo.cs
+++ b/MediaBrowser.Model/Session/UserDataChangeInfo.cs
@@ -1,3 +1,4 @@
+#nullable disable
using MediaBrowser.Model.Dto;
namespace MediaBrowser.Model.Session
diff --git a/MediaBrowser.Model/Sync/SyncJob.cs b/MediaBrowser.Model/Sync/SyncJob.cs
index 30bf27f38..3cc9ff726 100644
--- a/MediaBrowser.Model/Sync/SyncJob.cs
+++ b/MediaBrowser.Model/Sync/SyncJob.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/Sync/SyncTarget.cs b/MediaBrowser.Model/Sync/SyncTarget.cs
index 20a0c8cc7..9e6bbbc00 100644
--- a/MediaBrowser.Model/Sync/SyncTarget.cs
+++ b/MediaBrowser.Model/Sync/SyncTarget.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
namespace MediaBrowser.Model.Sync
diff --git a/MediaBrowser.Model/SyncPlay/GroupInfoView.cs b/MediaBrowser.Model/SyncPlay/GroupInfoView.cs
new file mode 100644
index 000000000..f4c685998
--- /dev/null
+++ b/MediaBrowser.Model/SyncPlay/GroupInfoView.cs
@@ -0,0 +1,42 @@
+#nullable disable
+
+using System.Collections.Generic;
+
+namespace MediaBrowser.Model.SyncPlay
+{
+ /// <summary>
+ /// Class GroupInfoView.
+ /// </summary>
+ public class GroupInfoView
+ {
+ /// <summary>
+ /// Gets or sets the group identifier.
+ /// </summary>
+ /// <value>The group identifier.</value>
+ public string GroupId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the playing item id.
+ /// </summary>
+ /// <value>The playing item id.</value>
+ public string PlayingItemId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the playing item name.
+ /// </summary>
+ /// <value>The playing item name.</value>
+ public string PlayingItemName { get; set; }
+
+ /// <summary>
+ /// Gets or sets the position ticks.
+ /// </summary>
+ /// <value>The position ticks.</value>
+ public long PositionTicks { get; set; }
+
+ /// <summary>
+ /// Gets or sets the participants.
+ /// </summary>
+ /// <value>The participants.</value>
+ public IReadOnlyList<string> Participants { get; set; }
+ }
+}
diff --git a/MediaBrowser.Model/SyncPlay/GroupUpdate.cs b/MediaBrowser.Model/SyncPlay/GroupUpdate.cs
new file mode 100644
index 000000000..8c7208211
--- /dev/null
+++ b/MediaBrowser.Model/SyncPlay/GroupUpdate.cs
@@ -0,0 +1,28 @@
+#nullable disable
+
+namespace MediaBrowser.Model.SyncPlay
+{
+ /// <summary>
+ /// Class GroupUpdate.
+ /// </summary>
+ public class GroupUpdate<T>
+ {
+ /// <summary>
+ /// Gets or sets the group identifier.
+ /// </summary>
+ /// <value>The group identifier.</value>
+ public string GroupId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the update type.
+ /// </summary>
+ /// <value>The update type.</value>
+ public GroupUpdateType Type { get; set; }
+
+ /// <summary>
+ /// Gets or sets the data.
+ /// </summary>
+ /// <value>The data.</value>
+ public T Data { get; set; }
+ }
+}
diff --git a/MediaBrowser.Model/SyncPlay/GroupUpdateType.cs b/MediaBrowser.Model/SyncPlay/GroupUpdateType.cs
new file mode 100644
index 000000000..89d245787
--- /dev/null
+++ b/MediaBrowser.Model/SyncPlay/GroupUpdateType.cs
@@ -0,0 +1,53 @@
+namespace MediaBrowser.Model.SyncPlay
+{
+ /// <summary>
+ /// Enum GroupUpdateType.
+ /// </summary>
+ public enum GroupUpdateType
+ {
+ /// <summary>
+ /// The user-joined update. Tells members of a group about a new user.
+ /// </summary>
+ UserJoined,
+ /// <summary>
+ /// The user-left update. Tells members of a group that a user left.
+ /// </summary>
+ UserLeft,
+ /// <summary>
+ /// The group-joined update. Tells a user that the group has been joined.
+ /// </summary>
+ GroupJoined,
+ /// <summary>
+ /// The group-left update. Tells a user that the group has been left.
+ /// </summary>
+ GroupLeft,
+ /// <summary>
+ /// The group-wait update. Tells members of the group that a user is buffering.
+ /// </summary>
+ GroupWait,
+ /// <summary>
+ /// The prepare-session update. Tells a user to load some content.
+ /// </summary>
+ PrepareSession,
+ /// <summary>
+ /// The not-in-group error. Tells a user that they don't belong to a group.
+ /// </summary>
+ NotInGroup,
+ /// <summary>
+ /// The group-does-not-exist error. Sent when trying to join a non-existing group.
+ /// </summary>
+ GroupDoesNotExist,
+ /// <summary>
+ /// The create-group-denied error. Sent when a user tries to create a group without required permissions.
+ /// </summary>
+ CreateGroupDenied,
+ /// <summary>
+ /// The join-group-denied error. Sent when a user tries to join a group without required permissions.
+ /// </summary>
+ JoinGroupDenied,
+ /// <summary>
+ /// The library-access-denied error. Sent when a user tries to join a group without required access to the library.
+ /// </summary>
+ LibraryAccessDenied
+ }
+}
diff --git a/MediaBrowser.Model/SyncPlay/JoinGroupRequest.cs b/MediaBrowser.Model/SyncPlay/JoinGroupRequest.cs
new file mode 100644
index 000000000..d67b6bd55
--- /dev/null
+++ b/MediaBrowser.Model/SyncPlay/JoinGroupRequest.cs
@@ -0,0 +1,22 @@
+using System;
+
+namespace MediaBrowser.Model.SyncPlay
+{
+ /// <summary>
+ /// Class JoinGroupRequest.
+ /// </summary>
+ public class JoinGroupRequest
+ {
+ /// <summary>
+ /// Gets or sets the Group id.
+ /// </summary>
+ /// <value>The Group id to join.</value>
+ public Guid GroupId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the playing item id.
+ /// </summary>
+ /// <value>The client's currently playing item id.</value>
+ public Guid PlayingItemId { get; set; }
+ }
+}
diff --git a/MediaBrowser.Model/SyncPlay/PlaybackRequest.cs b/MediaBrowser.Model/SyncPlay/PlaybackRequest.cs
new file mode 100644
index 000000000..9de23194e
--- /dev/null
+++ b/MediaBrowser.Model/SyncPlay/PlaybackRequest.cs
@@ -0,0 +1,34 @@
+using System;
+
+namespace MediaBrowser.Model.SyncPlay
+{
+ /// <summary>
+ /// Class PlaybackRequest.
+ /// </summary>
+ public class PlaybackRequest
+ {
+ /// <summary>
+ /// Gets or sets the request type.
+ /// </summary>
+ /// <value>The request type.</value>
+ public PlaybackRequestType Type { get; set; }
+
+ /// <summary>
+ /// Gets or sets when the request has been made by the client.
+ /// </summary>
+ /// <value>The date of the request.</value>
+ public DateTime? When { get; set; }
+
+ /// <summary>
+ /// Gets or sets the position ticks.
+ /// </summary>
+ /// <value>The position ticks.</value>
+ public long? PositionTicks { get; set; }
+
+ /// <summary>
+ /// Gets or sets the ping time.
+ /// </summary>
+ /// <value>The ping time.</value>
+ public long? Ping { get; set; }
+ }
+}
diff --git a/MediaBrowser.Model/SyncPlay/PlaybackRequestType.cs b/MediaBrowser.Model/SyncPlay/PlaybackRequestType.cs
new file mode 100644
index 000000000..f1e175fde
--- /dev/null
+++ b/MediaBrowser.Model/SyncPlay/PlaybackRequestType.cs
@@ -0,0 +1,33 @@
+namespace MediaBrowser.Model.SyncPlay
+{
+ /// <summary>
+ /// Enum PlaybackRequestType
+ /// </summary>
+ public enum PlaybackRequestType
+ {
+ /// <summary>
+ /// A user is requesting a play command for the group.
+ /// </summary>
+ Play = 0,
+ /// <summary>
+ /// A user is requesting a pause command for the group.
+ /// </summary>
+ Pause = 1,
+ /// <summary>
+ /// A user is requesting a seek command for the group.
+ /// </summary>
+ Seek = 2,
+ /// <summary>
+ /// A user is signaling that playback is buffering.
+ /// </summary>
+ Buffering = 3,
+ /// <summary>
+ /// A user is signaling that playback resumed.
+ /// </summary>
+ BufferingDone = 4,
+ /// <summary>
+ /// A user is reporting its ping.
+ /// </summary>
+ UpdatePing = 5
+ }
+}
diff --git a/MediaBrowser.Model/SyncPlay/SendCommand.cs b/MediaBrowser.Model/SyncPlay/SendCommand.cs
new file mode 100644
index 000000000..0f0be0152
--- /dev/null
+++ b/MediaBrowser.Model/SyncPlay/SendCommand.cs
@@ -0,0 +1,40 @@
+#nullable disable
+
+namespace MediaBrowser.Model.SyncPlay
+{
+ /// <summary>
+ /// Class SendCommand.
+ /// </summary>
+ public class SendCommand
+ {
+ /// <summary>
+ /// Gets or sets the group identifier.
+ /// </summary>
+ /// <value>The group identifier.</value>
+ public string GroupId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the UTC time when to execute the command.
+ /// </summary>
+ /// <value>The UTC time when to execute the command.</value>
+ public string When { get; set; }
+
+ /// <summary>
+ /// Gets or sets the position ticks.
+ /// </summary>
+ /// <value>The position ticks.</value>
+ public long? PositionTicks { get; set; }
+
+ /// <summary>
+ /// Gets or sets the command.
+ /// </summary>
+ /// <value>The command.</value>
+ public SendCommandType Command { get; set; }
+
+ /// <summary>
+ /// Gets or sets the UTC time when this command has been emitted.
+ /// </summary>
+ /// <value>The UTC time when this command has been emitted.</value>
+ public string EmittedAt { get; set; }
+ }
+}
diff --git a/MediaBrowser.Model/SyncPlay/SendCommandType.cs b/MediaBrowser.Model/SyncPlay/SendCommandType.cs
new file mode 100644
index 000000000..113719871
--- /dev/null
+++ b/MediaBrowser.Model/SyncPlay/SendCommandType.cs
@@ -0,0 +1,21 @@
+namespace MediaBrowser.Model.SyncPlay
+{
+ /// <summary>
+ /// Enum SendCommandType.
+ /// </summary>
+ public enum SendCommandType
+ {
+ /// <summary>
+ /// The play command. Instructs users to start playback.
+ /// </summary>
+ Play = 0,
+ /// <summary>
+ /// The pause command. Instructs users to pause playback.
+ /// </summary>
+ Pause = 1,
+ /// <summary>
+ /// The seek command. Instructs users to seek to a specified time.
+ /// </summary>
+ Seek = 2
+ }
+}
diff --git a/MediaBrowser.Model/SyncPlay/UtcTimeResponse.cs b/MediaBrowser.Model/SyncPlay/UtcTimeResponse.cs
new file mode 100644
index 000000000..8ec5eaab3
--- /dev/null
+++ b/MediaBrowser.Model/SyncPlay/UtcTimeResponse.cs
@@ -0,0 +1,22 @@
+#nullable disable
+
+namespace MediaBrowser.Model.SyncPlay
+{
+ /// <summary>
+ /// Class UtcTimeResponse.
+ /// </summary>
+ public class UtcTimeResponse
+ {
+ /// <summary>
+ /// Gets or sets the UTC time when request has been received.
+ /// </summary>
+ /// <value>The UTC time when request has been received.</value>
+ public string RequestReceptionTime { get; set; }
+
+ /// <summary>
+ /// Gets or sets the UTC time when response has been sent.
+ /// </summary>
+ /// <value>The UTC time when response has been sent.</value>
+ public string ResponseTransmissionTime { get; set; }
+ }
+}
diff --git a/MediaBrowser.Model/System/LogFile.cs b/MediaBrowser.Model/System/LogFile.cs
index a2b701664..aec910c92 100644
--- a/MediaBrowser.Model/System/LogFile.cs
+++ b/MediaBrowser.Model/System/LogFile.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/System/PublicSystemInfo.cs b/MediaBrowser.Model/System/PublicSystemInfo.cs
index 1775470b5..b6196a43f 100644
--- a/MediaBrowser.Model/System/PublicSystemInfo.cs
+++ b/MediaBrowser.Model/System/PublicSystemInfo.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
namespace MediaBrowser.Model.System
diff --git a/MediaBrowser.Model/System/SystemInfo.cs b/MediaBrowser.Model/System/SystemInfo.cs
index f2c5aa1e3..a67c38c3a 100644
--- a/MediaBrowser.Model/System/SystemInfo.cs
+++ b/MediaBrowser.Model/System/SystemInfo.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
@@ -32,7 +33,6 @@ namespace MediaBrowser.Model.System
/// <value>The display name of the operating system.</value>
public string OperatingSystemDisplayName { get; set; }
-
/// <summary>
/// Get or sets the package name.
/// </summary>
diff --git a/MediaBrowser.Model/System/WakeOnLanInfo.cs b/MediaBrowser.Model/System/WakeOnLanInfo.cs
index 534ad19ec..b2cbe737d 100644
--- a/MediaBrowser.Model/System/WakeOnLanInfo.cs
+++ b/MediaBrowser.Model/System/WakeOnLanInfo.cs
@@ -8,35 +8,20 @@ namespace MediaBrowser.Model.System
public class WakeOnLanInfo
{
/// <summary>
- /// Returns the MAC address of the device.
- /// </summary>
- /// <value>The MAC address.</value>
- public string MacAddress { get; set; }
-
- /// <summary>
- /// Returns the wake-on-LAN port.
- /// </summary>
- /// <value>The wake-on-LAN port.</value>
- public int Port { get; set; }
-
- /// <summary>
/// Initializes a new instance of the <see cref="WakeOnLanInfo" /> class.
/// </summary>
/// <param name="macAddress">The MAC address.</param>
- public WakeOnLanInfo(PhysicalAddress macAddress)
+ public WakeOnLanInfo(PhysicalAddress macAddress) : this(macAddress.ToString())
{
- MacAddress = macAddress.ToString();
- Port = 9;
}
/// <summary>
/// Initializes a new instance of the <see cref="WakeOnLanInfo" /> class.
/// </summary>
/// <param name="macAddress">The MAC address.</param>
- public WakeOnLanInfo(string macAddress)
+ public WakeOnLanInfo(string macAddress) : this()
{
MacAddress = macAddress;
- Port = 9;
}
/// <summary>
@@ -46,5 +31,17 @@ namespace MediaBrowser.Model.System
{
Port = 9;
}
+
+ /// <summary>
+ /// Gets the MAC address of the device.
+ /// </summary>
+ /// <value>The MAC address.</value>
+ public string? MacAddress { get; set; }
+
+ /// <summary>
+ /// Gets or sets the wake-on-LAN port.
+ /// </summary>
+ /// <value>The wake-on-LAN port.</value>
+ public int Port { get; set; }
}
}
diff --git a/MediaBrowser.Model/Tasks/IScheduledTask.cs b/MediaBrowser.Model/Tasks/IScheduledTask.cs
index ed160e176..bf87088e4 100644
--- a/MediaBrowser.Model/Tasks/IScheduledTask.cs
+++ b/MediaBrowser.Model/Tasks/IScheduledTask.cs
@@ -1,5 +1,3 @@
-#pragma warning disable CS1591
-
using System;
using System.Collections.Generic;
using System.Threading;
@@ -8,16 +6,19 @@ using System.Threading.Tasks;
namespace MediaBrowser.Model.Tasks
{
/// <summary>
- /// Interface IScheduledTaskWorker
+ /// Interface IScheduledTaskWorker.
/// </summary>
public interface IScheduledTask
{
/// <summary>
- /// Gets the name of the task
+ /// Gets the name of the task.
/// </summary>
/// <value>The name.</value>
string Name { get; }
+ /// <summary>
+ /// Gets the key of the task.
+ /// </summary>
string Key { get; }
/// <summary>
@@ -33,7 +34,7 @@ namespace MediaBrowser.Model.Tasks
string Category { get; }
/// <summary>
- /// Executes the task
+ /// Executes the task.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="progress">The progress.</param>
diff --git a/MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs b/MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs
index 4dd1bb5d0..c79d7fe75 100644
--- a/MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs
+++ b/MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs
@@ -1,3 +1,4 @@
+#nullable disable
using System;
using MediaBrowser.Model.Events;
diff --git a/MediaBrowser.Model/Tasks/ScheduledTaskHelpers.cs b/MediaBrowser.Model/Tasks/ScheduledTaskHelpers.cs
index ca0743cca..9063903ae 100644
--- a/MediaBrowser.Model/Tasks/ScheduledTaskHelpers.cs
+++ b/MediaBrowser.Model/Tasks/ScheduledTaskHelpers.cs
@@ -14,9 +14,7 @@ namespace MediaBrowser.Model.Tasks
{
var isHidden = false;
- var configurableTask = task.ScheduledTask as IConfigurableScheduledTask;
-
- if (configurableTask != null)
+ if (task.ScheduledTask is IConfigurableScheduledTask configurableTask)
{
isHidden = configurableTask.IsHidden;
}
diff --git a/MediaBrowser.Model/Tasks/TaskCompletionEventArgs.cs b/MediaBrowser.Model/Tasks/TaskCompletionEventArgs.cs
index cc6c2b62b..48950667e 100644
--- a/MediaBrowser.Model/Tasks/TaskCompletionEventArgs.cs
+++ b/MediaBrowser.Model/Tasks/TaskCompletionEventArgs.cs
@@ -6,8 +6,14 @@ namespace MediaBrowser.Model.Tasks
{
public class TaskCompletionEventArgs : EventArgs
{
- public IScheduledTaskWorker Task { get; set; }
+ public TaskCompletionEventArgs(IScheduledTaskWorker task, TaskResult result)
+ {
+ Task = task;
+ Result = result;
+ }
- public TaskResult Result { get; set; }
+ public IScheduledTaskWorker Task { get; }
+
+ public TaskResult Result { get; }
}
}
diff --git a/MediaBrowser.Model/Tasks/TaskInfo.cs b/MediaBrowser.Model/Tasks/TaskInfo.cs
index 5144c035a..77100dfe7 100644
--- a/MediaBrowser.Model/Tasks/TaskInfo.cs
+++ b/MediaBrowser.Model/Tasks/TaskInfo.cs
@@ -1,3 +1,4 @@
+#nullable disable
using System;
namespace MediaBrowser.Model.Tasks
diff --git a/MediaBrowser.Model/Tasks/TaskResult.cs b/MediaBrowser.Model/Tasks/TaskResult.cs
index c6f92e7ed..31001aeb2 100644
--- a/MediaBrowser.Model/Tasks/TaskResult.cs
+++ b/MediaBrowser.Model/Tasks/TaskResult.cs
@@ -1,3 +1,4 @@
+#nullable disable
using System;
namespace MediaBrowser.Model.Tasks
diff --git a/MediaBrowser.Model/Tasks/TaskTriggerInfo.cs b/MediaBrowser.Model/Tasks/TaskTriggerInfo.cs
index 699e0ea3a..5aeaffc2b 100644
--- a/MediaBrowser.Model/Tasks/TaskTriggerInfo.cs
+++ b/MediaBrowser.Model/Tasks/TaskTriggerInfo.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/Updates/InstallationInfo.cs b/MediaBrowser.Model/Updates/InstallationInfo.cs
index e0d450d06..a6d80dba6 100644
--- a/MediaBrowser.Model/Updates/InstallationInfo.cs
+++ b/MediaBrowser.Model/Updates/InstallationInfo.cs
@@ -1,3 +1,4 @@
+#nullable disable
using System;
namespace MediaBrowser.Model.Updates
@@ -11,7 +12,7 @@ namespace MediaBrowser.Model.Updates
/// Gets or sets the guid.
/// </summary>
/// <value>The guid.</value>
- public string Guid { get; set; }
+ public Guid Guid { get; set; }
/// <summary>
/// Gets or sets the name.
@@ -23,6 +24,24 @@ namespace MediaBrowser.Model.Updates
/// Gets or sets the version.
/// </summary>
/// <value>The version.</value>
- public string Version { get; set; }
+ public Version Version { get; set; }
+
+ /// <summary>
+ /// Gets or sets the changelog for this version.
+ /// </summary>
+ /// <value>The changelog.</value>
+ public string Changelog { get; set; }
+
+ /// <summary>
+ /// Gets or sets the source URL.
+ /// </summary>
+ /// <value>The source URL.</value>
+ public string SourceUrl { get; set; }
+
+ /// <summary>
+ /// Gets or sets a checksum for the binary.
+ /// </summary>
+ /// <value>The checksum.</value>
+ public string Checksum { get; set; }
}
}
diff --git a/MediaBrowser.Model/Updates/PackageInfo.cs b/MediaBrowser.Model/Updates/PackageInfo.cs
index f5aa8b6fa..d9eb1386e 100644
--- a/MediaBrowser.Model/Updates/PackageInfo.cs
+++ b/MediaBrowser.Model/Updates/PackageInfo.cs
@@ -1,3 +1,4 @@
+#nullable disable
using System;
using System.Collections.Generic;
diff --git a/MediaBrowser.Model/Updates/VersionInfo.cs b/MediaBrowser.Model/Updates/VersionInfo.cs
index fe5826ad2..a4aa0e75f 100644
--- a/MediaBrowser.Model/Updates/VersionInfo.cs
+++ b/MediaBrowser.Model/Updates/VersionInfo.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
namespace MediaBrowser.Model.Updates
@@ -8,22 +10,10 @@ namespace MediaBrowser.Model.Updates
public class VersionInfo
{
/// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string name { get; set; }
-
- /// <summary>
- /// Gets or sets the guid.
- /// </summary>
- /// <value>The guid.</value>
- public string guid { get; set; }
-
- /// <summary>
/// Gets or sets the version.
/// </summary>
/// <value>The version.</value>
- public Version version { get; set; }
+ public string version { get; set; }
/// <summary>
/// Gets or sets the changelog for this version.
@@ -50,9 +40,9 @@ namespace MediaBrowser.Model.Updates
public string checksum { get; set; }
/// <summary>
- /// Gets or sets the target filename for the downloaded binary.
+ /// Gets or sets a timestamp of when the binary was built.
/// </summary>
- /// <value>The target filename.</value>
- public string filename { get; set; }
+ /// <value>The timestamp.</value>
+ public string timestamp { get; set; }
}
}
diff --git a/MediaBrowser.Model/Users/ForgotPasswordResult.cs b/MediaBrowser.Model/Users/ForgotPasswordResult.cs
index 368c642e8..6bb13d4c9 100644
--- a/MediaBrowser.Model/Users/ForgotPasswordResult.cs
+++ b/MediaBrowser.Model/Users/ForgotPasswordResult.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/Users/PinRedeemResult.cs b/MediaBrowser.Model/Users/PinRedeemResult.cs
index ab868cad4..7e4553bac 100644
--- a/MediaBrowser.Model/Users/PinRedeemResult.cs
+++ b/MediaBrowser.Model/Users/PinRedeemResult.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
namespace MediaBrowser.Model.Users
diff --git a/MediaBrowser.Model/Users/UserAction.cs b/MediaBrowser.Model/Users/UserAction.cs
index f6bb6451b..36b8e6ee5 100644
--- a/MediaBrowser.Model/Users/UserAction.cs
+++ b/MediaBrowser.Model/Users/UserAction.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.Model/Users/UserPolicy.cs b/MediaBrowser.Model/Users/UserPolicy.cs
index ae2b3fd4e..2fd27d3b9 100644
--- a/MediaBrowser.Model/Users/UserPolicy.cs
+++ b/MediaBrowser.Model/Users/UserPolicy.cs
@@ -1,7 +1,10 @@
+#nullable disable
#pragma warning disable CS1591
using System;
-using MediaBrowser.Model.Configuration;
+using System.Xml.Serialization;
+using Jellyfin.Data.Enums;
+using AccessSchedule = Jellyfin.Data.Entities.AccessSchedule;
namespace MediaBrowser.Model.Users
{
@@ -77,9 +80,17 @@ namespace MediaBrowser.Model.Users
public string[] BlockedChannels { get; set; }
public int RemoteClientBitrateLimit { get; set; }
+
+ [XmlElement(ElementName = "AuthenticationProviderId")]
public string AuthenticationProviderId { get; set; }
public string PasswordResetProviderId { get; set; }
+ /// <summary>
+ /// Gets or sets a value indicating what SyncPlay features the user can access.
+ /// </summary>
+ /// <value>Access level to SyncPlay features.</value>
+ public SyncPlayAccess SyncPlayAccess { get; set; }
+
public UserPolicy()
{
IsHidden = true;
@@ -125,6 +136,7 @@ namespace MediaBrowser.Model.Users
EnableContentDownloading = true;
EnablePublicSharing = true;
EnableRemoteAccess = true;
+ SyncPlayAccess = SyncPlayAccess.CreateAndJoinGroups;
}
}
}
diff --git a/MediaBrowser.Providers/Books/AudioBookMetadataService.cs b/MediaBrowser.Providers/Books/AudioBookMetadataService.cs
index 8eaeeea08..4ff42429e 100644
--- a/MediaBrowser.Providers/Books/AudioBookMetadataService.cs
+++ b/MediaBrowser.Providers/Books/AudioBookMetadataService.cs
@@ -25,7 +25,7 @@ namespace MediaBrowser.Providers.Books
protected override void MergeData(
MetadataResult<AudioBook> source,
MetadataResult<AudioBook> target,
- MetadataFields[] lockedFields,
+ MetadataField[] lockedFields,
bool replaceData,
bool mergeMetadataSettings)
{
diff --git a/MediaBrowser.Providers/Books/BookMetadataService.cs b/MediaBrowser.Providers/Books/BookMetadataService.cs
index 340641711..dcdf36f92 100644
--- a/MediaBrowser.Providers/Books/BookMetadataService.cs
+++ b/MediaBrowser.Providers/Books/BookMetadataService.cs
@@ -22,7 +22,7 @@ namespace MediaBrowser.Providers.Books
}
/// <inheritdoc />
- protected override void MergeData(MetadataResult<Book> source, MetadataResult<Book> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<Book> source, MetadataResult<Book> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
diff --git a/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs b/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs
index 3c9760ea7..46c9d6720 100644
--- a/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs
+++ b/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs
@@ -43,7 +43,7 @@ namespace MediaBrowser.Providers.BoxSets
}
/// <inheritdoc />
- protected override void MergeData(MetadataResult<BoxSet> source, MetadataResult<BoxSet> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<BoxSet> source, MetadataResult<BoxSet> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
diff --git a/MediaBrowser.Providers/Channels/ChannelMetadataService.cs b/MediaBrowser.Providers/Channels/ChannelMetadataService.cs
index 9afa82319..b6abadc85 100644
--- a/MediaBrowser.Providers/Channels/ChannelMetadataService.cs
+++ b/MediaBrowser.Providers/Channels/ChannelMetadataService.cs
@@ -22,7 +22,7 @@ namespace MediaBrowser.Providers.Channels
}
/// <inheritdoc />
- protected override void MergeData(MetadataResult<Channel> source, MetadataResult<Channel> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<Channel> source, MetadataResult<Channel> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}
diff --git a/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs b/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs
index 921222543..0d00db3eb 100644
--- a/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs
+++ b/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs
@@ -23,7 +23,7 @@ namespace MediaBrowser.Providers.Folders
}
/// <inheritdoc />
- protected override void MergeData(MetadataResult<CollectionFolder> source, MetadataResult<CollectionFolder> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<CollectionFolder> source, MetadataResult<CollectionFolder> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}
diff --git a/MediaBrowser.Providers/Folders/FolderMetadataService.cs b/MediaBrowser.Providers/Folders/FolderMetadataService.cs
index b6bd2515d..be731e5c2 100644
--- a/MediaBrowser.Providers/Folders/FolderMetadataService.cs
+++ b/MediaBrowser.Providers/Folders/FolderMetadataService.cs
@@ -26,7 +26,7 @@ namespace MediaBrowser.Providers.Folders
public override int Order => 10;
/// <inheritdoc />
- protected override void MergeData(MetadataResult<Folder> source, MetadataResult<Folder> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<Folder> source, MetadataResult<Folder> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}
diff --git a/MediaBrowser.Providers/Folders/UserViewMetadataService.cs b/MediaBrowser.Providers/Folders/UserViewMetadataService.cs
index 60ee81114..abb78fbc7 100644
--- a/MediaBrowser.Providers/Folders/UserViewMetadataService.cs
+++ b/MediaBrowser.Providers/Folders/UserViewMetadataService.cs
@@ -22,7 +22,7 @@ namespace MediaBrowser.Providers.Folders
}
/// <inheritdoc />
- protected override void MergeData(MetadataResult<UserView> source, MetadataResult<UserView> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<UserView> source, MetadataResult<UserView> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}
diff --git a/MediaBrowser.Providers/Genres/GenreMetadataService.cs b/MediaBrowser.Providers/Genres/GenreMetadataService.cs
index f3406c1ab..461f3fa2b 100644
--- a/MediaBrowser.Providers/Genres/GenreMetadataService.cs
+++ b/MediaBrowser.Providers/Genres/GenreMetadataService.cs
@@ -22,7 +22,7 @@ namespace MediaBrowser.Providers.Genres
}
/// <inheritdoc />
- protected override void MergeData(MetadataResult<Genre> source, MetadataResult<Genre> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<Genre> source, MetadataResult<Genre> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}
diff --git a/MediaBrowser.Providers/LiveTv/ProgramMetadataService.cs b/MediaBrowser.Providers/LiveTv/ProgramMetadataService.cs
index 7dd49c71a..ccf90d7ac 100644
--- a/MediaBrowser.Providers/LiveTv/ProgramMetadataService.cs
+++ b/MediaBrowser.Providers/LiveTv/ProgramMetadataService.cs
@@ -22,7 +22,7 @@ namespace MediaBrowser.Providers.LiveTv
}
/// <inheritdoc />
- protected override void MergeData(MetadataResult<LiveTvChannel> source, MetadataResult<LiveTvChannel> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<LiveTvChannel> source, MetadataResult<LiveTvChannel> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}
diff --git a/MediaBrowser.Providers/Manager/ImageSaver.cs b/MediaBrowser.Providers/Manager/ImageSaver.cs
index 3ab621ba4..3c94f6215 100644
--- a/MediaBrowser.Providers/Manager/ImageSaver.cs
+++ b/MediaBrowser.Providers/Manager/ImageSaver.cs
@@ -5,17 +5,21 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Net;
using Microsoft.Extensions.Logging;
+using Episode = MediaBrowser.Controller.Entities.TV.Episode;
+using MusicAlbum = MediaBrowser.Controller.Entities.Audio.MusicAlbum;
+using Person = MediaBrowser.Controller.Entities.Person;
+using Season = MediaBrowser.Controller.Entities.TV.Season;
namespace MediaBrowser.Providers.Manager
{
@@ -78,11 +82,6 @@ namespace MediaBrowser.Providers.Manager
var saveLocally = item.SupportsLocalMetadata && item.IsSaveLocalMetadataEnabled() && !item.ExtraType.HasValue && !(item is Audio);
- if (item is User)
- {
- saveLocally = true;
- }
-
if (type != ImageType.Primary && item is Episode)
{
saveLocally = false;
@@ -132,11 +131,11 @@ namespace MediaBrowser.Providers.Manager
var currentImage = GetCurrentImage(item, type, index);
var currentImageIsLocalFile = currentImage != null && currentImage.IsLocalFile;
- var currentImagePath = currentImage == null ? null : currentImage.Path;
+ var currentImagePath = currentImage?.Path;
var savedPaths = new List<string>();
- using (source)
+ await using (source)
{
var currentPathIndex = 0;
@@ -172,7 +171,6 @@ namespace MediaBrowser.Providers.Manager
}
catch (FileNotFoundException)
{
-
}
finally
{
@@ -181,6 +179,11 @@ namespace MediaBrowser.Providers.Manager
}
}
+ public async Task SaveImage(User user, Stream source, string path)
+ {
+ await SaveImageToLocation(source, path, path, CancellationToken.None).ConfigureAwait(false);
+ }
+
private async Task<string> SaveImageToLocation(Stream source, string path, string retryPath, CancellationToken cancellationToken)
{
try
@@ -244,7 +247,7 @@ namespace MediaBrowser.Providers.Manager
_fileSystem.SetAttributes(path, false, false);
- using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous))
+ await using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous))
{
await source.CopyToAsync(fs, IODefaults.CopyToBufferSize, cancellationToken).ConfigureAwait(false);
}
@@ -439,7 +442,6 @@ namespace MediaBrowser.Providers.Manager
{
path = Path.Combine(Path.GetDirectoryName(item.Path), "metadata", filename + extension);
}
-
else if (item.IsInMixedFolder)
{
path = GetSavePathForItemInMixedFolder(item, type, filename, extension);
diff --git a/MediaBrowser.Providers/Manager/ItemImageProvider.cs b/MediaBrowser.Providers/Manager/ItemImageProvider.cs
index 6ef0e44a2..48e1c94ad 100644
--- a/MediaBrowser.Providers/Manager/ItemImageProvider.cs
+++ b/MediaBrowser.Providers/Manager/ItemImageProvider.cs
@@ -230,7 +230,9 @@ namespace MediaBrowser.Providers.Manager
/// <param name="result">The result.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
- private async Task RefreshFromProvider(BaseItem item, LibraryOptions libraryOptions,
+ private async Task RefreshFromProvider(
+ BaseItem item,
+ LibraryOptions libraryOptions,
IRemoteImageProvider provider,
ImageRefreshOptions refreshOptions,
TypeOptions savedOptions,
@@ -256,20 +258,24 @@ namespace MediaBrowser.Providers.Manager
_logger.LogDebug("Running {0} for {1}", provider.GetType().Name, item.Path ?? item.Name);
- var images = await _providerManager.GetAvailableRemoteImages(item, new RemoteImageQuery
- {
- ProviderName = provider.Name,
- IncludeAllLanguages = false,
- IncludeDisabledProviders = false,
-
- }, cancellationToken).ConfigureAwait(false);
+ var images = await _providerManager.GetAvailableRemoteImages(
+ item,
+ new RemoteImageQuery(provider.Name)
+ {
+ IncludeAllLanguages = false,
+ IncludeDisabledProviders = false,
+ },
+ cancellationToken).ConfigureAwait(false);
var list = images.ToList();
int minWidth;
foreach (var imageType in _singularImages)
{
- if (!IsEnabled(savedOptions, imageType, item)) continue;
+ if (!IsEnabled(savedOptions, imageType, item))
+ {
+ continue;
+ }
if (!HasImage(item, imageType) || (refreshOptions.IsReplacingImage(imageType) && !downloadedImages.Contains(imageType)))
{
diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs
index c49aa407a..4ea711a2a 100644
--- a/MediaBrowser.Providers/Manager/MetadataService.cs
+++ b/MediaBrowser.Providers/Manager/MetadataService.cs
@@ -20,12 +20,12 @@ namespace MediaBrowser.Providers.Manager
where TIdType : ItemLookupInfo, new()
{
protected readonly IServerConfigurationManager ServerConfigurationManager;
- protected readonly ILogger Logger;
+ protected readonly ILogger<MetadataService<TItemType, TIdType>> Logger;
protected readonly IProviderManager ProviderManager;
protected readonly IFileSystem FileSystem;
protected readonly ILibraryManager LibraryManager;
- protected MetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, ILibraryManager libraryManager)
+ protected MetadataService(IServerConfigurationManager serverConfigurationManager, ILogger<MetadataService<TItemType, TIdType>> logger, IProviderManager providerManager, IFileSystem fileSystem, ILibraryManager libraryManager)
{
ServerConfigurationManager = serverConfigurationManager;
Logger = logger;
@@ -486,7 +486,7 @@ namespace MediaBrowser.Providers.Manager
{
var updateType = ItemUpdateType.None;
- if (!item.LockedFields.Contains(MetadataFields.Genres))
+ if (!item.LockedFields.Contains(MetadataField.Genres))
{
var currentList = item.Genres;
@@ -507,7 +507,7 @@ namespace MediaBrowser.Providers.Manager
{
var updateType = ItemUpdateType.None;
- if (!item.LockedFields.Contains(MetadataFields.Studios))
+ if (!item.LockedFields.Contains(MetadataField.Studios))
{
var currentList = item.Studios;
@@ -528,7 +528,7 @@ namespace MediaBrowser.Providers.Manager
{
var updateType = ItemUpdateType.None;
- if (!item.LockedFields.Contains(MetadataFields.OfficialRating))
+ if (!item.LockedFields.Contains(MetadataField.OfficialRating))
{
if (item.UpdateRatingToItems(children))
{
@@ -718,7 +718,7 @@ namespace MediaBrowser.Providers.Manager
userDataList.AddRange(localItem.UserDataList);
}
- MergeData(localItem, temp, new MetadataFields[] { }, !options.ReplaceAllMetadata, true);
+ MergeData(localItem, temp, new MetadataField[] { }, !options.ReplaceAllMetadata, true);
refreshResult.UpdateType = refreshResult.UpdateType | ItemUpdateType.MetadataImport;
// Only one local provider allowed per item
@@ -766,7 +766,7 @@ namespace MediaBrowser.Providers.Manager
else
{
// TODO: If the new metadata from above has some blank data, this can cause old data to get filled into those empty fields
- MergeData(metadata, temp, new MetadataFields[] { }, false, false);
+ MergeData(metadata, temp, new MetadataField[] { }, false, false);
MergeData(temp, metadata, item.LockedFields, true, false);
}
}
@@ -843,7 +843,7 @@ namespace MediaBrowser.Providers.Manager
{
result.Provider = provider.Name;
- MergeData(result, temp, new MetadataFields[] { }, false, false);
+ MergeData(result, temp, new MetadataField[] { }, false, false);
MergeNewData(temp.Item, id);
refreshResult.UpdateType = refreshResult.UpdateType | ItemUpdateType.MetadataDownload;
@@ -894,7 +894,7 @@ namespace MediaBrowser.Providers.Manager
protected abstract void MergeData(MetadataResult<TItemType> source,
MetadataResult<TItemType> target,
- MetadataFields[] lockedFields,
+ MetadataField[] lockedFields,
bool replaceData,
bool mergeMetadataSettings);
diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs
index cfff89767..eeb74ec96 100644
--- a/MediaBrowser.Providers/Manager/ProviderManager.cs
+++ b/MediaBrowser.Providers/Manager/ProviderManager.cs
@@ -8,6 +8,7 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
using MediaBrowser.Common.Net;
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller;
@@ -16,7 +17,6 @@ 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.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Subtitles;
@@ -28,6 +28,12 @@ using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Serialization;
using Microsoft.Extensions.Logging;
using Priority_Queue;
+using Book = MediaBrowser.Controller.Entities.Book;
+using Episode = MediaBrowser.Controller.Entities.TV.Episode;
+using Movie = MediaBrowser.Controller.Entities.Movies.Movie;
+using MusicAlbum = MediaBrowser.Controller.Entities.Audio.MusicAlbum;
+using Season = MediaBrowser.Controller.Entities.TV.Season;
+using Series = MediaBrowser.Controller.Entities.TV.Series;
namespace MediaBrowser.Providers.Manager
{
@@ -36,7 +42,7 @@ namespace MediaBrowser.Providers.Manager
/// </summary>
public class ProviderManager : IProviderManager, IDisposable
{
- private readonly ILogger _logger;
+ private readonly ILogger<ProviderManager> _logger;
private readonly IHttpClient _httpClient;
private readonly ILibraryMonitor _libraryMonitor;
private readonly IFileSystem _fileSystem;
@@ -182,6 +188,12 @@ namespace MediaBrowser.Providers.Manager
return new ImageSaver(_configurationManager, _libraryMonitor, _fileSystem, _logger).SaveImage(item, fileStream, mimeType, type, imageIndex, saveLocallyWithMedia, cancellationToken);
}
+ public Task SaveImage(User user, Stream source, string mimeType, string path)
+ {
+ return new ImageSaver(_configurationManager, _libraryMonitor, _fileSystem, _logger)
+ .SaveImage(user, source, path);
+ }
+
public async Task<IEnumerable<RemoteImageInfo>> GetAvailableRemoteImages(BaseItem item, RemoteImageQuery query, CancellationToken cancellationToken)
{
var providers = GetRemoteImageProviders(item, query.IncludeDisabledProviders);
@@ -255,11 +267,7 @@ namespace MediaBrowser.Providers.Manager
/// <returns>IEnumerable{IImageProvider}.</returns>
public IEnumerable<ImageProviderInfo> GetRemoteImageProviderInfo(BaseItem item)
{
- return GetRemoteImageProviders(item, true).Select(i => new ImageProviderInfo
- {
- Name = i.Name,
- SupportedImages = i.GetSupportedImages(item).ToArray()
- });
+ return GetRemoteImageProviders(item, true).Select(i => new ImageProviderInfo(i.Name, i.GetSupportedImages(item).ToArray()));
}
public IEnumerable<IImageProvider> GetImageProviders(BaseItem item, ImageRefreshOptions refreshOptions)
diff --git a/MediaBrowser.Providers/Manager/ProviderUtils.cs b/MediaBrowser.Providers/Manager/ProviderUtils.cs
index 8d1588c4e..7f2a1410b 100644
--- a/MediaBrowser.Providers/Manager/ProviderUtils.cs
+++ b/MediaBrowser.Providers/Manager/ProviderUtils.cs
@@ -14,7 +14,7 @@ namespace MediaBrowser.Providers.Manager
public static void MergeBaseItemData<T>(
MetadataResult<T> sourceResult,
MetadataResult<T> targetResult,
- MetadataFields[] lockedFields,
+ MetadataField[] lockedFields,
bool replaceData,
bool mergeMetadataSettings)
where T : BaseItem
@@ -31,7 +31,7 @@ namespace MediaBrowser.Providers.Manager
throw new ArgumentNullException(nameof(target));
}
- if (!lockedFields.Contains(MetadataFields.Name))
+ if (!lockedFields.Contains(MetadataField.Name))
{
if (replaceData || string.IsNullOrEmpty(target.Name))
{
@@ -62,7 +62,7 @@ namespace MediaBrowser.Providers.Manager
target.EndDate = source.EndDate;
}
- if (!lockedFields.Contains(MetadataFields.Genres))
+ if (!lockedFields.Contains(MetadataField.Genres))
{
if (replaceData || target.Genres.Length == 0)
{
@@ -75,7 +75,7 @@ namespace MediaBrowser.Providers.Manager
target.IndexNumber = source.IndexNumber;
}
- if (!lockedFields.Contains(MetadataFields.OfficialRating))
+ if (!lockedFields.Contains(MetadataField.OfficialRating))
{
if (replaceData || string.IsNullOrEmpty(target.OfficialRating))
{
@@ -93,7 +93,7 @@ namespace MediaBrowser.Providers.Manager
target.Tagline = source.Tagline;
}
- if (!lockedFields.Contains(MetadataFields.Overview))
+ if (!lockedFields.Contains(MetadataField.Overview))
{
if (replaceData || string.IsNullOrEmpty(target.Overview))
{
@@ -106,7 +106,7 @@ namespace MediaBrowser.Providers.Manager
target.ParentIndexNumber = source.ParentIndexNumber;
}
- if (!lockedFields.Contains(MetadataFields.Cast))
+ if (!lockedFields.Contains(MetadataField.Cast))
{
if (replaceData || targetResult.People == null || targetResult.People.Count == 0)
{
@@ -129,7 +129,7 @@ namespace MediaBrowser.Providers.Manager
target.ProductionYear = source.ProductionYear;
}
- if (!lockedFields.Contains(MetadataFields.Runtime))
+ if (!lockedFields.Contains(MetadataField.Runtime))
{
if (replaceData || !target.RunTimeTicks.HasValue)
{
@@ -140,7 +140,7 @@ namespace MediaBrowser.Providers.Manager
}
}
- if (!lockedFields.Contains(MetadataFields.Studios))
+ if (!lockedFields.Contains(MetadataField.Studios))
{
if (replaceData || target.Studios.Length == 0)
{
@@ -148,7 +148,7 @@ namespace MediaBrowser.Providers.Manager
}
}
- if (!lockedFields.Contains(MetadataFields.Tags))
+ if (!lockedFields.Contains(MetadataField.Tags))
{
if (replaceData || target.Tags.Length == 0)
{
@@ -156,7 +156,7 @@ namespace MediaBrowser.Providers.Manager
}
}
- if (!lockedFields.Contains(MetadataFields.ProductionLocations))
+ if (!lockedFields.Contains(MetadataField.ProductionLocations))
{
if (replaceData || target.ProductionLocations.Length == 0)
{
diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
index 5073b4015..4d24a1ef5 100644
--- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj
+++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
@@ -16,11 +16,11 @@
</ItemGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.4" />
- <PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="3.1.4" />
+ <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.5" />
+ <PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="3.1.5" />
<PackageReference Include="OptimizedPriorityQueue" Version="4.2.0" />
<PackageReference Include="PlaylistsNET" Version="1.0.4" />
- <PackageReference Include="TvDbSharper" Version="3.0.1" />
+ <PackageReference Include="TvDbSharper" Version="3.2.0" />
</ItemGroup>
<PropertyGroup>
@@ -44,11 +44,9 @@
<ItemGroup>
<None Remove="Plugins\AudioDb\Configuration\config.html" />
<EmbeddedResource Include="Plugins\AudioDb\Configuration\config.html" />
- </ItemGroup>
-
- <ItemGroup>
+ <None Remove="Plugins\Omdb\Configuration\config.html" />
+ <EmbeddedResource Include="Plugins\Omdb\Configuration\config.html" />
<None Remove="Plugins\MusicBrainz\Configuration\config.html" />
<EmbeddedResource Include="Plugins\MusicBrainz\Configuration\config.html" />
</ItemGroup>
-
</Project>
diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs
index 207d75524..3b6c8ff72 100644
--- a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs
+++ b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs
@@ -112,7 +112,7 @@ namespace MediaBrowser.Providers.MediaInfo
audio.Name = data.Name;
}
- if (audio.SupportsPeople && !audio.LockedFields.Contains(MetadataFields.Cast))
+ if (audio.SupportsPeople && !audio.LockedFields.Contains(MetadataField.Cast))
{
var people = new List<PersonInfo>();
@@ -143,7 +143,7 @@ namespace MediaBrowser.Providers.MediaInfo
audio.ProductionYear = audio.PremiereDate.Value.ToLocalTime().Year;
}
- if (!audio.LockedFields.Contains(MetadataFields.Genres))
+ if (!audio.LockedFields.Contains(MetadataField.Genres))
{
audio.Genres = Array.Empty<string>();
@@ -153,16 +153,16 @@ namespace MediaBrowser.Providers.MediaInfo
}
}
- if (!audio.LockedFields.Contains(MetadataFields.Studios))
+ if (!audio.LockedFields.Contains(MetadataField.Studios))
{
audio.SetStudios(data.Studios);
}
- audio.SetProviderId(MetadataProviders.MusicBrainzAlbumArtist, data.GetProviderId(MetadataProviders.MusicBrainzAlbumArtist));
- audio.SetProviderId(MetadataProviders.MusicBrainzArtist, data.GetProviderId(MetadataProviders.MusicBrainzArtist));
- audio.SetProviderId(MetadataProviders.MusicBrainzAlbum, data.GetProviderId(MetadataProviders.MusicBrainzAlbum));
- audio.SetProviderId(MetadataProviders.MusicBrainzReleaseGroup, data.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup));
- audio.SetProviderId(MetadataProviders.MusicBrainzTrack, data.GetProviderId(MetadataProviders.MusicBrainzTrack));
+ audio.SetProviderId(MetadataProvider.MusicBrainzAlbumArtist, data.GetProviderId(MetadataProvider.MusicBrainzAlbumArtist));
+ audio.SetProviderId(MetadataProvider.MusicBrainzArtist, data.GetProviderId(MetadataProvider.MusicBrainzArtist));
+ audio.SetProviderId(MetadataProvider.MusicBrainzAlbum, data.GetProviderId(MetadataProvider.MusicBrainzAlbum));
+ audio.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, data.GetProviderId(MetadataProvider.MusicBrainzReleaseGroup));
+ audio.SetProviderId(MetadataProvider.MusicBrainzTrack, data.GetProviderId(MetadataProvider.MusicBrainzTrack));
}
}
}
diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
index 6982568eb..81103575d 100644
--- a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
+++ b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
@@ -37,7 +37,7 @@ namespace MediaBrowser.Providers.MediaInfo
IPreRefreshProvider,
IHasItemChangeMonitor
{
- private readonly ILogger _logger;
+ private readonly ILogger<FFProbeProvider> _logger;
private readonly IIsoManager _isoManager;
private readonly IMediaEncoder _mediaEncoder;
private readonly IItemRepository _itemRepo;
diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
index 89496622f..933cf03d6 100644
--- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
+++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
@@ -368,7 +368,7 @@ namespace MediaBrowser.Providers.MediaInfo
{
var isFullRefresh = refreshOptions.MetadataRefreshMode == MetadataRefreshMode.FullRefresh;
- if (!video.IsLocked && !video.LockedFields.Contains(MetadataFields.OfficialRating))
+ if (!video.IsLocked && !video.LockedFields.Contains(MetadataField.OfficialRating))
{
if (!string.IsNullOrWhiteSpace(data.OfficialRating) || isFullRefresh)
{
@@ -376,7 +376,7 @@ namespace MediaBrowser.Providers.MediaInfo
}
}
- if (!video.IsLocked && !video.LockedFields.Contains(MetadataFields.Genres))
+ if (!video.IsLocked && !video.LockedFields.Contains(MetadataField.Genres))
{
if (video.Genres.Length == 0 || isFullRefresh)
{
@@ -389,7 +389,7 @@ namespace MediaBrowser.Providers.MediaInfo
}
}
- if (!video.IsLocked && !video.LockedFields.Contains(MetadataFields.Studios))
+ if (!video.IsLocked && !video.LockedFields.Contains(MetadataField.Studios))
{
if (video.Studios.Length == 0 || isFullRefresh)
{
@@ -426,7 +426,7 @@ namespace MediaBrowser.Providers.MediaInfo
}
}
- if (!video.IsLocked && !video.LockedFields.Contains(MetadataFields.Name))
+ if (!video.IsLocked && !video.LockedFields.Contains(MetadataField.Name))
{
if (!string.IsNullOrWhiteSpace(data.Name) && libraryOptions.EnableEmbeddedTitles)
{
@@ -444,7 +444,7 @@ namespace MediaBrowser.Providers.MediaInfo
video.ProductionYear = video.PremiereDate.Value.ToLocalTime().Year;
}
- if (!video.IsLocked && !video.LockedFields.Contains(MetadataFields.Overview))
+ if (!video.IsLocked && !video.LockedFields.Contains(MetadataField.Overview))
{
if (string.IsNullOrWhiteSpace(video.Overview) || isFullRefresh)
{
@@ -457,7 +457,7 @@ namespace MediaBrowser.Providers.MediaInfo
{
var isFullRefresh = options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh;
- if (!video.IsLocked && !video.LockedFields.Contains(MetadataFields.Cast))
+ if (!video.IsLocked && !video.LockedFields.Contains(MetadataField.Cast))
{
if (isFullRefresh || _libraryManager.GetPeople(video).Count == 0)
{
diff --git a/MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs b/MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs
index 77c0e9b4e..f35e82bb5 100644
--- a/MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs
+++ b/MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs
@@ -25,7 +25,8 @@ namespace MediaBrowser.Providers.MediaInfo
_subtitleManager = subtitleManager;
}
- public async Task<List<string>> DownloadSubtitles(Video video,
+ public async Task<List<string>> DownloadSubtitles(
+ Video video,
List<MediaStream> mediaStreams,
bool skipIfEmbeddedSubtitlesPresent,
bool skipIfAudioTrackMatches,
diff --git a/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs b/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs
index 2615f2dbb..fb87030ff 100644
--- a/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs
+++ b/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs
@@ -24,7 +24,7 @@ namespace MediaBrowser.Providers.MediaInfo
private readonly IServerConfigurationManager _config;
private readonly ISubtitleManager _subtitleManager;
private readonly IMediaSourceManager _mediaSourceManager;
- private readonly ILogger _logger;
+ private readonly ILogger<SubtitleScheduledTask> _logger;
private readonly IJsonSerializer _json;
private readonly ILocalizationManager _localization;
diff --git a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs
index f40570040..08e503a71 100644
--- a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs
+++ b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs
@@ -17,7 +17,7 @@ namespace MediaBrowser.Providers.MediaInfo
public class VideoImageProvider : IDynamicImageProvider, IHasOrder
{
private readonly IMediaEncoder _mediaEncoder;
- private readonly ILogger _logger;
+ private readonly ILogger<VideoImageProvider> _logger;
private readonly IFileSystem _fileSystem;
public VideoImageProvider(IMediaEncoder mediaEncoder, ILogger<VideoImageProvider> logger, IFileSystem fileSystem)
diff --git a/MediaBrowser.Providers/Movies/MovieExternalIds.cs b/MediaBrowser.Providers/Movies/MovieExternalIds.cs
index 55810b1ed..1f07deacf 100644
--- a/MediaBrowser.Providers/Movies/MovieExternalIds.cs
+++ b/MediaBrowser.Providers/Movies/MovieExternalIds.cs
@@ -13,7 +13,7 @@ namespace MediaBrowser.Providers.Movies
public string Name => "IMDb";
/// <inheritdoc />
- public string Key => MetadataProviders.Imdb.ToString();
+ public string Key => MetadataProvider.Imdb.ToString();
/// <inheritdoc />
public string UrlFormatString => "https://www.imdb.com/title/{0}";
@@ -37,7 +37,7 @@ namespace MediaBrowser.Providers.Movies
public string Name => "IMDb";
/// <inheritdoc />
- public string Key => MetadataProviders.Imdb.ToString();
+ public string Key => MetadataProvider.Imdb.ToString();
/// <inheritdoc />
public string UrlFormatString => "https://www.imdb.com/name/{0}";
diff --git a/MediaBrowser.Providers/Movies/MovieMetadataService.cs b/MediaBrowser.Providers/Movies/MovieMetadataService.cs
index 1e2c325d9..9faba4798 100644
--- a/MediaBrowser.Providers/Movies/MovieMetadataService.cs
+++ b/MediaBrowser.Providers/Movies/MovieMetadataService.cs
@@ -36,7 +36,7 @@ namespace MediaBrowser.Providers.Movies
}
/// <inheritdoc />
- protected override void MergeData(MetadataResult<Movie> source, MetadataResult<Movie> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<Movie> source, MetadataResult<Movie> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
diff --git a/MediaBrowser.Providers/Movies/TrailerMetadataService.cs b/MediaBrowser.Providers/Movies/TrailerMetadataService.cs
index 2e6f762b8..b45d2b745 100644
--- a/MediaBrowser.Providers/Movies/TrailerMetadataService.cs
+++ b/MediaBrowser.Providers/Movies/TrailerMetadataService.cs
@@ -36,7 +36,7 @@ namespace MediaBrowser.Providers.Movies
}
/// <inheritdoc />
- protected override void MergeData(MetadataResult<Trailer> source, MetadataResult<Trailer> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<Trailer> source, MetadataResult<Trailer> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
diff --git a/MediaBrowser.Providers/Music/AlbumMetadataService.cs b/MediaBrowser.Providers/Music/AlbumMetadataService.cs
index ed6c01968..0c6f88c8b 100644
--- a/MediaBrowser.Providers/Music/AlbumMetadataService.cs
+++ b/MediaBrowser.Providers/Music/AlbumMetadataService.cs
@@ -45,7 +45,7 @@ namespace MediaBrowser.Providers.Music
if (isFullRefresh || currentUpdateType > ItemUpdateType.None)
{
- if (!item.LockedFields.Contains(MetadataFields.Name))
+ if (!item.LockedFields.Contains(MetadataField.Name))
{
var name = children.Select(i => i.Album).FirstOrDefault(i => !string.IsNullOrEmpty(i));
@@ -108,7 +108,7 @@ namespace MediaBrowser.Providers.Music
protected override void MergeData(
MetadataResult<MusicAlbum> source,
MetadataResult<MusicAlbum> target,
- MetadataFields[] lockedFields,
+ MetadataField[] lockedFields,
bool replaceData,
bool mergeMetadataSettings)
{
diff --git a/MediaBrowser.Providers/Music/ArtistMetadataService.cs b/MediaBrowser.Providers/Music/ArtistMetadataService.cs
index 5a30260a5..4199c08c6 100644
--- a/MediaBrowser.Providers/Music/ArtistMetadataService.cs
+++ b/MediaBrowser.Providers/Music/ArtistMetadataService.cs
@@ -39,7 +39,7 @@ namespace MediaBrowser.Providers.Music
}
/// <inheritdoc />
- protected override void MergeData(MetadataResult<MusicArtist> source, MetadataResult<MusicArtist> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<MusicArtist> source, MetadataResult<MusicArtist> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}
diff --git a/MediaBrowser.Providers/Music/AudioMetadataService.cs b/MediaBrowser.Providers/Music/AudioMetadataService.cs
index e726fa1e2..251cbbbec 100644
--- a/MediaBrowser.Providers/Music/AudioMetadataService.cs
+++ b/MediaBrowser.Providers/Music/AudioMetadataService.cs
@@ -22,7 +22,7 @@ namespace MediaBrowser.Providers.Music
}
/// <inheritdoc />
- protected override void MergeData(MetadataResult<Audio> source, MetadataResult<Audio> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<Audio> source, MetadataResult<Audio> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
diff --git a/MediaBrowser.Providers/Music/Extensions.cs b/MediaBrowser.Providers/Music/Extensions.cs
index ea1efe266..a1d5aa826 100644
--- a/MediaBrowser.Providers/Music/Extensions.cs
+++ b/MediaBrowser.Providers/Music/Extensions.cs
@@ -21,11 +21,11 @@ namespace MediaBrowser.Providers.Music
public static string GetReleaseGroupId(this AlbumInfo info)
{
- var id = info.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup);
+ var id = info.GetProviderId(MetadataProvider.MusicBrainzReleaseGroup);
if (string.IsNullOrEmpty(id))
{
- return info.SongInfos.Select(i => i.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup))
+ return info.SongInfos.Select(i => i.GetProviderId(MetadataProvider.MusicBrainzReleaseGroup))
.FirstOrDefault(i => !string.IsNullOrEmpty(i));
}
@@ -34,11 +34,11 @@ namespace MediaBrowser.Providers.Music
public static string GetReleaseId(this AlbumInfo info)
{
- var id = info.GetProviderId(MetadataProviders.MusicBrainzAlbum);
+ var id = info.GetProviderId(MetadataProvider.MusicBrainzAlbum);
if (string.IsNullOrEmpty(id))
{
- return info.SongInfos.Select(i => i.GetProviderId(MetadataProviders.MusicBrainzAlbum))
+ return info.SongInfos.Select(i => i.GetProviderId(MetadataProvider.MusicBrainzAlbum))
.FirstOrDefault(i => !string.IsNullOrEmpty(i));
}
@@ -47,16 +47,16 @@ namespace MediaBrowser.Providers.Music
public static string GetMusicBrainzArtistId(this AlbumInfo info)
{
- info.ProviderIds.TryGetValue(MetadataProviders.MusicBrainzAlbumArtist.ToString(), out string id);
+ info.ProviderIds.TryGetValue(MetadataProvider.MusicBrainzAlbumArtist.ToString(), out string id);
if (string.IsNullOrEmpty(id))
{
- info.ArtistProviderIds.TryGetValue(MetadataProviders.MusicBrainzArtist.ToString(), out id);
+ info.ArtistProviderIds.TryGetValue(MetadataProvider.MusicBrainzArtist.ToString(), out id);
}
if (string.IsNullOrEmpty(id))
{
- return info.SongInfos.Select(i => i.GetProviderId(MetadataProviders.MusicBrainzAlbumArtist))
+ return info.SongInfos.Select(i => i.GetProviderId(MetadataProvider.MusicBrainzAlbumArtist))
.FirstOrDefault(i => !string.IsNullOrEmpty(i));
}
@@ -65,11 +65,11 @@ namespace MediaBrowser.Providers.Music
public static string GetMusicBrainzArtistId(this ArtistInfo info)
{
- info.ProviderIds.TryGetValue(MetadataProviders.MusicBrainzArtist.ToString(), out var id);
+ info.ProviderIds.TryGetValue(MetadataProvider.MusicBrainzArtist.ToString(), out var id);
if (string.IsNullOrEmpty(id))
{
- return info.SongInfos.Select(i => i.GetProviderId(MetadataProviders.MusicBrainzAlbumArtist))
+ return info.SongInfos.Select(i => i.GetProviderId(MetadataProvider.MusicBrainzAlbumArtist))
.FirstOrDefault(i => !string.IsNullOrEmpty(i));
}
diff --git a/MediaBrowser.Providers/Music/MusicVideoMetadataService.cs b/MediaBrowser.Providers/Music/MusicVideoMetadataService.cs
index d653e1063..c586e7f70 100644
--- a/MediaBrowser.Providers/Music/MusicVideoMetadataService.cs
+++ b/MediaBrowser.Providers/Music/MusicVideoMetadataService.cs
@@ -25,7 +25,7 @@ namespace MediaBrowser.Providers.Music
protected override void MergeData(
MetadataResult<MusicVideo> source,
MetadataResult<MusicVideo> target,
- MetadataFields[] lockedFields,
+ MetadataField[] lockedFields,
bool replaceData,
bool mergeMetadataSettings)
{
diff --git a/MediaBrowser.Providers/MusicGenres/MusicGenreMetadataService.cs b/MediaBrowser.Providers/MusicGenres/MusicGenreMetadataService.cs
index bb47de40b..c121eaa43 100644
--- a/MediaBrowser.Providers/MusicGenres/MusicGenreMetadataService.cs
+++ b/MediaBrowser.Providers/MusicGenres/MusicGenreMetadataService.cs
@@ -22,7 +22,7 @@ namespace MediaBrowser.Providers.MusicGenres
}
/// <inheritdoc />
- protected override void MergeData(MetadataResult<MusicGenre> source, MetadataResult<MusicGenre> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<MusicGenre> source, MetadataResult<MusicGenre> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}
diff --git a/MediaBrowser.Providers/People/PersonMetadataService.cs b/MediaBrowser.Providers/People/PersonMetadataService.cs
index 804f3f3e3..eeba9a56d 100644
--- a/MediaBrowser.Providers/People/PersonMetadataService.cs
+++ b/MediaBrowser.Providers/People/PersonMetadataService.cs
@@ -22,7 +22,7 @@ namespace MediaBrowser.Providers.People
}
/// <inheritdoc />
- protected override void MergeData(MetadataResult<Person> source, MetadataResult<Person> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<Person> source, MetadataResult<Person> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}
diff --git a/MediaBrowser.Providers/Photos/PhotoAlbumMetadataService.cs b/MediaBrowser.Providers/Photos/PhotoAlbumMetadataService.cs
index af8f7a262..0cae5d8fc 100644
--- a/MediaBrowser.Providers/Photos/PhotoAlbumMetadataService.cs
+++ b/MediaBrowser.Providers/Photos/PhotoAlbumMetadataService.cs
@@ -22,7 +22,7 @@ namespace MediaBrowser.Providers.Photos
}
/// <inheritdoc />
- protected override void MergeData(MetadataResult<PhotoAlbum> source, MetadataResult<PhotoAlbum> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<PhotoAlbum> source, MetadataResult<PhotoAlbum> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}
diff --git a/MediaBrowser.Providers/Photos/PhotoMetadataService.cs b/MediaBrowser.Providers/Photos/PhotoMetadataService.cs
index 579b5a4d0..8cfae7e1a 100644
--- a/MediaBrowser.Providers/Photos/PhotoMetadataService.cs
+++ b/MediaBrowser.Providers/Photos/PhotoMetadataService.cs
@@ -22,7 +22,7 @@ namespace MediaBrowser.Providers.Photos
}
/// <inheritdoc />
- protected override void MergeData(MetadataResult<Photo> source, MetadataResult<Photo> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<Photo> source, MetadataResult<Photo> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}
diff --git a/MediaBrowser.Providers/Playlists/PlaylistItemsProvider.cs b/MediaBrowser.Providers/Playlists/PlaylistItemsProvider.cs
index ae837c591..9fc86b213 100644
--- a/MediaBrowser.Providers/Playlists/PlaylistItemsProvider.cs
+++ b/MediaBrowser.Providers/Playlists/PlaylistItemsProvider.cs
@@ -20,7 +20,7 @@ namespace MediaBrowser.Providers.Playlists
IPreRefreshProvider,
IHasItemChangeMonitor
{
- private ILogger _logger;
+ private readonly ILogger<PlaylistItemsProvider> _logger;
private IFileSystem _fileSystem;
public PlaylistItemsProvider(IFileSystem fileSystem, ILogger<PlaylistItemsProvider> logger)
diff --git a/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs b/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs
index a41362ea3..fcfc37bfe 100644
--- a/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs
+++ b/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs
@@ -37,7 +37,7 @@ namespace MediaBrowser.Providers.Playlists
=> item.GetLinkedChildren();
/// <inheritdoc />
- protected override void MergeData(MetadataResult<Playlist> source, MetadataResult<Playlist> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<Playlist> source, MetadataResult<Playlist> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs
index dee2d59f0..3c314acb3 100644
--- a/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs
@@ -45,7 +45,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
/// <inheritdoc />
public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken)
{
- var id = item.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup);
+ var id = item.GetProviderId(MetadataProvider.MusicBrainzReleaseGroup);
if (!string.IsNullOrWhiteSpace(id))
{
diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs
index 1a0e87871..b1a54f22f 100644
--- a/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs
+++ b/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs
@@ -104,11 +104,11 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
item.Genres = new[] { result.strGenre };
}
- item.SetProviderId(MetadataProviders.AudioDbArtist, result.idArtist);
- item.SetProviderId(MetadataProviders.AudioDbAlbum, result.idAlbum);
+ item.SetProviderId(MetadataProvider.AudioDbArtist, result.idArtist);
+ item.SetProviderId(MetadataProvider.AudioDbAlbum, result.idAlbum);
- item.SetProviderId(MetadataProviders.MusicBrainzAlbumArtist, result.strMusicBrainzArtistID);
- item.SetProviderId(MetadataProviders.MusicBrainzReleaseGroup, result.strMusicBrainzID);
+ item.SetProviderId(MetadataProvider.MusicBrainzAlbumArtist, result.strMusicBrainzArtistID);
+ item.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, result.strMusicBrainzID);
string overview = null;
diff --git a/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs
index 18afd5dd5..04cdab66a 100644
--- a/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs
@@ -47,7 +47,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
/// <inheritdoc />
public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken)
{
- var id = item.GetProviderId(MetadataProviders.MusicBrainzArtist);
+ var id = item.GetProviderId(MetadataProvider.MusicBrainzArtist);
if (!string.IsNullOrWhiteSpace(id))
{
diff --git a/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs
index df0f3df8f..d8a18a6bc 100644
--- a/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs
+++ b/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs
@@ -92,8 +92,8 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
item.Genres = new[] { result.strGenre };
}
- item.SetProviderId(MetadataProviders.AudioDbArtist, result.idArtist);
- item.SetProviderId(MetadataProviders.MusicBrainzArtist, result.strMusicBrainzID);
+ item.SetProviderId(MetadataProvider.AudioDbArtist, result.idArtist);
+ item.SetProviderId(MetadataProvider.MusicBrainzArtist, result.strMusicBrainzID);
string overview = null;
diff --git a/MediaBrowser.Providers/Plugins/AudioDb/Configuration/config.html b/MediaBrowser.Providers/Plugins/AudioDb/Configuration/config.html
index 34494644d..fbf413f2b 100644
--- a/MediaBrowser.Providers/Plugins/AudioDb/Configuration/config.html
+++ b/MediaBrowser.Providers/Plugins/AudioDb/Configuration/config.html
@@ -31,8 +31,8 @@
$('.configPage').on('pageshow', function () {
Dashboard.showLoadingMsg();
ApiClient.getPluginConfiguration(PluginConfig.pluginId).then(function (config) {
- $('#enable').checked(config.Enable);
- $('#replaceAlbumName').checked(config.ReplaceAlbumName);
+ $('#enable').checked = config.Enable;
+ $('#replaceAlbumName').checked = config.ReplaceAlbumName;
Dashboard.hideLoadingMsg();
});
@@ -43,8 +43,8 @@
var form = this;
ApiClient.getPluginConfiguration(PluginConfig.pluginId).then(function (config) {
- config.Enable = $('#enable', form).checked();
- config.ReplaceAlbumName = $('#replaceAlbumName', form).checked();
+ config.Enable = $('#enable', form).checked;
+ config.ReplaceAlbumName = $('#replaceAlbumName', form).checked;
ApiClient.updatePluginConfiguration(PluginConfig.pluginId, config).then(Dashboard.processPluginConfigurationUpdateResult);
});
diff --git a/MediaBrowser.Providers/Plugins/AudioDb/ExternalIds.cs b/MediaBrowser.Providers/Plugins/AudioDb/ExternalIds.cs
index 2d8cb431c..478ea5190 100644
--- a/MediaBrowser.Providers/Plugins/AudioDb/ExternalIds.cs
+++ b/MediaBrowser.Providers/Plugins/AudioDb/ExternalIds.cs
@@ -10,7 +10,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
public string Name => "TheAudioDb";
/// <inheritdoc />
- public string Key => MetadataProviders.AudioDbAlbum.ToString();
+ public string Key => MetadataProvider.AudioDbAlbum.ToString();
/// <inheritdoc />
public string UrlFormatString => "https://www.theaudiodb.com/album/{0}";
@@ -25,7 +25,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
public string Name => "TheAudioDb Album";
/// <inheritdoc />
- public string Key => MetadataProviders.AudioDbAlbum.ToString();
+ public string Key => MetadataProvider.AudioDbAlbum.ToString();
/// <inheritdoc />
public string UrlFormatString => "https://www.theaudiodb.com/album/{0}";
@@ -40,7 +40,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
public string Name => "TheAudioDb";
/// <inheritdoc />
- public string Key => MetadataProviders.AudioDbArtist.ToString();
+ public string Key => MetadataProvider.AudioDbArtist.ToString();
/// <inheritdoc />
public string UrlFormatString => "https://www.theaudiodb.com/artist/{0}";
@@ -55,7 +55,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
public string Name => "TheAudioDb Artist";
/// <inheritdoc />
- public string Key => MetadataProviders.AudioDbArtist.ToString();
+ public string Key => MetadataProvider.AudioDbArtist.ToString();
/// <inheritdoc />
public string UrlFormatString => "https://www.theaudiodb.com/artist/{0}";
diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/AlbumProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/AlbumProvider.cs
index 31cdaf616..0a2c7c124 100644
--- a/MediaBrowser.Providers/Plugins/MusicBrainz/AlbumProvider.cs
+++ b/MediaBrowser.Providers/Plugins/MusicBrainz/AlbumProvider.cs
@@ -42,7 +42,7 @@ namespace MediaBrowser.Providers.Music
private readonly IHttpClient _httpClient;
private readonly IApplicationHost _appHost;
- private readonly ILogger _logger;
+ private readonly ILogger<MusicBrainzAlbumProvider> _logger;
private readonly string _musicBrainzBaseUrl;
@@ -163,17 +163,17 @@ namespace MediaBrowser.Providers.Music
Name = i.Artists[0].Item1
};
- result.AlbumArtist.SetProviderId(MetadataProviders.MusicBrainzArtist, i.Artists[0].Item2);
+ result.AlbumArtist.SetProviderId(MetadataProvider.MusicBrainzArtist, i.Artists[0].Item2);
}
if (!string.IsNullOrWhiteSpace(i.ReleaseId))
{
- result.SetProviderId(MetadataProviders.MusicBrainzAlbum, i.ReleaseId);
+ result.SetProviderId(MetadataProvider.MusicBrainzAlbum, i.ReleaseId);
}
if (!string.IsNullOrWhiteSpace(i.ReleaseGroupId))
{
- result.SetProviderId(MetadataProviders.MusicBrainzReleaseGroup, i.ReleaseGroupId);
+ result.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, i.ReleaseGroupId);
}
return result;
@@ -247,12 +247,12 @@ namespace MediaBrowser.Providers.Music
{
if (!string.IsNullOrEmpty(releaseId))
{
- result.Item.SetProviderId(MetadataProviders.MusicBrainzAlbum, releaseId);
+ result.Item.SetProviderId(MetadataProvider.MusicBrainzAlbum, releaseId);
}
if (!string.IsNullOrEmpty(releaseGroupId))
{
- result.Item.SetProviderId(MetadataProviders.MusicBrainzReleaseGroup, releaseGroupId);
+ result.Item.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, releaseGroupId);
}
}
diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs
index 260a3b6e7..9d93dbdd1 100644
--- a/MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs
+++ b/MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs
@@ -216,7 +216,7 @@ namespace MediaBrowser.Providers.Music
}
}
- result.SetProviderId(MetadataProviders.MusicBrainzArtist, artistId);
+ result.SetProviderId(MetadataProvider.MusicBrainzArtist, artistId);
if (string.IsNullOrWhiteSpace(artistId) || string.IsNullOrWhiteSpace(result.Name))
{
@@ -249,7 +249,7 @@ namespace MediaBrowser.Providers.Music
if (singleResult != null)
{
- musicBrainzId = singleResult.GetProviderId(MetadataProviders.MusicBrainzArtist);
+ musicBrainzId = singleResult.GetProviderId(MetadataProvider.MusicBrainzArtist);
result.Item.Overview = singleResult.Overview;
if (Plugin.Instance.Configuration.ReplaceArtistName)
@@ -262,7 +262,7 @@ namespace MediaBrowser.Providers.Music
if (!string.IsNullOrWhiteSpace(musicBrainzId))
{
result.HasMetadata = true;
- result.Item.SetProviderId(MetadataProviders.MusicBrainzArtist, musicBrainzId);
+ result.Item.SetProviderId(MetadataProvider.MusicBrainzArtist, musicBrainzId);
}
return result;
diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/config.html b/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/config.html
index 1f02461da..90196b046 100644
--- a/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/config.html
+++ b/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/config.html
@@ -41,8 +41,8 @@
ApiClient.getPluginConfiguration(MusicBrainzPluginConfig.uniquePluginId).then(function (config) {
$('#server').val(config.Server).change();
$('#rateLimit').val(config.RateLimit).change();
- $('#enable').checked(config.Enable);
- $('#replaceArtistName').checked(config.ReplaceArtistName);
+ $('#enable').checked = config.Enable;
+ $('#replaceArtistName').checked = config.ReplaceArtistName;
Dashboard.hideLoadingMsg();
});
@@ -55,8 +55,8 @@
ApiClient.getPluginConfiguration(MusicBrainzPluginConfig.uniquePluginId).then(function (config) {
config.Server = $('#server', form).val();
config.RateLimit = $('#rateLimit', form).val();
- config.Enable = $('#enable', form).checked();
- config.ReplaceArtistName = $('#replaceArtistName', form).checked();
+ config.Enable = $('#enable', form).checked;
+ config.ReplaceArtistName = $('#replaceArtistName', form).checked;
ApiClient.updatePluginConfiguration(MusicBrainzPluginConfig.uniquePluginId, config).then(Dashboard.processPluginConfigurationUpdateResult);
});
diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/ExternalIds.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/ExternalIds.cs
index 03565a34c..3be6f570b 100644
--- a/MediaBrowser.Providers/Plugins/MusicBrainz/ExternalIds.cs
+++ b/MediaBrowser.Providers/Plugins/MusicBrainz/ExternalIds.cs
@@ -11,7 +11,7 @@ namespace MediaBrowser.Providers.Music
public string Name => "MusicBrainz Release Group";
/// <inheritdoc />
- public string Key => MetadataProviders.MusicBrainzReleaseGroup.ToString();
+ public string Key => MetadataProvider.MusicBrainzReleaseGroup.ToString();
/// <inheritdoc />
public string UrlFormatString => Plugin.Instance.Configuration.Server + "/release-group/{0}";
@@ -26,7 +26,7 @@ namespace MediaBrowser.Providers.Music
public string Name => "MusicBrainz Album Artist";
/// <inheritdoc />
- public string Key => MetadataProviders.MusicBrainzAlbumArtist.ToString();
+ public string Key => MetadataProvider.MusicBrainzAlbumArtist.ToString();
/// <inheritdoc />
public string UrlFormatString => Plugin.Instance.Configuration.Server + "/artist/{0}";
@@ -41,7 +41,7 @@ namespace MediaBrowser.Providers.Music
public string Name => "MusicBrainz Album";
/// <inheritdoc />
- public string Key => MetadataProviders.MusicBrainzAlbum.ToString();
+ public string Key => MetadataProvider.MusicBrainzAlbum.ToString();
/// <inheritdoc />
public string UrlFormatString => Plugin.Instance.Configuration.Server + "/release/{0}";
@@ -56,7 +56,7 @@ namespace MediaBrowser.Providers.Music
public string Name => "MusicBrainz";
/// <inheritdoc />
- public string Key => MetadataProviders.MusicBrainzArtist.ToString();
+ public string Key => MetadataProvider.MusicBrainzArtist.ToString();
/// <inheritdoc />
public string UrlFormatString => Plugin.Instance.Configuration.Server + "/artist/{0}";
@@ -72,7 +72,7 @@ namespace MediaBrowser.Providers.Music
/// <inheritdoc />
- public string Key => MetadataProviders.MusicBrainzArtist.ToString();
+ public string Key => MetadataProvider.MusicBrainzArtist.ToString();
/// <inheritdoc />
public string UrlFormatString => Plugin.Instance.Configuration.Server + "/artist/{0}";
@@ -87,7 +87,7 @@ namespace MediaBrowser.Providers.Music
public string Name => "MusicBrainz Track";
/// <inheritdoc />
- public string Key => MetadataProviders.MusicBrainzTrack.ToString();
+ public string Key => MetadataProvider.MusicBrainzTrack.ToString();
/// <inheritdoc />
public string UrlFormatString => Plugin.Instance.Configuration.Server + "/track/{0}";
diff --git a/MediaBrowser.Providers/Plugins/Omdb/Configuration/PluginConfiguration.cs b/MediaBrowser.Providers/Plugins/Omdb/Configuration/PluginConfiguration.cs
new file mode 100644
index 000000000..a9eecdd9e
--- /dev/null
+++ b/MediaBrowser.Providers/Plugins/Omdb/Configuration/PluginConfiguration.cs
@@ -0,0 +1,9 @@
+using MediaBrowser.Model.Plugins;
+
+namespace MediaBrowser.Providers.Plugins.Omdb
+{
+ public class PluginConfiguration : BasePluginConfiguration
+ {
+ public bool CastAndCrew { get; set; }
+ }
+}
diff --git a/MediaBrowser.Providers/Plugins/Omdb/Configuration/config.html b/MediaBrowser.Providers/Plugins/Omdb/Configuration/config.html
new file mode 100644
index 000000000..8b117ec8d
--- /dev/null
+++ b/MediaBrowser.Providers/Plugins/Omdb/Configuration/config.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>OMDb</title>
+</head>
+<body>
+ <div data-role="page" class="page type-interior pluginConfigurationPage configPage" data-require="emby-input,emby-button,emby-checkbox">
+ <div data-role="content">
+ <div class="content-primary">
+ <form class="configForm">
+ <label class="checkboxContainer">
+ <input is="emby-checkbox" type="checkbox" id="castAndCrew" />
+ <span>Collect information about the cast and other crew members from OMDb.</span>
+ </label>
+ <br />
+ <div>
+ <button is="emby-button" type="submit" class="raised button-submit block"><span>Save</span></button>
+ </div>
+ </form>
+ </div>
+ </div>
+ <script type="text/javascript">
+ var PluginConfig = {
+ pluginId: "a628c0da-fac5-4c7e-9d1a-7134223f14c8"
+ };
+
+ $('.configPage').on('pageshow', function () {
+ Dashboard.showLoadingMsg();
+ ApiClient.getPluginConfiguration(PluginConfig.pluginId).then(function (config) {
+ $('#castAndCrew').checked = config.CastAndCrew;
+ Dashboard.hideLoadingMsg();
+ });
+ });
+
+ $('.configForm').on('submit', function (e) {
+ Dashboard.showLoadingMsg();
+
+ var form = this;
+ ApiClient.getPluginConfiguration(PluginConfig.pluginId).then(function (config) {
+ config.CastAndCrew = $('#castAndCrew', form).checked;
+ ApiClient.updatePluginConfiguration(PluginConfig.pluginId, config).then(Dashboard.processPluginConfigurationUpdateResult);
+ });
+
+ return false;
+ });
+ </script>
+ </div>
+</body>
+</html>
diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbEpisodeProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbEpisodeProvider.cs
index f0328e8d8..b074a1b0a 100644
--- a/MediaBrowser.Providers/Plugins/Omdb/OmdbEpisodeProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbEpisodeProvider.cs
@@ -63,12 +63,12 @@ namespace MediaBrowser.Providers.Plugins.Omdb
return result;
}
- if (info.SeriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out string seriesImdbId) && !string.IsNullOrEmpty(seriesImdbId))
+ if (info.SeriesProviderIds.TryGetValue(MetadataProvider.Imdb.ToString(), out string seriesImdbId) && !string.IsNullOrEmpty(seriesImdbId))
{
if (info.IndexNumber.HasValue && info.ParentIndexNumber.HasValue)
{
result.HasMetadata = await new OmdbProvider(_jsonSerializer, _httpClient, _fileSystem, _appHost, _configurationManager)
- .FetchEpisodeData(result, info.IndexNumber.Value, info.ParentIndexNumber.Value, info.GetProviderId(MetadataProviders.Imdb), seriesImdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
+ .FetchEpisodeData(result, info.IndexNumber.Value, info.ParentIndexNumber.Value, info.GetProviderId(MetadataProvider.Imdb), seriesImdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
}
}
diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs
index a450c2a6d..d78a37784 100644
--- a/MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs
@@ -42,7 +42,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken)
{
- var imdbId = item.GetProviderId(MetadataProviders.Imdb);
+ var imdbId = item.GetProviderId(MetadataProvider.Imdb);
var list = new List<RemoteImageInfo>();
@@ -92,6 +92,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
{
return item is Movie || item is Trailer || item is Episode;
}
+
// After other internet providers, because they're better
// But before fallback providers like screengrab
public int Order => 90;
diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs
index 64a75955a..4a29ba4d0 100644
--- a/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs
@@ -68,12 +68,12 @@ namespace MediaBrowser.Providers.Plugins.Omdb
{
var episodeSearchInfo = searchInfo as EpisodeInfo;
- var imdbId = searchInfo.GetProviderId(MetadataProviders.Imdb);
+ var imdbId = searchInfo.GetProviderId(MetadataProvider.Imdb);
var urlQuery = "plot=full&r=json";
if (type == "episode" && episodeSearchInfo != null)
{
- episodeSearchInfo.SeriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out imdbId);
+ episodeSearchInfo.SeriesProviderIds.TryGetValue(MetadataProvider.Imdb.ToString(), out imdbId);
}
var name = searchInfo.Name;
@@ -103,6 +103,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
{
urlQuery += "&t=" + WebUtility.UrlEncode(name);
}
+
urlQuery += "&type=" + type;
}
else
@@ -117,6 +118,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
{
urlQuery += string.Format(CultureInfo.InvariantCulture, "&Episode={0}", searchInfo.IndexNumber);
}
+
if (searchInfo.ParentIndexNumber.HasValue)
{
urlQuery += string.Format(CultureInfo.InvariantCulture, "&Season={0}", searchInfo.ParentIndexNumber);
@@ -163,7 +165,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
item.IndexNumberEnd = episodeSearchInfo.IndexNumberEnd.Value;
}
- item.SetProviderId(MetadataProviders.Imdb, result.imdbID);
+ item.SetProviderId(MetadataProvider.Imdb, result.imdbID);
if (result.Year.Length > 0
&& int.TryParse(result.Year.Substring(0, Math.Min(result.Year.Length, 4)), NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedYear))
@@ -208,7 +210,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
QueriedById = true
};
- var imdbId = info.GetProviderId(MetadataProviders.Imdb);
+ var imdbId = info.GetProviderId(MetadataProvider.Imdb);
if (string.IsNullOrWhiteSpace(imdbId))
{
imdbId = await GetSeriesImdbId(info, cancellationToken).ConfigureAwait(false);
@@ -217,7 +219,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
if (!string.IsNullOrEmpty(imdbId))
{
- result.Item.SetProviderId(MetadataProviders.Imdb, imdbId);
+ result.Item.SetProviderId(MetadataProvider.Imdb, imdbId);
result.HasMetadata = true;
await new OmdbProvider(_jsonSerializer, _httpClient, _fileSystem, _appHost, _configurationManager).Fetch(result, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
@@ -240,7 +242,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
QueriedById = true
};
- var imdbId = info.GetProviderId(MetadataProviders.Imdb);
+ var imdbId = info.GetProviderId(MetadataProvider.Imdb);
if (string.IsNullOrWhiteSpace(imdbId))
{
imdbId = await GetMovieImdbId(info, cancellationToken).ConfigureAwait(false);
@@ -249,7 +251,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
if (!string.IsNullOrEmpty(imdbId))
{
- result.Item.SetProviderId(MetadataProviders.Imdb, imdbId);
+ result.Item.SetProviderId(MetadataProvider.Imdb, imdbId);
result.HasMetadata = true;
await new OmdbProvider(_jsonSerializer, _httpClient, _fileSystem, _appHost, _configurationManager).Fetch(result, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
@@ -262,14 +264,14 @@ namespace MediaBrowser.Providers.Plugins.Omdb
{
var results = await GetSearchResultsInternal(info, "movie", false, cancellationToken).ConfigureAwait(false);
var first = results.FirstOrDefault();
- return first == null ? null : first.GetProviderId(MetadataProviders.Imdb);
+ return first == null ? null : first.GetProviderId(MetadataProvider.Imdb);
}
private async Task<string> GetSeriesImdbId(SeriesInfo info, CancellationToken cancellationToken)
{
var results = await GetSearchResultsInternal(info, "series", false, cancellationToken).ConfigureAwait(false);
var first = results.FirstOrDefault();
- return first == null ? null : first.GetProviderId(MetadataProviders.Imdb);
+ return first == null ? null : first.GetProviderId(MetadataProvider.Imdb);
}
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs
index fbdd293ed..19b4bd1e3 100644
--- a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs
@@ -87,14 +87,14 @@ namespace MediaBrowser.Providers.Plugins.Omdb
item.CommunityRating = imdbRating;
}
- //if (!string.IsNullOrEmpty(result.Website))
- //{
- // item.HomePageUrl = result.Website;
- //}
+ if (!string.IsNullOrEmpty(result.Website))
+ {
+ item.HomePageUrl = result.Website;
+ }
if (!string.IsNullOrWhiteSpace(result.imdbID))
{
- item.SetProviderId(MetadataProviders.Imdb, result.imdbID);
+ item.SetProviderId(MetadataProvider.Imdb, result.imdbID);
}
ParseAdditionalMetadata(itemResult, result);
@@ -121,7 +121,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
if (!string.IsNullOrWhiteSpace(episodeImdbId))
{
- foreach (var episode in (seasonResult.Episodes ?? new RootObject[] { }))
+ foreach (var episode in seasonResult.Episodes)
{
if (string.Equals(episodeImdbId, episode.imdbID, StringComparison.OrdinalIgnoreCase))
{
@@ -134,7 +134,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
// finally, search by numbers
if (result == null)
{
- foreach (var episode in (seasonResult.Episodes ?? new RootObject[] { }))
+ foreach (var episode in seasonResult.Episodes)
{
if (episode.Episode == episodeNumber)
{
@@ -188,14 +188,14 @@ namespace MediaBrowser.Providers.Plugins.Omdb
item.CommunityRating = imdbRating;
}
- //if (!string.IsNullOrEmpty(result.Website))
- //{
- // item.HomePageUrl = result.Website;
- //}
+ if (!string.IsNullOrEmpty(result.Website))
+ {
+ item.HomePageUrl = result.Website;
+ }
if (!string.IsNullOrWhiteSpace(result.imdbID))
{
- item.SetProviderId(MetadataProviders.Imdb, result.imdbID);
+ item.SetProviderId(MetadataProvider.Imdb, result.imdbID);
}
ParseAdditionalMetadata(itemResult, result);
@@ -243,7 +243,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
internal static bool IsValidSeries(Dictionary<string, string> seriesProviderIds)
{
- if (seriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out string id) && !string.IsNullOrEmpty(id))
+ if (seriesProviderIds.TryGetValue(MetadataProvider.Imdb.ToString(), out string id) && !string.IsNullOrEmpty(id))
{
// This check should ideally never be necessary but we're seeing some cases of this and haven't tracked them down yet.
if (!string.IsNullOrWhiteSpace(id))
@@ -263,6 +263,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
{
return url;
}
+
return url + "&" + query;
}
@@ -386,7 +387,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
var isConfiguredForEnglish = IsConfiguredForEnglish(item) || _configurationManager.Configuration.EnableNewOmdbSupport;
- // Grab series genres because imdb data is better than tvdb. Leave movies alone
+ // Grab series genres because IMDb data is better than TVDB. Leave movies alone
// But only do it if english is the preferred language because this data will not be localized
if (isConfiguredForEnglish && !string.IsNullOrWhiteSpace(result.Genre))
{
@@ -407,45 +408,50 @@ namespace MediaBrowser.Providers.Plugins.Omdb
item.Overview = result.Plot;
}
- //if (!string.IsNullOrWhiteSpace(result.Director))
- //{
- // var person = new PersonInfo
- // {
- // Name = result.Director.Trim(),
- // Type = PersonType.Director
- // };
-
- // itemResult.AddPerson(person);
- //}
-
- //if (!string.IsNullOrWhiteSpace(result.Writer))
- //{
- // var person = new PersonInfo
- // {
- // Name = result.Director.Trim(),
- // Type = PersonType.Writer
- // };
-
- // itemResult.AddPerson(person);
- //}
-
- //if (!string.IsNullOrWhiteSpace(result.Actors))
- //{
- // var actorList = result.Actors.Split(',');
- // foreach (var actor in actorList)
- // {
- // if (!string.IsNullOrWhiteSpace(actor))
- // {
- // var person = new PersonInfo
- // {
- // Name = actor.Trim(),
- // Type = PersonType.Actor
- // };
-
- // itemResult.AddPerson(person);
- // }
- // }
- //}
+ if (!Plugin.Instance.Configuration.CastAndCrew)
+ {
+ return;
+ }
+
+ if (!string.IsNullOrWhiteSpace(result.Director))
+ {
+ var person = new PersonInfo
+ {
+ Name = result.Director.Trim(),
+ Type = PersonType.Director
+ };
+
+ itemResult.AddPerson(person);
+ }
+
+ if (!string.IsNullOrWhiteSpace(result.Writer))
+ {
+ var person = new PersonInfo
+ {
+ Name = result.Director.Trim(),
+ Type = PersonType.Writer
+ };
+
+ itemResult.AddPerson(person);
+ }
+
+ if (!string.IsNullOrWhiteSpace(result.Actors))
+ {
+ var actorList = result.Actors.Split(',');
+ foreach (var actor in actorList)
+ {
+ if (!string.IsNullOrWhiteSpace(actor))
+ {
+ var person = new PersonInfo
+ {
+ Name = actor.Trim(),
+ Type = PersonType.Actor
+ };
+
+ itemResult.AddPerson(person);
+ }
+ }
+ }
}
private bool IsConfiguredForEnglish(BaseItem item)
@@ -459,40 +465,70 @@ namespace MediaBrowser.Providers.Plugins.Omdb
internal class SeasonRootObject
{
public string Title { get; set; }
+
public string seriesID { get; set; }
+
public int Season { get; set; }
+
public int? totalSeasons { get; set; }
+
public RootObject[] Episodes { get; set; }
+
public string Response { get; set; }
}
internal class RootObject
{
public string Title { get; set; }
+
public string Year { get; set; }
+
public string Rated { get; set; }
+
public string Released { get; set; }
+
public string Runtime { get; set; }
+
public string Genre { get; set; }
+
public string Director { get; set; }
+
public string Writer { get; set; }
+
public string Actors { get; set; }
+
public string Plot { get; set; }
+
public string Language { get; set; }
+
public string Country { get; set; }
+
public string Awards { get; set; }
+
public string Poster { get; set; }
+
public List<OmdbRating> Ratings { get; set; }
+
public string Metascore { get; set; }
+
public string imdbRating { get; set; }
+
public string imdbVotes { get; set; }
+
public string imdbID { get; set; }
+
public string Type { get; set; }
+
public string DVD { get; set; }
+
public string BoxOffice { get; set; }
+
public string Production { get; set; }
+
public string Website { get; set; }
+
public string Response { get; set; }
+
public int Episode { get; set; }
public float? GetRottenTomatoScore()
@@ -509,12 +545,15 @@ namespace MediaBrowser.Providers.Plugins.Omdb
}
}
}
+
return null;
}
}
+
public class OmdbRating
{
public string Source { get; set; }
+
public string Value { get; set; }
}
}
diff --git a/MediaBrowser.Providers/Plugins/Omdb/Plugin.cs b/MediaBrowser.Providers/Plugins/Omdb/Plugin.cs
new file mode 100644
index 000000000..6ce2333e0
--- /dev/null
+++ b/MediaBrowser.Providers/Plugins/Omdb/Plugin.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Collections.Generic;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Plugins;
+using MediaBrowser.Model.Plugins;
+using MediaBrowser.Model.Serialization;
+
+namespace MediaBrowser.Providers.Plugins.Omdb
+{
+ public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
+ {
+ public static Plugin Instance { get; private set; }
+
+ public override Guid Id => new Guid("a628c0da-fac5-4c7e-9d1a-7134223f14c8");
+
+ public override string Name => "OMDb";
+
+ public override string Description => "Get metadata for movies and other video content from OMDb.";
+
+ public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer)
+ : base(applicationPaths, xmlSerializer)
+ {
+ Instance = this;
+ }
+
+ public IEnumerable<PluginPageInfo> GetPages()
+ {
+ yield return new PluginPageInfo
+ {
+ Name = Name,
+ EmbeddedResourcePath = GetType().Namespace + ".Configuration.config.html"
+ };
+ }
+ }
+}
diff --git a/MediaBrowser.Providers/Plugins/TheTvdb/Configuration/PluginConfiguration.cs b/MediaBrowser.Providers/Plugins/TheTvdb/Configuration/PluginConfiguration.cs
new file mode 100644
index 000000000..0a73634dc
--- /dev/null
+++ b/MediaBrowser.Providers/Plugins/TheTvdb/Configuration/PluginConfiguration.cs
@@ -0,0 +1,8 @@
+using MediaBrowser.Model.Plugins;
+
+namespace MediaBrowser.Providers.Plugins.TheTvdb
+{
+ public class PluginConfiguration : BasePluginConfiguration
+ {
+ }
+}
diff --git a/MediaBrowser.Providers/Plugins/TheTvdb/Plugin.cs b/MediaBrowser.Providers/Plugins/TheTvdb/Plugin.cs
new file mode 100644
index 000000000..2e6f548ca
--- /dev/null
+++ b/MediaBrowser.Providers/Plugins/TheTvdb/Plugin.cs
@@ -0,0 +1,24 @@
+using System;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Plugins;
+using MediaBrowser.Model.Serialization;
+
+namespace MediaBrowser.Providers.Plugins.TheTvdb
+{
+ public class Plugin : BasePlugin<PluginConfiguration>
+ {
+ public static Plugin Instance { get; private set; }
+
+ public override Guid Id => new Guid("a677c0da-fac5-4cde-941a-7134223f14c8");
+
+ public override string Name => "TheTVDB";
+
+ public override string Description => "Get metadata for movies and other video content from TheTVDB.";
+
+ public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer)
+ : base(applicationPaths, xmlSerializer)
+ {
+ Instance = this;
+ }
+ }
+}
diff --git a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbClientManager.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbClientManager.cs
index b73834155..38e887be1 100644
--- a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbClientManager.cs
+++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbClientManager.cs
@@ -120,6 +120,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
var cacheKey = GenerateKey("series", zap2ItId, language);
return TryGetValue(cacheKey, language, () => TvDbClient.Search.SearchSeriesByZap2ItIdAsync(zap2ItId, cancellationToken));
}
+
public Task<TvDbResponse<Actor[]>> GetActorsAsync(
int tvdbId,
string language,
@@ -172,7 +173,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
string language,
CancellationToken cancellationToken)
{
- searchInfo.SeriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(),
+ searchInfo.SeriesProviderIds.TryGetValue(MetadataProvider.Tvdb.ToString(),
out var seriesTvdbId);
var episodeQuery = new EpisodeQuery();
@@ -190,7 +191,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
episodeQuery.AbsoluteNumber = searchInfo.IndexNumber.Value;
break;
default:
- //aired order
+ // aired order
episodeQuery.AiredEpisode = searchInfo.IndexNumber.Value;
episodeQuery.AiredSeason = searchInfo.ParentIndexNumber.Value;
break;
diff --git a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeImageProvider.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeImageProvider.cs
index 6118a9c53..1a4c78538 100644
--- a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeImageProvider.cs
@@ -17,7 +17,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
public class TvdbEpisodeImageProvider : IRemoteImageProvider
{
private readonly IHttpClient _httpClient;
- private readonly ILogger _logger;
+ private readonly ILogger<TvdbEpisodeImageProvider> _logger;
private readonly TvdbClientManager _tvdbClientManager;
public TvdbEpisodeImageProvider(IHttpClient httpClient, ILogger<TvdbEpisodeImageProvider> logger, TvdbClientManager tvdbClientManager)
@@ -68,7 +68,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
"Episode {SeasonNumber}x{EpisodeNumber} not found for series {SeriesTvdbId}",
episodeInfo.ParentIndexNumber,
episodeInfo.IndexNumber,
- series.GetProviderId(MetadataProviders.Tvdb));
+ series.GetProviderId(MetadataProvider.Tvdb));
return imageResult;
}
@@ -85,7 +85,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
}
catch (TvDbServerException e)
{
- _logger.LogError(e, "Failed to retrieve episode images for series {TvDbId}", series.GetProviderId(MetadataProviders.Tvdb));
+ _logger.LogError(e, "Failed to retrieve episode images for series {TvDbId}", series.GetProviderId(MetadataProvider.Tvdb));
}
}
diff --git a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeProvider.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeProvider.cs
index 08c2a74d2..b4876c24e 100644
--- a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeProvider.cs
+++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeProvider.cs
@@ -14,14 +14,13 @@ using TvDbSharper.Dto;
namespace MediaBrowser.Providers.Plugins.TheTvdb
{
-
/// <summary>
- /// Class RemoteEpisodeProvider
+ /// Class RemoteEpisodeProvider.
/// </summary>
public class TvdbEpisodeProvider : IRemoteMetadataProvider<Episode, EpisodeInfo>, IHasOrder
{
private readonly IHttpClient _httpClient;
- private readonly ILogger _logger;
+ private readonly ILogger<TvdbEpisodeProvider> _logger;
private readonly TvdbClientManager _tvdbClientManager;
public TvdbEpisodeProvider(IHttpClient httpClient, ILogger<TvdbEpisodeProvider> logger, TvdbClientManager tvdbClientManager)
@@ -95,7 +94,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
QueriedById = true
};
- string seriesTvdbId = searchInfo.GetProviderId(MetadataProviders.Tvdb);
+ string seriesTvdbId = searchInfo.GetProviderId(MetadataProvider.Tvdb);
string episodeTvdbId = null;
try
{
@@ -139,14 +138,13 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
Name = episode.EpisodeName,
Overview = episode.Overview,
CommunityRating = (float?)episode.SiteRating,
-
}
};
result.ResetPeople();
var item = result.Item;
- item.SetProviderId(MetadataProviders.Tvdb, episode.Id.ToString());
- item.SetProviderId(MetadataProviders.Imdb, episode.ImdbId);
+ item.SetProviderId(MetadataProvider.Tvdb, episode.Id.ToString());
+ item.SetProviderId(MetadataProvider.Imdb, episode.ImdbId);
if (string.Equals(id.SeriesDisplayOrder, "dvd", StringComparison.OrdinalIgnoreCase))
{
diff --git a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbPersonImageProvider.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbPersonImageProvider.cs
index c1cdc90e9..9a44573ed 100644
--- a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbPersonImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbPersonImageProvider.cs
@@ -19,7 +19,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
public class TvdbPersonImageProvider : IRemoteImageProvider, IHasOrder
{
private readonly IHttpClient _httpClient;
- private readonly ILogger _logger;
+ private readonly ILogger<TvdbPersonImageProvider> _logger;
private readonly ILibraryManager _libraryManager;
private readonly TvdbClientManager _tvdbClientManager;
@@ -57,7 +57,6 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
{
EnableImages = false
}
-
}).Cast<Series>()
.Where(i => TvdbSeriesProvider.IsValidSeries(i.ProviderIds))
.ToList();
@@ -73,7 +72,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
private async Task<RemoteImageInfo> GetImageFromSeriesData(Series series, string personName, CancellationToken cancellationToken)
{
- var tvdbId = Convert.ToInt32(series.GetProviderId(MetadataProviders.Tvdb));
+ var tvdbId = Convert.ToInt32(series.GetProviderId(MetadataProvider.Tvdb));
try
{
diff --git a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeasonImageProvider.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeasonImageProvider.cs
index a5d183df7..7bd815f76 100644
--- a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeasonImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeasonImageProvider.cs
@@ -19,7 +19,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
public class TvdbSeasonImageProvider : IRemoteImageProvider, IHasOrder
{
private readonly IHttpClient _httpClient;
- private readonly ILogger _logger;
+ private readonly ILogger<TvdbSeasonImageProvider> _logger;
private readonly TvdbClientManager _tvdbClientManager;
public TvdbSeasonImageProvider(IHttpClient httpClient, ILogger<TvdbSeasonImageProvider> logger, TvdbClientManager tvdbClientManager)
@@ -55,10 +55,10 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
if (series == null || !season.IndexNumber.HasValue || !TvdbSeriesProvider.IsValidSeries(series.ProviderIds))
{
- return new RemoteImageInfo[] { };
+ return Array.Empty<RemoteImageInfo>();
}
- var tvdbId = Convert.ToInt32(series.GetProviderId(MetadataProviders.Tvdb));
+ var tvdbId = Convert.ToInt32(series.GetProviderId(MetadataProvider.Tvdb));
var seasonNumber = season.IndexNumber.Value;
var language = item.GetPreferredMetadataLanguage();
var remoteImages = new List<RemoteImageInfo>();
@@ -89,7 +89,8 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
private IEnumerable<RemoteImageInfo> GetImages(Image[] images, string preferredLanguage)
{
var list = new List<RemoteImageInfo>();
- var languages = _tvdbClientManager.GetLanguagesAsync(CancellationToken.None).Result.Data;
+ // any languages with null ids are ignored
+ var languages = _tvdbClientManager.GetLanguagesAsync(CancellationToken.None).Result.Data.Where(x => x.Id.HasValue);
foreach (Image image in images)
{
var imageInfo = new RemoteImageInfo
@@ -113,8 +114,8 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
imageInfo.Type = TvdbUtils.GetImageTypeFromKeyType(image.KeyType);
list.Add(imageInfo);
}
- var isLanguageEn = string.Equals(preferredLanguage, "en", StringComparison.OrdinalIgnoreCase);
+ var isLanguageEn = string.Equals(preferredLanguage, "en", StringComparison.OrdinalIgnoreCase);
return list.OrderByDescending(i =>
{
if (string.Equals(preferredLanguage, i.Language, StringComparison.OrdinalIgnoreCase))
diff --git a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesImageProvider.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesImageProvider.cs
index 1bad60756..50f66945f 100644
--- a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesImageProvider.cs
@@ -19,7 +19,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
public class TvdbSeriesImageProvider : IRemoteImageProvider, IHasOrder
{
private readonly IHttpClient _httpClient;
- private readonly ILogger _logger;
+ private readonly ILogger<TvdbSeriesImageProvider> _logger;
private readonly TvdbClientManager _tvdbClientManager;
public TvdbSeriesImageProvider(IHttpClient httpClient, ILogger<TvdbSeriesImageProvider> logger, TvdbClientManager tvdbClientManager)
@@ -58,7 +58,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
var language = item.GetPreferredMetadataLanguage();
var remoteImages = new List<RemoteImageInfo>();
var keyTypes = new[] { KeyType.Poster, KeyType.Series, KeyType.Fanart };
- var tvdbId = Convert.ToInt32(item.GetProviderId(MetadataProviders.Tvdb));
+ var tvdbId = Convert.ToInt32(item.GetProviderId(MetadataProvider.Tvdb));
foreach (KeyType keyType in keyTypes)
{
var imageQuery = new ImagesQuery
@@ -79,6 +79,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
tvdbId);
}
}
+
return remoteImages;
}
@@ -110,8 +111,8 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
imageInfo.Type = TvdbUtils.GetImageTypeFromKeyType(image.KeyType);
list.Add(imageInfo);
}
- var isLanguageEn = string.Equals(preferredLanguage, "en", StringComparison.OrdinalIgnoreCase);
+ var isLanguageEn = string.Equals(preferredLanguage, "en", StringComparison.OrdinalIgnoreCase);
return list.OrderByDescending(i =>
{
if (string.Equals(preferredLanguage, i.Language, StringComparison.OrdinalIgnoreCase))
diff --git a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesProvider.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesProvider.cs
index f6cd249f5..c9edcc8e9 100644
--- a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesProvider.cs
+++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesProvider.cs
@@ -22,8 +22,9 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
public class TvdbSeriesProvider : IRemoteMetadataProvider<Series, SeriesInfo>, IHasOrder
{
internal static TvdbSeriesProvider Current { get; private set; }
+
private readonly IHttpClient _httpClient;
- private readonly ILogger _logger;
+ private readonly ILogger<TvdbSeriesProvider> _logger;
private readonly ILibraryManager _libraryManager;
private readonly ILocalizationManager _localizationManager;
private readonly TvdbClientManager _tvdbClientManager;
@@ -94,22 +95,22 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
{
var series = result.Item;
- if (seriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), out var tvdbId) && !string.IsNullOrEmpty(tvdbId))
+ if (seriesProviderIds.TryGetValue(MetadataProvider.Tvdb.ToString(), out var tvdbId) && !string.IsNullOrEmpty(tvdbId))
{
- series.SetProviderId(MetadataProviders.Tvdb, tvdbId);
+ series.SetProviderId(MetadataProvider.Tvdb, tvdbId);
}
- if (seriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out var imdbId) && !string.IsNullOrEmpty(imdbId))
+ if (seriesProviderIds.TryGetValue(MetadataProvider.Imdb.ToString(), out var imdbId) && !string.IsNullOrEmpty(imdbId))
{
- series.SetProviderId(MetadataProviders.Imdb, imdbId);
- tvdbId = await GetSeriesByRemoteId(imdbId, MetadataProviders.Imdb.ToString(), metadataLanguage,
+ series.SetProviderId(MetadataProvider.Imdb, imdbId);
+ tvdbId = await GetSeriesByRemoteId(imdbId, MetadataProvider.Imdb.ToString(), metadataLanguage,
cancellationToken).ConfigureAwait(false);
}
- if (seriesProviderIds.TryGetValue(MetadataProviders.Zap2It.ToString(), out var zap2It) && !string.IsNullOrEmpty(zap2It))
+ if (seriesProviderIds.TryGetValue(MetadataProvider.Zap2It.ToString(), out var zap2It) && !string.IsNullOrEmpty(zap2It))
{
- series.SetProviderId(MetadataProviders.Zap2It, zap2It);
- tvdbId = await GetSeriesByRemoteId(zap2It, MetadataProviders.Zap2It.ToString(), metadataLanguage,
+ series.SetProviderId(MetadataProvider.Zap2It, zap2It);
+ tvdbId = await GetSeriesByRemoteId(zap2It, MetadataProvider.Zap2It.ToString(), metadataLanguage,
cancellationToken).ConfigureAwait(false);
}
@@ -145,12 +146,11 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
private async Task<string> GetSeriesByRemoteId(string id, string idType, string language, CancellationToken cancellationToken)
{
-
TvDbResponse<SeriesSearchResult[]> result = null;
try
{
- if (string.Equals(idType, MetadataProviders.Zap2It.ToString(), StringComparison.OrdinalIgnoreCase))
+ if (string.Equals(idType, MetadataProvider.Zap2It.ToString(), StringComparison.OrdinalIgnoreCase))
{
result = await _tvdbClientManager.GetSeriesByZap2ItIdAsync(id, language, cancellationToken)
.ConfigureAwait(false);
@@ -176,9 +176,9 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
/// <returns>True, if the dictionary contains a valid TV provider ID, otherwise false.</returns>
internal static bool IsValidSeries(Dictionary<string, string> seriesProviderIds)
{
- return seriesProviderIds.ContainsKey(MetadataProviders.Tvdb.ToString()) ||
- seriesProviderIds.ContainsKey(MetadataProviders.Imdb.ToString()) ||
- seriesProviderIds.ContainsKey(MetadataProviders.Zap2It.ToString());
+ return seriesProviderIds.ContainsKey(MetadataProvider.Tvdb.ToString()) ||
+ seriesProviderIds.ContainsKey(MetadataProvider.Imdb.ToString()) ||
+ seriesProviderIds.ContainsKey(MetadataProvider.Zap2It.ToString());
}
/// <summary>
@@ -249,20 +249,21 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
ImageUrl = TvdbUtils.BannerUrl + seriesSearchResult.Banner
};
+
try
{
var seriesSesult =
await _tvdbClientManager.GetSeriesByIdAsync(seriesSearchResult.Id, language, cancellationToken)
.ConfigureAwait(false);
- remoteSearchResult.SetProviderId(MetadataProviders.Imdb, seriesSesult.Data.ImdbId);
- remoteSearchResult.SetProviderId(MetadataProviders.Zap2It, seriesSesult.Data.Zap2itId);
+ remoteSearchResult.SetProviderId(MetadataProvider.Imdb, seriesSesult.Data.ImdbId);
+ remoteSearchResult.SetProviderId(MetadataProvider.Zap2It, seriesSesult.Data.Zap2itId);
}
catch (TvDbServerException e)
{
_logger.LogError(e, "Unable to retrieve series with id {TvdbId}", seriesSearchResult.Id);
}
- remoteSearchResult.SetProviderId(MetadataProviders.Tvdb, seriesSearchResult.Id.ToString());
+ remoteSearchResult.SetProviderId(MetadataProvider.Tvdb, seriesSearchResult.Id.ToString());
list.Add(new Tuple<List<string>, RemoteSearchResult>(tvdbTitles, remoteSearchResult));
}
@@ -274,15 +275,6 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
}
/// <summary>
- /// The remove
- /// </summary>
- const string remove = "\"'!`?";
- /// <summary>
- /// The spacers
- /// </summary>
- const string spacers = "/,.:;\\(){}[]+-_=–*"; // (there are two types of dashes, short and long)
-
- /// <summary>
/// Gets the name of the comparable.
/// </summary>
/// <param name="name">The name.</param>
@@ -291,47 +283,25 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
{
name = name.ToLowerInvariant();
name = name.Normalize(NormalizationForm.FormKD);
- var sb = new StringBuilder();
- foreach (var c in name)
- {
- if (c >= 0x2B0 && c <= 0x0333)
- {
- // skip char modifier and diacritics
- }
- else if (remove.IndexOf(c) > -1)
- {
- // skip chars we are removing
- }
- else if (spacers.IndexOf(c) > -1)
- {
- sb.Append(" ");
- }
- else if (c == '&')
- {
- sb.Append(" and ");
- }
- else
- {
- sb.Append(c);
- }
- }
- sb.Replace(", the", string.Empty).Replace("the ", " ").Replace(" the ", " ");
-
- return Regex.Replace(sb.ToString().Trim(), @"\s+", " ");
+ name = name.Replace(", the", string.Empty).Replace("the ", " ").Replace(" the ", " ");
+ name = name.Replace("&", " and " );
+ name = Regex.Replace(name, @"[\p{Lm}\p{Mn}]", string.Empty); // Remove diacritics, etc
+ name = Regex.Replace(name, @"[\W\p{Pc}]+", " "); // Replace sequences of non-word characters and _ with " "
+ return name.Trim();
}
private void MapSeriesToResult(MetadataResult<Series> result, TvDbSharper.Dto.Series tvdbSeries, string metadataLanguage)
{
Series series = result.Item;
- series.SetProviderId(MetadataProviders.Tvdb, tvdbSeries.Id.ToString());
+ series.SetProviderId(MetadataProvider.Tvdb, tvdbSeries.Id.ToString());
series.Name = tvdbSeries.SeriesName;
series.Overview = (tvdbSeries.Overview ?? string.Empty).Trim();
result.ResultLanguage = metadataLanguage;
series.AirDays = TVUtils.GetAirDays(tvdbSeries.AirsDayOfWeek);
series.AirTime = tvdbSeries.AirsTime;
series.CommunityRating = (float?)tvdbSeries.SiteRating;
- series.SetProviderId(MetadataProviders.Imdb, tvdbSeries.ImdbId);
- series.SetProviderId(MetadataProviders.Zap2It, tvdbSeries.Zap2itId);
+ series.SetProviderId(MetadataProvider.Imdb, tvdbSeries.ImdbId);
+ series.SetProviderId(MetadataProvider.Zap2It, tvdbSeries.Zap2itId);
if (Enum.TryParse(tvdbSeries.Status, true, out SeriesStatus seriesStatus))
{
series.Status = seriesStatus;
@@ -409,7 +379,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
public async Task Identify(SeriesInfo info)
{
- if (!string.IsNullOrWhiteSpace(info.GetProviderId(MetadataProviders.Tvdb)))
+ if (!string.IsNullOrWhiteSpace(info.GetProviderId(MetadataProvider.Tvdb)))
{
return;
}
@@ -421,8 +391,8 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
if (entry != null)
{
- var id = entry.GetProviderId(MetadataProviders.Tvdb);
- info.SetProviderId(MetadataProviders.Tvdb, id);
+ var id = entry.GetProviderId(MetadataProvider.Tvdb);
+ info.SetProviderId(MetadataProvider.Tvdb, id);
}
}
diff --git a/MediaBrowser.Providers/Tmdb/BoxSets/TmdbBoxSetExternalId.cs b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetExternalId.cs
index 187295e1e..ad0851cef 100644
--- a/MediaBrowser.Providers/Tmdb/BoxSets/TmdbBoxSetExternalId.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetExternalId.cs
@@ -3,7 +3,7 @@ using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
-namespace MediaBrowser.Providers.Tmdb.BoxSets
+namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets
{
public class TmdbBoxSetExternalId : IExternalId
{
@@ -11,7 +11,7 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets
public string Name => TmdbUtils.ProviderName;
/// <inheritdoc />
- public string Key => MetadataProviders.TmdbCollection.ToString();
+ public string Key => MetadataProvider.TmdbCollection.ToString();
/// <inheritdoc />
public string UrlFormatString => TmdbUtils.BaseTmdbUrl + "collection/{0}";
diff --git a/MediaBrowser.Providers/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs
index 0bdf2bce1..23eb00b5c 100644
--- a/MediaBrowser.Providers/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs
@@ -10,11 +10,11 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
-using MediaBrowser.Providers.Tmdb.Models.Collections;
-using MediaBrowser.Providers.Tmdb.Models.General;
-using MediaBrowser.Providers.Tmdb.Movies;
+using MediaBrowser.Providers.Plugins.Tmdb.Models.Collections;
+using MediaBrowser.Providers.Plugins.Tmdb.Models.General;
+using MediaBrowser.Providers.Plugins.Tmdb.Movies;
-namespace MediaBrowser.Providers.Tmdb.BoxSets
+namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets
{
public class TmdbBoxSetImageProvider : IRemoteImageProvider, IHasOrder
{
@@ -45,7 +45,7 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets
public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken)
{
- var tmdbId = item.GetProviderId(MetadataProviders.Tmdb);
+ var tmdbId = item.GetProviderId(MetadataProvider.Tmdb);
if (!string.IsNullOrEmpty(tmdbId))
{
@@ -105,6 +105,7 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets
{
return 3;
}
+
if (!isLanguageEn)
{
if (string.Equals("en", i.Language, StringComparison.OrdinalIgnoreCase))
@@ -112,10 +113,12 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets
return 2;
}
}
+
if (string.IsNullOrEmpty(i.Language))
{
return isLanguageEn ? 3 : 2;
}
+
return 0;
})
.ThenByDescending(i => i.CommunityRating ?? 0)
diff --git a/MediaBrowser.Providers/Tmdb/BoxSets/TmdbBoxSetProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs
index dd3783ffb..15f0a9004 100644
--- a/MediaBrowser.Providers/Tmdb/BoxSets/TmdbBoxSetProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs
@@ -16,12 +16,12 @@ using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Serialization;
-using MediaBrowser.Providers.Tmdb.Models.Collections;
-using MediaBrowser.Providers.Tmdb.Models.General;
-using MediaBrowser.Providers.Tmdb.Movies;
+using MediaBrowser.Providers.Plugins.Tmdb.Models.Collections;
+using MediaBrowser.Providers.Plugins.Tmdb.Models.General;
+using MediaBrowser.Providers.Plugins.Tmdb.Movies;
using Microsoft.Extensions.Logging;
-namespace MediaBrowser.Providers.Tmdb.BoxSets
+namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets
{
public class TmdbBoxSetProvider : IRemoteMetadataProvider<BoxSet, BoxSetInfo>
{
@@ -29,7 +29,7 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets
internal static TmdbBoxSetProvider Current;
- private readonly ILogger _logger;
+ private readonly ILogger<TmdbBoxSetProvider> _logger;
private readonly IJsonSerializer _json;
private readonly IServerConfigurationManager _config;
private readonly IFileSystem _fileSystem;
@@ -60,7 +60,7 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets
public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(BoxSetInfo searchInfo, CancellationToken cancellationToken)
{
- var tmdbId = searchInfo.GetProviderId(MetadataProviders.Tmdb);
+ var tmdbId = searchInfo.GetProviderId(MetadataProvider.Tmdb);
if (!string.IsNullOrEmpty(tmdbId))
{
@@ -78,13 +78,11 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets
var result = new RemoteSearchResult
{
Name = info.Name,
-
SearchProviderName = Name,
-
ImageUrl = images.Count == 0 ? null : (tmdbImageUrl + images[0].File_Path)
};
- result.SetProviderId(MetadataProviders.Tmdb, info.Id.ToString(_usCulture));
+ result.SetProviderId(MetadataProvider.Tmdb, info.Id.ToString(_usCulture));
return new[] { result };
}
@@ -94,7 +92,7 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets
public async Task<MetadataResult<BoxSet>> GetMetadata(BoxSetInfo id, CancellationToken cancellationToken)
{
- var tmdbId = id.GetProviderId(MetadataProviders.Tmdb);
+ var tmdbId = id.GetProviderId(MetadataProvider.Tmdb);
// We don't already have an Id, need to fetch it
if (string.IsNullOrEmpty(tmdbId))
@@ -105,7 +103,7 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets
if (searchResult != null)
{
- tmdbId = searchResult.GetProviderId(MetadataProviders.Tmdb);
+ tmdbId = searchResult.GetProviderId(MetadataProvider.Tmdb);
}
}
@@ -152,7 +150,7 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets
Overview = obj.Overview
};
- item.SetProviderId(MetadataProviders.Tmdb, obj.Id.ToString(_usCulture));
+ item.SetProviderId(MetadataProvider.Tmdb, obj.Id.ToString(_usCulture));
return item;
}
@@ -191,7 +189,6 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets
Url = url,
CancellationToken = cancellationToken,
AcceptHeader = TmdbUtils.AcceptHeader
-
}).ConfigureAwait(false))
{
using (var json = response.Content)
@@ -219,7 +216,6 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets
Url = url,
CancellationToken = cancellationToken,
AcceptHeader = TmdbUtils.AcceptHeader
-
}).ConfigureAwait(false))
{
using (var json = response.Content)
@@ -229,6 +225,7 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets
}
}
}
+
return mainResult;
}
diff --git a/MediaBrowser.Providers/Tmdb/Models/Collections/CollectionImages.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/Collections/CollectionImages.cs
index 18f26c397..2410ca16b 100644
--- a/MediaBrowser.Providers/Tmdb/Models/Collections/CollectionImages.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/Collections/CollectionImages.cs
@@ -1,7 +1,7 @@
using System.Collections.Generic;
-using MediaBrowser.Providers.Tmdb.Models.General;
+using MediaBrowser.Providers.Plugins.Tmdb.Models.General;
-namespace MediaBrowser.Providers.Tmdb.Models.Collections
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Collections
{
public class CollectionImages
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/Collections/CollectionResult.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/Collections/CollectionResult.cs
index 53d2599f8..3437552df 100644
--- a/MediaBrowser.Providers/Tmdb/Models/Collections/CollectionResult.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/Collections/CollectionResult.cs
@@ -1,6 +1,6 @@
using System.Collections.Generic;
-namespace MediaBrowser.Providers.Tmdb.Models.Collections
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Collections
{
public class CollectionResult
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/Collections/Part.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/Collections/Part.cs
index ff19291c7..462fdab53 100644
--- a/MediaBrowser.Providers/Tmdb/Models/Collections/Part.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/Collections/Part.cs
@@ -1,4 +1,4 @@
-namespace MediaBrowser.Providers.Tmdb.Models.Collections
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Collections
{
public class Part
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/General/Backdrop.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Backdrop.cs
index db4cd6681..35e3e2112 100644
--- a/MediaBrowser.Providers/Tmdb/Models/General/Backdrop.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Backdrop.cs
@@ -1,4 +1,4 @@
-namespace MediaBrowser.Providers.Tmdb.Models.General
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General
{
public class Backdrop
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/General/Crew.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Crew.cs
index 47b985403..6a5e74ddb 100644
--- a/MediaBrowser.Providers/Tmdb/Models/General/Crew.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Crew.cs
@@ -1,4 +1,4 @@
-namespace MediaBrowser.Providers.Tmdb.Models.General
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General
{
public class Crew
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/General/ExternalIds.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/ExternalIds.cs
index 37e37b0be..a083f6e9c 100644
--- a/MediaBrowser.Providers/Tmdb/Models/General/ExternalIds.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/ExternalIds.cs
@@ -1,4 +1,4 @@
-namespace MediaBrowser.Providers.Tmdb.Models.General
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General
{
public class ExternalIds
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/General/Genre.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Genre.cs
index 9a6686d50..7f1a394c3 100644
--- a/MediaBrowser.Providers/Tmdb/Models/General/Genre.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Genre.cs
@@ -1,4 +1,4 @@
-namespace MediaBrowser.Providers.Tmdb.Models.General
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General
{
public class Genre
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/General/Images.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Images.cs
index f1c99537d..166f9b740 100644
--- a/MediaBrowser.Providers/Tmdb/Models/General/Images.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Images.cs
@@ -1,6 +1,6 @@
using System.Collections.Generic;
-namespace MediaBrowser.Providers.Tmdb.Models.General
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General
{
public class Images
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/General/Keyword.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Keyword.cs
index 4e3011349..72f417be5 100644
--- a/MediaBrowser.Providers/Tmdb/Models/General/Keyword.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Keyword.cs
@@ -1,4 +1,4 @@
-namespace MediaBrowser.Providers.Tmdb.Models.General
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General
{
public class Keyword
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/General/Keywords.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Keywords.cs
index 1950a51b3..ec2d7a035 100644
--- a/MediaBrowser.Providers/Tmdb/Models/General/Keywords.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Keywords.cs
@@ -1,6 +1,6 @@
using System.Collections.Generic;
-namespace MediaBrowser.Providers.Tmdb.Models.General
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General
{
public class Keywords
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/General/Poster.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Poster.cs
index 33401b15d..0cf04a6ce 100644
--- a/MediaBrowser.Providers/Tmdb/Models/General/Poster.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Poster.cs
@@ -1,4 +1,4 @@
-namespace MediaBrowser.Providers.Tmdb.Models.General
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General
{
public class Poster
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/General/Profile.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Profile.cs
index f87d14850..b45cfc30f 100644
--- a/MediaBrowser.Providers/Tmdb/Models/General/Profile.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Profile.cs
@@ -1,4 +1,4 @@
-namespace MediaBrowser.Providers.Tmdb.Models.General
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General
{
public class Profile
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/General/Still.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Still.cs
index 15ff4a099..9fc82cfee 100644
--- a/MediaBrowser.Providers/Tmdb/Models/General/Still.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Still.cs
@@ -1,4 +1,4 @@
-namespace MediaBrowser.Providers.Tmdb.Models.General
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General
{
public class Still
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/General/StillImages.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/StillImages.cs
index 266965c47..23af4b697 100644
--- a/MediaBrowser.Providers/Tmdb/Models/General/StillImages.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/StillImages.cs
@@ -1,6 +1,6 @@
using System.Collections.Generic;
-namespace MediaBrowser.Providers.Tmdb.Models.General
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General
{
public class StillImages
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/General/Video.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Video.cs
index fb69e7767..19bfd62f6 100644
--- a/MediaBrowser.Providers/Tmdb/Models/General/Video.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Video.cs
@@ -1,4 +1,4 @@
-namespace MediaBrowser.Providers.Tmdb.Models.General
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General
{
public class Video
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/General/Videos.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Videos.cs
index 26812780d..26e839de7 100644
--- a/MediaBrowser.Providers/Tmdb/Models/General/Videos.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Videos.cs
@@ -1,6 +1,6 @@
using System.Collections.Generic;
-namespace MediaBrowser.Providers.Tmdb.Models.General
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General
{
public class Videos
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/Movies/BelongsToCollection.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/BelongsToCollection.cs
index ac673df61..aaca57f05 100644
--- a/MediaBrowser.Providers/Tmdb/Models/Movies/BelongsToCollection.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/BelongsToCollection.cs
@@ -1,4 +1,4 @@
-namespace MediaBrowser.Providers.Tmdb.Models.Movies
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Movies
{
public class BelongsToCollection
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/Movies/Cast.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/Cast.cs
index 44af9e568..d70f218aa 100644
--- a/MediaBrowser.Providers/Tmdb/Models/Movies/Cast.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/Cast.cs
@@ -1,4 +1,4 @@
-namespace MediaBrowser.Providers.Tmdb.Models.Movies
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Movies
{
public class Cast
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/Movies/Casts.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/Casts.cs
index 7b5094fa3..c41699bc7 100644
--- a/MediaBrowser.Providers/Tmdb/Models/Movies/Casts.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/Casts.cs
@@ -1,7 +1,7 @@
using System.Collections.Generic;
-using MediaBrowser.Providers.Tmdb.Models.General;
+using MediaBrowser.Providers.Plugins.Tmdb.Models.General;
-namespace MediaBrowser.Providers.Tmdb.Models.Movies
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Movies
{
public class Casts
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/Movies/Country.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/Country.cs
index 6f843addd..71d1f7c24 100644
--- a/MediaBrowser.Providers/Tmdb/Models/Movies/Country.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/Country.cs
@@ -1,6 +1,6 @@
using System;
-namespace MediaBrowser.Providers.Tmdb.Models.Movies
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Movies
{
public class Country
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/Movies/MovieResult.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/MovieResult.cs
index 1b262946f..2a9b9779a 100644
--- a/MediaBrowser.Providers/Tmdb/Models/Movies/MovieResult.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/MovieResult.cs
@@ -1,7 +1,7 @@
using System.Collections.Generic;
-using MediaBrowser.Providers.Tmdb.Models.General;
+using MediaBrowser.Providers.Plugins.Tmdb.Models.General;
-namespace MediaBrowser.Providers.Tmdb.Models.Movies
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Movies
{
public class MovieResult
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/Movies/ProductionCompany.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/ProductionCompany.cs
index c3382f305..11158ade5 100644
--- a/MediaBrowser.Providers/Tmdb/Models/Movies/ProductionCompany.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/ProductionCompany.cs
@@ -1,4 +1,4 @@
-namespace MediaBrowser.Providers.Tmdb.Models.Movies
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Movies
{
public class ProductionCompany
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/Movies/ProductionCountry.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/ProductionCountry.cs
index 78112c915..43d00fe7a 100644
--- a/MediaBrowser.Providers/Tmdb/Models/Movies/ProductionCountry.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/ProductionCountry.cs
@@ -1,4 +1,4 @@
-namespace MediaBrowser.Providers.Tmdb.Models.Movies
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Movies
{
public class ProductionCountry
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/Movies/Releases.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/Releases.cs
index c44f31e46..d35111dc4 100644
--- a/MediaBrowser.Providers/Tmdb/Models/Movies/Releases.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/Releases.cs
@@ -1,6 +1,6 @@
using System.Collections.Generic;
-namespace MediaBrowser.Providers.Tmdb.Models.Movies
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Movies
{
public class Releases
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/Movies/SpokenLanguage.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/SpokenLanguage.cs
index 4bc5cfa48..41defa9d0 100644
--- a/MediaBrowser.Providers/Tmdb/Models/Movies/SpokenLanguage.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/SpokenLanguage.cs
@@ -1,4 +1,4 @@
-namespace MediaBrowser.Providers.Tmdb.Models.Movies
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Movies
{
public class SpokenLanguage
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/Movies/Trailers.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/Trailers.cs
index 4bfa02f06..bdc40b483 100644
--- a/MediaBrowser.Providers/Tmdb/Models/Movies/Trailers.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/Trailers.cs
@@ -1,6 +1,6 @@
using System.Collections.Generic;
-namespace MediaBrowser.Providers.Tmdb.Models.Movies
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Movies
{
public class Trailers
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/Movies/Youtube.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/Youtube.cs
index 069572824..6be4ef5b5 100644
--- a/MediaBrowser.Providers/Tmdb/Models/Movies/Youtube.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/Youtube.cs
@@ -1,4 +1,4 @@
-namespace MediaBrowser.Providers.Tmdb.Models.Movies
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Movies
{
public class Youtube
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/People/PersonImages.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/People/PersonImages.cs
index 113f410b2..59423c7bc 100644
--- a/MediaBrowser.Providers/Tmdb/Models/People/PersonImages.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/People/PersonImages.cs
@@ -1,7 +1,7 @@
using System.Collections.Generic;
-using MediaBrowser.Providers.Tmdb.Models.General;
+using MediaBrowser.Providers.Plugins.Tmdb.Models.General;
-namespace MediaBrowser.Providers.Tmdb.Models.People
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.People
{
public class PersonImages
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/People/PersonResult.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/People/PersonResult.cs
index 6e997050f..50c47eefd 100644
--- a/MediaBrowser.Providers/Tmdb/Models/People/PersonResult.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/People/PersonResult.cs
@@ -1,7 +1,7 @@
using System.Collections.Generic;
-using MediaBrowser.Providers.Tmdb.Models.General;
+using MediaBrowser.Providers.Plugins.Tmdb.Models.General;
-namespace MediaBrowser.Providers.Tmdb.Models.People
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.People
{
public class PersonResult
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/Search/ExternalIdLookupResult.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/Search/ExternalIdLookupResult.cs
index d19f4e8cb..62b12aa97 100644
--- a/MediaBrowser.Providers/Tmdb/Models/Search/ExternalIdLookupResult.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/Search/ExternalIdLookupResult.cs
@@ -1,6 +1,6 @@
using System.Collections.Generic;
-namespace MediaBrowser.Providers.Tmdb.Models.Search
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Search
{
public class ExternalIdLookupResult
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/Search/MovieResult.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/Search/MovieResult.cs
index 245162728..51c26a61c 100644
--- a/MediaBrowser.Providers/Tmdb/Models/Search/MovieResult.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/Search/MovieResult.cs
@@ -1,4 +1,4 @@
-namespace MediaBrowser.Providers.Tmdb.Models.Search
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Search
{
public class MovieResult
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/Search/PersonSearchResult.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/Search/PersonSearchResult.cs
index 93916068f..c3ad7253a 100644
--- a/MediaBrowser.Providers/Tmdb/Models/Search/PersonSearchResult.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/Search/PersonSearchResult.cs
@@ -1,4 +1,4 @@
-namespace MediaBrowser.Providers.Tmdb.Models.Search
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Search
{
public class PersonSearchResult
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/Search/TmdbSearchResult.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/Search/TmdbSearchResult.cs
index a9f888e75..7a33acbc7 100644
--- a/MediaBrowser.Providers/Tmdb/Models/Search/TmdbSearchResult.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/Search/TmdbSearchResult.cs
@@ -1,6 +1,6 @@
using System.Collections.Generic;
-namespace MediaBrowser.Providers.Tmdb.Models.Search
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Search
{
public class TmdbSearchResult<T>
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/Search/TvResult.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/Search/TvResult.cs
index ed140bedd..b7fbd294c 100644
--- a/MediaBrowser.Providers/Tmdb/Models/Search/TvResult.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/Search/TvResult.cs
@@ -1,4 +1,4 @@
-namespace MediaBrowser.Providers.Tmdb.Models.Search
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Search
{
public class TvResult
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/TV/Cast.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/Cast.cs
index c659df9ac..9c770545c 100644
--- a/MediaBrowser.Providers/Tmdb/Models/TV/Cast.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/Cast.cs
@@ -1,4 +1,4 @@
-namespace MediaBrowser.Providers.Tmdb.Models.TV
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.TV
{
public class Cast
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/TV/ContentRating.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/ContentRating.cs
index 3177cd71b..bccb234e7 100644
--- a/MediaBrowser.Providers/Tmdb/Models/TV/ContentRating.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/ContentRating.cs
@@ -1,4 +1,4 @@
-namespace MediaBrowser.Providers.Tmdb.Models.TV
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.TV
{
public class ContentRating
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/TV/ContentRatings.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/ContentRatings.cs
index 883e605c9..360c20c66 100644
--- a/MediaBrowser.Providers/Tmdb/Models/TV/ContentRatings.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/ContentRatings.cs
@@ -1,6 +1,6 @@
using System.Collections.Generic;
-namespace MediaBrowser.Providers.Tmdb.Models.TV
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.TV
{
public class ContentRatings
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/TV/CreatedBy.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/CreatedBy.cs
index 21588d897..35e8eaecb 100644
--- a/MediaBrowser.Providers/Tmdb/Models/TV/CreatedBy.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/CreatedBy.cs
@@ -1,4 +1,4 @@
-namespace MediaBrowser.Providers.Tmdb.Models.TV
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.TV
{
public class CreatedBy
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/TV/Credits.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/Credits.cs
index b62b5f605..ebf412c2d 100644
--- a/MediaBrowser.Providers/Tmdb/Models/TV/Credits.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/Credits.cs
@@ -1,7 +1,7 @@
using System.Collections.Generic;
-using MediaBrowser.Providers.Tmdb.Models.General;
+using MediaBrowser.Providers.Plugins.Tmdb.Models.General;
-namespace MediaBrowser.Providers.Tmdb.Models.TV
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.TV
{
public class Credits
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/TV/Episode.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/Episode.cs
index ab11a6cd2..8203632b7 100644
--- a/MediaBrowser.Providers/Tmdb/Models/TV/Episode.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/Episode.cs
@@ -1,4 +1,4 @@
-namespace MediaBrowser.Providers.Tmdb.Models.TV
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.TV
{
public class Episode
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/TV/EpisodeCredits.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/EpisodeCredits.cs
index 1c86be0f4..f89859f85 100644
--- a/MediaBrowser.Providers/Tmdb/Models/TV/EpisodeCredits.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/EpisodeCredits.cs
@@ -1,7 +1,7 @@
using System.Collections.Generic;
-using MediaBrowser.Providers.Tmdb.Models.General;
+using MediaBrowser.Providers.Plugins.Tmdb.Models.General;
-namespace MediaBrowser.Providers.Tmdb.Models.TV
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.TV
{
public class EpisodeCredits
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/TV/EpisodeResult.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/EpisodeResult.cs
index 0513ce7e2..e25b65d70 100644
--- a/MediaBrowser.Providers/Tmdb/Models/TV/EpisodeResult.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/EpisodeResult.cs
@@ -1,7 +1,7 @@
using System;
-using MediaBrowser.Providers.Tmdb.Models.General;
+using MediaBrowser.Providers.Plugins.Tmdb.Models.General;
-namespace MediaBrowser.Providers.Tmdb.Models.TV
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.TV
{
public class EpisodeResult
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/TV/GuestStar.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/GuestStar.cs
index 2dfe7a862..260f3f610 100644
--- a/MediaBrowser.Providers/Tmdb/Models/TV/GuestStar.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/GuestStar.cs
@@ -1,4 +1,4 @@
-namespace MediaBrowser.Providers.Tmdb.Models.TV
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.TV
{
public class GuestStar
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/TV/Network.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/Network.cs
index f982682d1..5ed310827 100644
--- a/MediaBrowser.Providers/Tmdb/Models/TV/Network.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/Network.cs
@@ -1,4 +1,4 @@
-namespace MediaBrowser.Providers.Tmdb.Models.TV
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.TV
{
public class Network
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/TV/Season.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/Season.cs
index 976e3c97e..fddf950ee 100644
--- a/MediaBrowser.Providers/Tmdb/Models/TV/Season.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/Season.cs
@@ -1,4 +1,4 @@
-namespace MediaBrowser.Providers.Tmdb.Models.TV
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.TV
{
public class Season
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/TV/SeasonImages.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/SeasonImages.cs
index 9a93dd6ae..13f6d57c8 100644
--- a/MediaBrowser.Providers/Tmdb/Models/TV/SeasonImages.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/SeasonImages.cs
@@ -1,7 +1,7 @@
using System.Collections.Generic;
-using MediaBrowser.Providers.Tmdb.Models.General;
+using MediaBrowser.Providers.Plugins.Tmdb.Models.General;
-namespace MediaBrowser.Providers.Tmdb.Models.TV
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.TV
{
public class SeasonImages
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/TV/SeasonResult.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/SeasonResult.cs
index bc9213c04..13b4c30f8 100644
--- a/MediaBrowser.Providers/Tmdb/Models/TV/SeasonResult.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/SeasonResult.cs
@@ -1,8 +1,8 @@
using System;
using System.Collections.Generic;
-using MediaBrowser.Providers.Tmdb.Models.General;
+using MediaBrowser.Providers.Plugins.Tmdb.Models.General;
-namespace MediaBrowser.Providers.Tmdb.Models.TV
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.TV
{
public class SeasonResult
{
diff --git a/MediaBrowser.Providers/Tmdb/Models/TV/SeriesResult.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/SeriesResult.cs
index ad95e502e..5c1666c77 100644
--- a/MediaBrowser.Providers/Tmdb/Models/TV/SeriesResult.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/SeriesResult.cs
@@ -1,8 +1,8 @@
using System;
using System.Collections.Generic;
-using MediaBrowser.Providers.Tmdb.Models.General;
+using MediaBrowser.Providers.Plugins.Tmdb.Models.General;
-namespace MediaBrowser.Providers.Tmdb.Models.TV
+namespace MediaBrowser.Providers.Plugins.Tmdb.Models.TV
{
public class SeriesResult
{
diff --git a/MediaBrowser.Providers/Tmdb/Movies/GenericTmdbMovieInfo.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/GenericTmdbMovieInfo.cs
index ad42b564c..60f37dc17 100644
--- a/MediaBrowser.Providers/Tmdb/Movies/GenericTmdbMovieInfo.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/GenericTmdbMovieInfo.cs
@@ -13,10 +13,10 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
-using MediaBrowser.Providers.Tmdb.Models.Movies;
+using MediaBrowser.Providers.Plugins.Tmdb.Models.Movies;
using Microsoft.Extensions.Logging;
-namespace MediaBrowser.Providers.Tmdb.Movies
+namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
{
public class GenericTmdbMovieInfo<T>
where T : BaseItem, new()
@@ -38,8 +38,8 @@ namespace MediaBrowser.Providers.Tmdb.Movies
public async Task<MetadataResult<T>> GetMetadata(ItemLookupInfo itemId, CancellationToken cancellationToken)
{
- var tmdbId = itemId.GetProviderId(MetadataProviders.Tmdb);
- var imdbId = itemId.GetProviderId(MetadataProviders.Imdb);
+ var tmdbId = itemId.GetProviderId(MetadataProvider.Tmdb);
+ var imdbId = itemId.GetProviderId(MetadataProvider.Imdb);
// Don't search for music video id's because it is very easy to misidentify.
if (string.IsNullOrEmpty(tmdbId) && string.IsNullOrEmpty(imdbId) && typeof(T) != typeof(MusicVideo))
@@ -50,7 +50,7 @@ namespace MediaBrowser.Providers.Tmdb.Movies
if (searchResult != null)
{
- tmdbId = searchResult.GetProviderId(MetadataProviders.Tmdb);
+ tmdbId = searchResult.GetProviderId(MetadataProvider.Tmdb);
}
}
@@ -146,12 +146,12 @@ namespace MediaBrowser.Providers.Tmdb.Movies
.ToArray();
}
- movie.SetProviderId(MetadataProviders.Tmdb, movieData.Id.ToString(_usCulture));
- movie.SetProviderId(MetadataProviders.Imdb, movieData.Imdb_Id);
+ movie.SetProviderId(MetadataProvider.Tmdb, movieData.Id.ToString(_usCulture));
+ movie.SetProviderId(MetadataProvider.Imdb, movieData.Imdb_Id);
if (movieData.Belongs_To_Collection != null)
{
- movie.SetProviderId(MetadataProviders.TmdbCollection,
+ movie.SetProviderId(MetadataProvider.TmdbCollection,
movieData.Belongs_To_Collection.Id.ToString(CultureInfo.InvariantCulture));
if (movie is Movie movieItem)
@@ -240,7 +240,7 @@ namespace MediaBrowser.Providers.Tmdb.Movies
if (actor.Id > 0)
{
- personInfo.SetProviderId(MetadataProviders.Tmdb, actor.Id.ToString(CultureInfo.InvariantCulture));
+ personInfo.SetProviderId(MetadataProvider.Tmdb, actor.Id.ToString(CultureInfo.InvariantCulture));
}
resultItem.AddPerson(personInfo);
@@ -282,7 +282,7 @@ namespace MediaBrowser.Providers.Tmdb.Movies
if (person.Id > 0)
{
- personInfo.SetProviderId(MetadataProviders.Tmdb, person.Id.ToString(CultureInfo.InvariantCulture));
+ personInfo.SetProviderId(MetadataProvider.Tmdb, person.Id.ToString(CultureInfo.InvariantCulture));
}
resultItem.AddPerson(personInfo);
diff --git a/MediaBrowser.Providers/Tmdb/Movies/TmdbImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbImageProvider.cs
index 039a49728..a11c89459 100644
--- a/MediaBrowser.Providers/Tmdb/Movies/TmdbImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbImageProvider.cs
@@ -13,10 +13,10 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Serialization;
-using MediaBrowser.Providers.Tmdb.Models.General;
-using MediaBrowser.Providers.Tmdb.Models.Movies;
+using MediaBrowser.Providers.Plugins.Tmdb.Models.General;
+using MediaBrowser.Providers.Plugins.Tmdb.Models.Movies;
-namespace MediaBrowser.Providers.Tmdb.Movies
+namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
{
public class TmdbImageProvider : IRemoteImageProvider, IHasOrder
{
@@ -158,11 +158,11 @@ namespace MediaBrowser.Providers.Tmdb.Movies
/// <returns>Task{MovieImages}.</returns>
private async Task<Images> FetchImages(BaseItem item, string language, IJsonSerializer jsonSerializer, CancellationToken cancellationToken)
{
- var tmdbId = item.GetProviderId(MetadataProviders.Tmdb);
+ var tmdbId = item.GetProviderId(MetadataProvider.Tmdb);
if (string.IsNullOrWhiteSpace(tmdbId))
{
- var imdbId = item.GetProviderId(MetadataProviders.Imdb);
+ var imdbId = item.GetProviderId(MetadataProvider.Imdb);
if (!string.IsNullOrWhiteSpace(imdbId))
{
var movieInfo = await TmdbMovieProvider.Current.FetchMainResult(imdbId, false, language, cancellationToken).ConfigureAwait(false);
diff --git a/MediaBrowser.Providers/Tmdb/Movies/TmdbMovieExternalId.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieExternalId.cs
index fc7a4583f..7aec27e97 100644
--- a/MediaBrowser.Providers/Tmdb/Movies/TmdbMovieExternalId.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieExternalId.cs
@@ -4,7 +4,7 @@ using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
-namespace MediaBrowser.Providers.Tmdb.Movies
+namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
{
public class TmdbMovieExternalId : IExternalId
{
@@ -12,7 +12,7 @@ namespace MediaBrowser.Providers.Tmdb.Movies
public string Name => TmdbUtils.ProviderName;
/// <inheritdoc />
- public string Key => MetadataProviders.Tmdb.ToString();
+ public string Key => MetadataProvider.Tmdb.ToString();
/// <inheritdoc />
public string UrlFormatString => TmdbUtils.BaseTmdbUrl + "movie/{0}";
diff --git a/MediaBrowser.Providers/Tmdb/Movies/TmdbMovieProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs
index e2fd5b9e3..64d3ecd7b 100644
--- a/MediaBrowser.Providers/Tmdb/Movies/TmdbMovieProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs
@@ -15,15 +15,14 @@ using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Serialization;
-using MediaBrowser.Providers.Tmdb.Models.Movies;
+using MediaBrowser.Providers.Plugins.Tmdb.Models.Movies;
using Microsoft.Extensions.Logging;
-namespace MediaBrowser.Providers.Tmdb.Movies
+namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
{
/// <summary>
/// Class MovieDbProvider
@@ -36,7 +35,7 @@ namespace MediaBrowser.Providers.Tmdb.Movies
private readonly IHttpClient _httpClient;
private readonly IFileSystem _fileSystem;
private readonly IServerConfigurationManager _configurationManager;
- private readonly ILogger _logger;
+ private readonly ILogger<TmdbMovieProvider> _logger;
private readonly ILibraryManager _libraryManager;
private readonly IApplicationHost _appHost;
@@ -68,7 +67,7 @@ namespace MediaBrowser.Providers.Tmdb.Movies
public async Task<IEnumerable<RemoteSearchResult>> GetMovieSearchResults(ItemLookupInfo searchInfo, CancellationToken cancellationToken)
{
- var tmdbId = searchInfo.GetProviderId(MetadataProviders.Tmdb);
+ var tmdbId = searchInfo.GetProviderId(MetadataProvider.Tmdb);
if (!string.IsNullOrEmpty(tmdbId))
{
@@ -101,11 +100,11 @@ namespace MediaBrowser.Providers.Tmdb.Movies
}
}
- remoteResult.SetProviderId(MetadataProviders.Tmdb, obj.Id.ToString(_usCulture));
+ remoteResult.SetProviderId(MetadataProvider.Tmdb, obj.Id.ToString(_usCulture));
if (!string.IsNullOrWhiteSpace(obj.Imdb_Id))
{
- remoteResult.SetProviderId(MetadataProviders.Imdb, obj.Imdb_Id);
+ remoteResult.SetProviderId(MetadataProvider.Imdb, obj.Imdb_Id);
}
return new[] { remoteResult };
diff --git a/MediaBrowser.Providers/Tmdb/Movies/TmdbSearch.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbSearch.cs
index 223cef086..e1e34afb9 100644
--- a/MediaBrowser.Providers/Tmdb/Movies/TmdbSearch.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbSearch.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net;
+using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Net;
@@ -11,15 +12,29 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Serialization;
-using MediaBrowser.Providers.Tmdb.Models.Search;
+using MediaBrowser.Providers.Plugins.Tmdb.Models.Search;
using Microsoft.Extensions.Logging;
-namespace MediaBrowser.Providers.Tmdb.Movies
+namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
{
public class TmdbSearch
{
- private static readonly CultureInfo EnUs = new CultureInfo("en-US");
- private const string Search3 = TmdbUtils.BaseTmdbApiUrl + @"3/search/{3}?api_key={1}&query={0}&language={2}";
+ private static readonly CultureInfo _usCulture = new CultureInfo("en-US");
+
+ private static readonly Regex _cleanEnclosed = new Regex(@"\p{Ps}.*\p{Pe}", RegexOptions.Compiled);
+ private static readonly Regex _cleanNonWord = new Regex(@"[\W_]+", RegexOptions.Compiled);
+ private static readonly Regex _cleanStopWords = new Regex(@"\b( # Start at word boundary
+ 19[0-9]{2}|20[0-9]{2}| # 1900-2099
+ S[0-9]{2}| # Season
+ E[0-9]{2}| # Episode
+ (2160|1080|720|576|480)[ip]?| # Resolution
+ [xh]?264| # Encoding
+ (web|dvd|bd|hdtv|hd)rip| # *Rip
+ web|hdtv|mp4|bluray|ktr|dl|single|imageset|internal|doku|dubbed|retail|xxx|flac
+ ).* # Match rest of string",
+ RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace | RegexOptions.IgnoreCase);
+
+ private const string _searchURL = TmdbUtils.BaseTmdbApiUrl + @"3/search/{3}?api_key={1}&query={0}&language={2}";
private readonly ILogger _logger;
private readonly IJsonSerializer _json;
@@ -61,20 +76,20 @@ namespace MediaBrowser.Providers.Tmdb.Movies
var tmdbImageUrl = tmdbSettings.images.GetImageUrl("original");
- if (!string.IsNullOrWhiteSpace(name))
- {
- var parsedName = _libraryManager.ParseName(name);
- var yearInName = parsedName.Year;
- name = parsedName.Name;
- year = year ?? yearInName;
- }
+ // ParseName is required here.
+ // Caller provides the filename with extension stripped and NOT the parsed filename
+ var parsedName = _libraryManager.ParseName(name);
+ var yearInName = parsedName.Year;
+ name = parsedName.Name;
+ year ??= yearInName;
- _logger.LogInformation("MovieDbProvider: Finding id for item: " + name);
var language = idInfo.MetadataLanguage.ToLowerInvariant();
- //nope - search for it
- //var searchType = item is BoxSet ? "collection" : "movie";
+ // Replace sequences of non-word characters with space
+ // TMDB expects a space separated list of words make sure that is the case
+ name = _cleanNonWord.Replace(name, " ").Trim();
+ _logger.LogInformation("TmdbSearch: Finding id for item: {0} ({1})", name, year);
var results = await GetSearchResults(name, searchType, year, language, tmdbImageUrl, cancellationToken).ConfigureAwait(false);
if (results.Count == 0)
@@ -86,36 +101,35 @@ namespace MediaBrowser.Providers.Tmdb.Movies
}
}
+ // TODO: retrying alternatives should be done outside the search
+ // provider so that the retry logic can be common for all search
+ // providers
if (results.Count == 0)
{
- // try with dot and _ turned to space
- var originalName = name;
-
- name = name.Replace(",", " ");
- name = name.Replace(".", " ");
- name = name.Replace("_", " ");
- name = name.Replace("-", " ");
- name = name.Replace("!", " ");
- name = name.Replace("?", " ");
-
- var parenthIndex = name.IndexOf('(');
- if (parenthIndex != -1)
- {
- name = name.Substring(0, parenthIndex);
- }
+ var name2 = parsedName.Name;
- name = name.Trim();
+ // Remove things enclosed in []{}() etc
+ name2 = _cleanEnclosed.Replace(name2, string.Empty);
+
+ // Replace sequences of non-word characters with space
+ name2 = _cleanNonWord.Replace(name2, " ");
+
+ // Clean based on common stop words / tokens
+ name2 = _cleanStopWords.Replace(name2, string.Empty);
+
+ // Trim whitespace
+ name2 = name2.Trim();
// Search again if the new name is different
- if (!string.Equals(name, originalName))
+ if (!string.Equals(name2, name) && !string.IsNullOrWhiteSpace(name2))
{
- results = await GetSearchResults(name, searchType, year, language, tmdbImageUrl, cancellationToken).ConfigureAwait(false);
+ _logger.LogInformation("TmdbSearch: Finding id for item: {0} ({1})", name2, year);
+ results = await GetSearchResults(name2, searchType, year, language, tmdbImageUrl, cancellationToken).ConfigureAwait(false);
if (results.Count == 0 && !string.Equals(language, "en", StringComparison.OrdinalIgnoreCase))
{
//one more time, in english
- results = await GetSearchResults(name, searchType, year, "en", tmdbImageUrl, cancellationToken).ConfigureAwait(false);
-
+ results = await GetSearchResults(name2, searchType, year, "en", tmdbImageUrl, cancellationToken).ConfigureAwait(false);
}
}
}
@@ -150,7 +164,7 @@ namespace MediaBrowser.Providers.Tmdb.Movies
throw new ArgumentException("name");
}
- var url3 = string.Format(Search3, WebUtility.UrlEncode(name), TmdbUtils.ApiKey, language, type);
+ var url3 = string.Format(_searchURL, WebUtility.UrlEncode(name), TmdbUtils.ApiKey, language, type);
using (var response = await TmdbMovieProvider.Current.GetMovieDbResponse(new HttpRequestOptions
{
@@ -179,14 +193,14 @@ namespace MediaBrowser.Providers.Tmdb.Movies
if (!string.IsNullOrWhiteSpace(i.Release_Date))
{
// These dates are always in this exact format
- if (DateTime.TryParseExact(i.Release_Date, "yyyy-MM-dd", EnUs, DateTimeStyles.None, out var r))
+ if (DateTime.TryParseExact(i.Release_Date, "yyyy-MM-dd", _usCulture, DateTimeStyles.None, out var r))
{
remoteResult.PremiereDate = r.ToUniversalTime();
remoteResult.ProductionYear = remoteResult.PremiereDate.Value.Year;
}
}
- remoteResult.SetProviderId(MetadataProviders.Tmdb, i.Id.ToString(EnUs));
+ remoteResult.SetProviderId(MetadataProvider.Tmdb, i.Id.ToString(_usCulture));
return remoteResult;
@@ -203,7 +217,7 @@ namespace MediaBrowser.Providers.Tmdb.Movies
throw new ArgumentException("name");
}
- var url3 = string.Format(Search3, WebUtility.UrlEncode(name), TmdbUtils.ApiKey, language, "tv");
+ var url3 = string.Format(_searchURL, WebUtility.UrlEncode(name), TmdbUtils.ApiKey, language, "tv");
using (var response = await TmdbMovieProvider.Current.GetMovieDbResponse(new HttpRequestOptions
{
@@ -232,14 +246,14 @@ namespace MediaBrowser.Providers.Tmdb.Movies
if (!string.IsNullOrWhiteSpace(i.First_Air_Date))
{
// These dates are always in this exact format
- if (DateTime.TryParseExact(i.First_Air_Date, "yyyy-MM-dd", EnUs, DateTimeStyles.None, out var r))
+ if (DateTime.TryParseExact(i.First_Air_Date, "yyyy-MM-dd", _usCulture, DateTimeStyles.None, out var r))
{
remoteResult.PremiereDate = r.ToUniversalTime();
remoteResult.ProductionYear = remoteResult.PremiereDate.Value.Year;
}
}
- remoteResult.SetProviderId(MetadataProviders.Tmdb, i.Id.ToString(EnUs));
+ remoteResult.SetProviderId(MetadataProvider.Tmdb, i.Id.ToString(_usCulture));
return remoteResult;
diff --git a/MediaBrowser.Providers/Tmdb/Movies/TmdbSettings.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbSettings.cs
index dca406b99..03669ca67 100644
--- a/MediaBrowser.Providers/Tmdb/Movies/TmdbSettings.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbSettings.cs
@@ -1,6 +1,6 @@
using System.Collections.Generic;
-namespace MediaBrowser.Providers.Tmdb.Movies
+namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
{
internal class TmdbImageSettings
{
diff --git a/MediaBrowser.Providers/Tmdb/Music/TmdbMusicVideoProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Music/TmdbMusicVideoProvider.cs
index 81909fa38..d173bcc9a 100644
--- a/MediaBrowser.Providers/Tmdb/Music/TmdbMusicVideoProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Music/TmdbMusicVideoProvider.cs
@@ -6,9 +6,9 @@ using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Providers;
-using MediaBrowser.Providers.Tmdb.Movies;
+using MediaBrowser.Providers.Plugins.Tmdb.Movies;
-namespace MediaBrowser.Providers.Tmdb.Music
+namespace MediaBrowser.Providers.Plugins.Tmdb.Music
{
public class TmdbMusicVideoProvider : IRemoteMetadataProvider<MusicVideo, MusicVideoInfo>
{
diff --git a/MediaBrowser.Providers/Tmdb/People/TmdbPersonExternalId.cs b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonExternalId.cs
index 2c61bc70a..70cd1cd95 100644
--- a/MediaBrowser.Providers/Tmdb/People/TmdbPersonExternalId.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonExternalId.cs
@@ -2,7 +2,7 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
-namespace MediaBrowser.Providers.Tmdb.People
+namespace MediaBrowser.Providers.Plugins.Tmdb.People
{
public class TmdbPersonExternalId : IExternalId
{
@@ -10,7 +10,7 @@ namespace MediaBrowser.Providers.Tmdb.People
public string Name => TmdbUtils.ProviderName;
/// <inheritdoc />
- public string Key => MetadataProviders.Tmdb.ToString();
+ public string Key => MetadataProvider.Tmdb.ToString();
/// <inheritdoc />
public string UrlFormatString => TmdbUtils.BaseTmdbUrl + "person/{0}";
diff --git a/MediaBrowser.Providers/Tmdb/People/TmdbPersonImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs
index e205d796a..525c0072b 100644
--- a/MediaBrowser.Providers/Tmdb/People/TmdbPersonImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs
@@ -10,11 +10,11 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Serialization;
-using MediaBrowser.Providers.Tmdb.Models.General;
-using MediaBrowser.Providers.Tmdb.Models.People;
-using MediaBrowser.Providers.Tmdb.Movies;
+using MediaBrowser.Providers.Plugins.Tmdb.Models.General;
+using MediaBrowser.Providers.Plugins.Tmdb.Models.People;
+using MediaBrowser.Providers.Plugins.Tmdb.Movies;
-namespace MediaBrowser.Providers.Tmdb.People
+namespace MediaBrowser.Providers.Plugins.Tmdb.People
{
public class TmdbPersonImageProvider : IRemoteImageProvider, IHasOrder
{
@@ -49,7 +49,7 @@ namespace MediaBrowser.Providers.Tmdb.People
public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken)
{
var person = (Person)item;
- var id = person.GetProviderId(MetadataProviders.Tmdb);
+ var id = person.GetProviderId(MetadataProvider.Tmdb);
if (!string.IsNullOrEmpty(id))
{
diff --git a/MediaBrowser.Providers/Tmdb/People/TmdbPersonProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs
index 588001169..3f28483f7 100644
--- a/MediaBrowser.Providers/Tmdb/People/TmdbPersonProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs
@@ -17,13 +17,13 @@ using MediaBrowser.Model.IO;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Serialization;
-using MediaBrowser.Providers.Tmdb.Models.General;
-using MediaBrowser.Providers.Tmdb.Models.People;
-using MediaBrowser.Providers.Tmdb.Models.Search;
-using MediaBrowser.Providers.Tmdb.Movies;
+using MediaBrowser.Providers.Plugins.Tmdb.Models.General;
+using MediaBrowser.Providers.Plugins.Tmdb.Models.People;
+using MediaBrowser.Providers.Plugins.Tmdb.Models.Search;
+using MediaBrowser.Providers.Plugins.Tmdb.Movies;
using Microsoft.Extensions.Logging;
-namespace MediaBrowser.Providers.Tmdb.People
+namespace MediaBrowser.Providers.Plugins.Tmdb.People
{
public class TmdbPersonProvider : IRemoteMetadataProvider<Person, PersonLookupInfo>
{
@@ -35,7 +35,7 @@ namespace MediaBrowser.Providers.Tmdb.People
private readonly IFileSystem _fileSystem;
private readonly IServerConfigurationManager _configurationManager;
private readonly IHttpClient _httpClient;
- private readonly ILogger _logger;
+ private readonly ILogger<TmdbPersonProvider> _logger;
public TmdbPersonProvider(
IFileSystem fileSystem,
@@ -56,7 +56,7 @@ namespace MediaBrowser.Providers.Tmdb.People
public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(PersonLookupInfo searchInfo, CancellationToken cancellationToken)
{
- var tmdbId = searchInfo.GetProviderId(MetadataProviders.Tmdb);
+ var tmdbId = searchInfo.GetProviderId(MetadataProvider.Tmdb);
var tmdbSettings = await TmdbMovieProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false);
@@ -80,8 +80,8 @@ namespace MediaBrowser.Providers.Tmdb.People
ImageUrl = images.Count == 0 ? null : (tmdbImageUrl + images[0].File_Path)
};
- result.SetProviderId(MetadataProviders.Tmdb, info.Id.ToString(_usCulture));
- result.SetProviderId(MetadataProviders.Imdb, info.Imdb_Id);
+ result.SetProviderId(MetadataProvider.Tmdb, info.Id.ToString(_usCulture));
+ result.SetProviderId(MetadataProvider.Imdb, info.Imdb_Id);
return new[] { result };
}
@@ -123,14 +123,14 @@ namespace MediaBrowser.Providers.Tmdb.People
ImageUrl = string.IsNullOrEmpty(i.Profile_Path) ? null : baseImageUrl + i.Profile_Path
};
- result.SetProviderId(MetadataProviders.Tmdb, i.Id.ToString(_usCulture));
+ result.SetProviderId(MetadataProvider.Tmdb, i.Id.ToString(_usCulture));
return result;
}
public async Task<MetadataResult<Person>> GetMetadata(PersonLookupInfo id, CancellationToken cancellationToken)
{
- var tmdbId = id.GetProviderId(MetadataProviders.Tmdb);
+ var tmdbId = id.GetProviderId(MetadataProvider.Tmdb);
// We don't already have an Id, need to fetch it
if (string.IsNullOrEmpty(tmdbId))
@@ -185,11 +185,11 @@ namespace MediaBrowser.Providers.Tmdb.People
item.EndDate = date.ToUniversalTime();
}
- item.SetProviderId(MetadataProviders.Tmdb, info.Id.ToString(_usCulture));
+ item.SetProviderId(MetadataProvider.Tmdb, info.Id.ToString(_usCulture));
if (!string.IsNullOrEmpty(info.Imdb_Id))
{
- item.SetProviderId(MetadataProviders.Imdb, info.Imdb_Id);
+ item.SetProviderId(MetadataProvider.Imdb, info.Imdb_Id);
}
result.HasMetadata = true;
@@ -211,7 +211,7 @@ namespace MediaBrowser.Providers.Tmdb.People
{
var results = await GetSearchResults(info, cancellationToken).ConfigureAwait(false);
- return results.Select(i => i.GetProviderId(MetadataProviders.Tmdb)).FirstOrDefault();
+ return results.Select(i => i.GetProviderId(MetadataProvider.Tmdb)).FirstOrDefault();
}
internal async Task EnsurePersonInfo(string id, CancellationToken cancellationToken)
diff --git a/MediaBrowser.Providers/Tmdb/TV/TmdbEpisodeImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs
index 558c8149e..3fa47d54b 100644
--- a/MediaBrowser.Providers/Tmdb/TV/TmdbEpisodeImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs
@@ -13,11 +13,11 @@ using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Serialization;
-using MediaBrowser.Providers.Tmdb.Models.General;
-using MediaBrowser.Providers.Tmdb.Movies;
+using MediaBrowser.Providers.Plugins.Tmdb.Models.General;
+using MediaBrowser.Providers.Plugins.Tmdb.Movies;
using Microsoft.Extensions.Logging;
-namespace MediaBrowser.Providers.Tmdb.TV
+namespace MediaBrowser.Providers.Plugins.Tmdb.TV
{
public class TmdbEpisodeImageProvider :
TmdbEpisodeProviderBase,
@@ -41,7 +41,7 @@ namespace MediaBrowser.Providers.Tmdb.TV
var episode = (Controller.Entities.TV.Episode)item;
var series = episode.Series;
- var seriesId = series != null ? series.GetProviderId(MetadataProviders.Tmdb) : null;
+ var seriesId = series != null ? series.GetProviderId(MetadataProvider.Tmdb) : null;
var list = new List<RemoteImageInfo>();
@@ -80,7 +80,6 @@ namespace MediaBrowser.Providers.Tmdb.TV
RatingType = RatingType.Score
}));
-
var isLanguageEn = string.Equals(language, "en", StringComparison.OrdinalIgnoreCase);
return list.OrderByDescending(i =>
@@ -89,6 +88,7 @@ namespace MediaBrowser.Providers.Tmdb.TV
{
return 3;
}
+
if (!isLanguageEn)
{
if (string.Equals("en", i.Language, StringComparison.OrdinalIgnoreCase))
@@ -96,15 +96,16 @@ namespace MediaBrowser.Providers.Tmdb.TV
return 2;
}
}
+
if (string.IsNullOrEmpty(i.Language))
{
return isLanguageEn ? 3 : 2;
}
+
return 0;
})
.ThenByDescending(i => i.CommunityRating ?? 0)
.ThenByDescending(i => i.VoteCount ?? 0);
-
}
private IEnumerable<Still> GetPosters(StillImages images)
@@ -112,7 +113,6 @@ namespace MediaBrowser.Providers.Tmdb.TV
return images.Stills ?? new List<Still>();
}
-
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
{
return GetResponse(url, cancellationToken);
diff --git a/MediaBrowser.Providers/Tmdb/TV/TmdbEpisodeProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs
index a17f5d17a..01b295f86 100644
--- a/MediaBrowser.Providers/Tmdb/TV/TmdbEpisodeProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs
@@ -18,7 +18,7 @@ using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Serialization;
using Microsoft.Extensions.Logging;
-namespace MediaBrowser.Providers.Tmdb.TV
+namespace MediaBrowser.Providers.Plugins.Tmdb.TV
{
public class TmdbEpisodeProvider :
TmdbEpisodeProviderBase,
@@ -71,7 +71,7 @@ namespace MediaBrowser.Providers.Tmdb.TV
return result;
}
- info.SeriesProviderIds.TryGetValue(MetadataProviders.Tmdb.ToString(), out string seriesTmdbId);
+ info.SeriesProviderIds.TryGetValue(MetadataProvider.Tmdb.ToString(), out string seriesTmdbId);
if (string.IsNullOrEmpty(seriesTmdbId))
{
@@ -109,7 +109,7 @@ namespace MediaBrowser.Providers.Tmdb.TV
if (response.External_Ids.Tvdb_Id > 0)
{
- item.SetProviderId(MetadataProviders.Tvdb, response.External_Ids.Tvdb_Id.ToString(CultureInfo.InvariantCulture));
+ item.SetProviderId(MetadataProvider.Tvdb, response.External_Ids.Tvdb_Id.ToString(CultureInfo.InvariantCulture));
}
item.PremiereDate = response.Air_Date;
@@ -203,6 +203,7 @@ namespace MediaBrowser.Providers.Tmdb.TV
{
return GetResponse(url, cancellationToken);
}
+
// After TheTvDb
public int Order => 1;
diff --git a/MediaBrowser.Providers/Tmdb/TV/TmdbEpisodeProviderBase.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProviderBase.cs
index e87fe9332..f82f5f2ab 100644
--- a/MediaBrowser.Providers/Tmdb/TV/TmdbEpisodeProviderBase.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProviderBase.cs
@@ -8,11 +8,11 @@ using MediaBrowser.Controller.Configuration;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
-using MediaBrowser.Providers.Tmdb.Models.TV;
-using MediaBrowser.Providers.Tmdb.Movies;
+using MediaBrowser.Providers.Plugins.Tmdb.Models.TV;
+using MediaBrowser.Providers.Plugins.Tmdb.Movies;
using Microsoft.Extensions.Logging;
-namespace MediaBrowser.Providers.Tmdb.TV
+namespace MediaBrowser.Providers.Plugins.Tmdb.TV
{
public abstract class TmdbEpisodeProviderBase
{
@@ -22,7 +22,7 @@ namespace MediaBrowser.Providers.Tmdb.TV
private readonly IJsonSerializer _jsonSerializer;
private readonly IFileSystem _fileSystem;
private readonly ILocalizationManager _localization;
- private readonly ILogger _logger;
+ private readonly ILogger<TmdbEpisodeProviderBase> _logger;
protected TmdbEpisodeProviderBase(IHttpClient httpClient, IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IFileSystem fileSystem, ILocalizationManager localization, ILoggerFactory loggerFactory)
{
@@ -31,7 +31,7 @@ namespace MediaBrowser.Providers.Tmdb.TV
_jsonSerializer = jsonSerializer;
_fileSystem = fileSystem;
_localization = localization;
- _logger = loggerFactory.CreateLogger(GetType().Name);
+ _logger = loggerFactory.CreateLogger<TmdbEpisodeProviderBase>();
}
protected ILogger Logger => _logger;
@@ -53,6 +53,7 @@ namespace MediaBrowser.Providers.Tmdb.TV
{
throw new ArgumentNullException(nameof(tmdbId));
}
+
if (string.IsNullOrEmpty(language))
{
throw new ArgumentNullException(nameof(language));
@@ -80,6 +81,7 @@ namespace MediaBrowser.Providers.Tmdb.TV
{
throw new ArgumentNullException(nameof(tmdbId));
}
+
if (string.IsNullOrEmpty(preferredLanguage))
{
throw new ArgumentNullException(nameof(preferredLanguage));
diff --git a/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs
index 698a43604..b5456b45c 100644
--- a/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs
@@ -12,10 +12,10 @@ using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Serialization;
-using MediaBrowser.Providers.Tmdb.Models.General;
-using MediaBrowser.Providers.Tmdb.Movies;
+using MediaBrowser.Providers.Plugins.Tmdb.Models.General;
+using MediaBrowser.Providers.Plugins.Tmdb.Movies;
-namespace MediaBrowser.Providers.Tmdb.TV
+namespace MediaBrowser.Providers.Plugins.Tmdb.TV
{
public class TmdbSeasonImageProvider : IRemoteImageProvider, IHasOrder
{
@@ -48,7 +48,7 @@ namespace MediaBrowser.Providers.Tmdb.TV
var season = (Season)item;
var series = season.Series;
- var seriesId = series?.GetProviderId(MetadataProviders.Tmdb);
+ var seriesId = series?.GetProviderId(MetadataProvider.Tmdb);
if (string.IsNullOrEmpty(seriesId))
{
diff --git a/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs
index 5ad331971..c7cd672b4 100644
--- a/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs
@@ -14,12 +14,12 @@ using MediaBrowser.Model.IO;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Serialization;
-using MediaBrowser.Providers.Tmdb.Models.TV;
-using MediaBrowser.Providers.Tmdb.Movies;
+using MediaBrowser.Providers.Plugins.Tmdb.Models.TV;
+using MediaBrowser.Providers.Plugins.Tmdb.Movies;
using Microsoft.Extensions.Logging;
using Season = MediaBrowser.Controller.Entities.TV.Season;
-namespace MediaBrowser.Providers.Tmdb.TV
+namespace MediaBrowser.Providers.Plugins.Tmdb.TV
{
public class TmdbSeasonProvider : IRemoteMetadataProvider<Season, SeasonInfo>
{
@@ -29,18 +29,18 @@ namespace MediaBrowser.Providers.Tmdb.TV
private readonly IJsonSerializer _jsonSerializer;
private readonly IFileSystem _fileSystem;
private readonly ILocalizationManager _localization;
- private readonly ILogger _logger;
+ private readonly ILogger<TmdbSeasonProvider> _logger;
internal static TmdbSeasonProvider Current { get; private set; }
- public TmdbSeasonProvider(IHttpClient httpClient, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ILocalizationManager localization, IJsonSerializer jsonSerializer, ILoggerFactory loggerFactory)
+ public TmdbSeasonProvider(IHttpClient httpClient, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ILocalizationManager localization, IJsonSerializer jsonSerializer, ILogger<TmdbSeasonProvider> logger)
{
_httpClient = httpClient;
_configurationManager = configurationManager;
_fileSystem = fileSystem;
_localization = localization;
_jsonSerializer = jsonSerializer;
- _logger = loggerFactory.CreateLogger(GetType().Name);
+ _logger = logger;
Current = this;
}
@@ -48,7 +48,7 @@ namespace MediaBrowser.Providers.Tmdb.TV
{
var result = new MetadataResult<Season>();
- info.SeriesProviderIds.TryGetValue(MetadataProviders.Tmdb.ToString(), out string seriesTmdbId);
+ info.SeriesProviderIds.TryGetValue(MetadataProvider.Tmdb.ToString(), out string seriesTmdbId);
var seasonNumber = info.IndexNumber;
@@ -73,7 +73,7 @@ namespace MediaBrowser.Providers.Tmdb.TV
if (seasonInfo.External_Ids.Tvdb_Id > 0)
{
- result.Item.SetProviderId(MetadataProviders.Tvdb, seasonInfo.External_Ids.Tvdb_Id.ToString(CultureInfo.InvariantCulture));
+ result.Item.SetProviderId(MetadataProvider.Tvdb, seasonInfo.External_Ids.Tvdb_Id.ToString(CultureInfo.InvariantCulture));
}
var credits = seasonInfo.Credits;
@@ -145,6 +145,7 @@ namespace MediaBrowser.Providers.Tmdb.TV
{
throw new ArgumentNullException(nameof(tmdbId));
}
+
if (string.IsNullOrEmpty(language))
{
throw new ArgumentNullException(nameof(language));
@@ -172,6 +173,7 @@ namespace MediaBrowser.Providers.Tmdb.TV
{
throw new ArgumentNullException(nameof(tmdbId));
}
+
if (string.IsNullOrEmpty(preferredLanguage))
{
throw new ArgumentNullException(nameof(preferredLanguage));
@@ -216,7 +218,6 @@ namespace MediaBrowser.Providers.Tmdb.TV
Url = url,
CancellationToken = cancellationToken,
AcceptHeader = TmdbUtils.AcceptHeader
-
}).ConfigureAwait(false))
{
using (var json = response.Content)
diff --git a/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesExternalId.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesExternalId.cs
index 524a3b05e..705f8041b 100644
--- a/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesExternalId.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesExternalId.cs
@@ -2,7 +2,7 @@ using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
-namespace MediaBrowser.Providers.Tmdb.TV
+namespace MediaBrowser.Providers.Plugins.Tmdb.TV
{
public class TmdbSeriesExternalId : IExternalId
{
@@ -10,7 +10,7 @@ namespace MediaBrowser.Providers.Tmdb.TV
public string Name => TmdbUtils.ProviderName;
/// <inheritdoc />
- public string Key => MetadataProviders.Tmdb.ToString();
+ public string Key => MetadataProvider.Tmdb.ToString();
/// <inheritdoc />
public string UrlFormatString => TmdbUtils.BaseTmdbUrl + "tv/{0}";
diff --git a/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs
index 0460fe994..40824d88d 100644
--- a/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs
@@ -12,11 +12,11 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Serialization;
-using MediaBrowser.Providers.Tmdb.Models.General;
-using MediaBrowser.Providers.Tmdb.Models.TV;
-using MediaBrowser.Providers.Tmdb.Movies;
+using MediaBrowser.Providers.Plugins.Tmdb.Models.General;
+using MediaBrowser.Providers.Plugins.Tmdb.Models.TV;
+using MediaBrowser.Providers.Plugins.Tmdb.Movies;
-namespace MediaBrowser.Providers.Tmdb.TV
+namespace MediaBrowser.Providers.Plugins.Tmdb.TV
{
public class TmdbSeriesImageProvider : IRemoteImageProvider, IHasOrder
{
@@ -99,6 +99,7 @@ namespace MediaBrowser.Providers.Tmdb.TV
{
return 3;
}
+
if (!isLanguageEn)
{
if (string.Equals("en", i.Language, StringComparison.OrdinalIgnoreCase))
@@ -106,10 +107,12 @@ namespace MediaBrowser.Providers.Tmdb.TV
return 2;
}
}
+
if (string.IsNullOrEmpty(i.Language))
{
return isLanguageEn ? 3 : 2;
}
+
return 0;
})
.ThenByDescending(i => i.CommunityRating ?? 0)
@@ -148,7 +151,7 @@ namespace MediaBrowser.Providers.Tmdb.TV
private async Task<Images> FetchImages(BaseItem item, string language, IJsonSerializer jsonSerializer,
CancellationToken cancellationToken)
{
- var tmdbId = item.GetProviderId(MetadataProviders.Tmdb);
+ var tmdbId = item.GetProviderId(MetadataProvider.Tmdb);
if (string.IsNullOrEmpty(tmdbId))
{
@@ -171,6 +174,7 @@ namespace MediaBrowser.Providers.Tmdb.TV
return null;
}
+
// After tvdb and fanart
public int Order => 2;
diff --git a/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs
index 6e3c26c26..7e46a65bb 100644
--- a/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs
@@ -17,12 +17,12 @@ using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Serialization;
-using MediaBrowser.Providers.Tmdb.Models.Search;
-using MediaBrowser.Providers.Tmdb.Models.TV;
-using MediaBrowser.Providers.Tmdb.Movies;
+using MediaBrowser.Providers.Plugins.Tmdb.Models.Search;
+using MediaBrowser.Providers.Plugins.Tmdb.Models.TV;
+using MediaBrowser.Providers.Plugins.Tmdb.Movies;
using Microsoft.Extensions.Logging;
-namespace MediaBrowser.Providers.Tmdb.TV
+namespace MediaBrowser.Providers.Plugins.Tmdb.TV
{
public class TmdbSeriesProvider : IRemoteMetadataProvider<Series, SeriesInfo>, IHasOrder
{
@@ -31,7 +31,7 @@ namespace MediaBrowser.Providers.Tmdb.TV
private readonly IJsonSerializer _jsonSerializer;
private readonly IFileSystem _fileSystem;
private readonly IServerConfigurationManager _configurationManager;
- private readonly ILogger _logger;
+ private readonly ILogger<TmdbSeriesProvider> _logger;
private readonly ILocalizationManager _localization;
private readonly IHttpClient _httpClient;
private readonly ILibraryManager _libraryManager;
@@ -63,7 +63,7 @@ namespace MediaBrowser.Providers.Tmdb.TV
public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(SeriesInfo searchInfo, CancellationToken cancellationToken)
{
- var tmdbId = searchInfo.GetProviderId(MetadataProviders.Tmdb);
+ var tmdbId = searchInfo.GetProviderId(MetadataProvider.Tmdb);
if (!string.IsNullOrEmpty(tmdbId))
{
@@ -85,18 +85,18 @@ namespace MediaBrowser.Providers.Tmdb.TV
ImageUrl = string.IsNullOrWhiteSpace(obj.Poster_Path) ? null : tmdbImageUrl + obj.Poster_Path
};
- remoteResult.SetProviderId(MetadataProviders.Tmdb, obj.Id.ToString(_usCulture));
- remoteResult.SetProviderId(MetadataProviders.Imdb, obj.External_Ids.Imdb_Id);
+ remoteResult.SetProviderId(MetadataProvider.Tmdb, obj.Id.ToString(_usCulture));
+ remoteResult.SetProviderId(MetadataProvider.Imdb, obj.External_Ids.Imdb_Id);
if (obj.External_Ids.Tvdb_Id > 0)
{
- remoteResult.SetProviderId(MetadataProviders.Tvdb, obj.External_Ids.Tvdb_Id.ToString(_usCulture));
+ remoteResult.SetProviderId(MetadataProvider.Tvdb, obj.External_Ids.Tvdb_Id.ToString(_usCulture));
}
return new[] { remoteResult };
}
- var imdbId = searchInfo.GetProviderId(MetadataProviders.Imdb);
+ var imdbId = searchInfo.GetProviderId(MetadataProvider.Imdb);
if (!string.IsNullOrEmpty(imdbId))
{
@@ -108,7 +108,7 @@ namespace MediaBrowser.Providers.Tmdb.TV
}
}
- var tvdbId = searchInfo.GetProviderId(MetadataProviders.Tvdb);
+ var tvdbId = searchInfo.GetProviderId(MetadataProvider.Tvdb);
if (!string.IsNullOrEmpty(tvdbId))
{
@@ -128,11 +128,11 @@ namespace MediaBrowser.Providers.Tmdb.TV
var result = new MetadataResult<Series>();
result.QueriedById = true;
- var tmdbId = info.GetProviderId(MetadataProviders.Tmdb);
+ var tmdbId = info.GetProviderId(MetadataProvider.Tmdb);
if (string.IsNullOrEmpty(tmdbId))
{
- var imdbId = info.GetProviderId(MetadataProviders.Imdb);
+ var imdbId = info.GetProviderId(MetadataProvider.Imdb);
if (!string.IsNullOrEmpty(imdbId))
{
@@ -140,14 +140,14 @@ namespace MediaBrowser.Providers.Tmdb.TV
if (searchResult != null)
{
- tmdbId = searchResult.GetProviderId(MetadataProviders.Tmdb);
+ tmdbId = searchResult.GetProviderId(MetadataProvider.Tmdb);
}
}
}
if (string.IsNullOrEmpty(tmdbId))
{
- var tvdbId = info.GetProviderId(MetadataProviders.Tvdb);
+ var tvdbId = info.GetProviderId(MetadataProvider.Tvdb);
if (!string.IsNullOrEmpty(tvdbId))
{
@@ -155,7 +155,7 @@ namespace MediaBrowser.Providers.Tmdb.TV
if (searchResult != null)
{
- tmdbId = searchResult.GetProviderId(MetadataProviders.Tmdb);
+ tmdbId = searchResult.GetProviderId(MetadataProvider.Tmdb);
}
}
}
@@ -169,7 +169,7 @@ namespace MediaBrowser.Providers.Tmdb.TV
if (searchResult != null)
{
- tmdbId = searchResult.GetProviderId(MetadataProviders.Tmdb);
+ tmdbId = searchResult.GetProviderId(MetadataProvider.Tmdb);
}
}
@@ -219,7 +219,7 @@ namespace MediaBrowser.Providers.Tmdb.TV
series.Name = seriesInfo.Name;
series.OriginalTitle = seriesInfo.Original_Name;
- series.SetProviderId(MetadataProviders.Tmdb, seriesInfo.Id.ToString(_usCulture));
+ series.SetProviderId(MetadataProvider.Tmdb, seriesInfo.Id.ToString(_usCulture));
string voteAvg = seriesInfo.Vote_Average.ToString(CultureInfo.InvariantCulture);
@@ -261,15 +261,17 @@ namespace MediaBrowser.Providers.Tmdb.TV
{
if (!string.IsNullOrWhiteSpace(ids.Imdb_Id))
{
- series.SetProviderId(MetadataProviders.Imdb, ids.Imdb_Id);
+ series.SetProviderId(MetadataProvider.Imdb, ids.Imdb_Id);
}
+
if (ids.Tvrage_Id > 0)
{
- series.SetProviderId(MetadataProviders.TvRage, ids.Tvrage_Id.ToString(_usCulture));
+ series.SetProviderId(MetadataProvider.TvRage, ids.Tvrage_Id.ToString(_usCulture));
}
+
if (ids.Tvdb_Id > 0)
{
- series.SetProviderId(MetadataProviders.Tvdb, ids.Tvdb_Id.ToString(_usCulture));
+ series.SetProviderId(MetadataProvider.Tvdb, ids.Tvdb_Id.ToString(_usCulture));
}
}
@@ -329,7 +331,7 @@ namespace MediaBrowser.Providers.Tmdb.TV
if (actor.Id > 0)
{
- personInfo.SetProviderId(MetadataProviders.Tmdb, actor.Id.ToString(CultureInfo.InvariantCulture));
+ personInfo.SetProviderId(MetadataProvider.Tmdb, actor.Id.ToString(CultureInfo.InvariantCulture));
}
seriesResult.AddPerson(personInfo);
@@ -416,7 +418,6 @@ namespace MediaBrowser.Providers.Tmdb.TV
Url = url,
CancellationToken = cancellationToken,
AcceptHeader = TmdbUtils.AcceptHeader
-
}).ConfigureAwait(false))
{
using (var json = response.Content)
@@ -453,7 +454,6 @@ namespace MediaBrowser.Providers.Tmdb.TV
Url = url,
CancellationToken = cancellationToken,
AcceptHeader = TmdbUtils.AcceptHeader
-
}).ConfigureAwait(false))
{
using (var json = response.Content)
@@ -518,7 +518,6 @@ namespace MediaBrowser.Providers.Tmdb.TV
Url = url,
CancellationToken = cancellationToken,
AcceptHeader = TmdbUtils.AcceptHeader
-
}).ConfigureAwait(false))
{
using (var json = response.Content)
@@ -541,7 +540,7 @@ namespace MediaBrowser.Providers.Tmdb.TV
ImageUrl = string.IsNullOrWhiteSpace(tv.Poster_Path) ? null : tmdbImageUrl + tv.Poster_Path
};
- remoteResult.SetProviderId(MetadataProviders.Tmdb, tv.Id.ToString(_usCulture));
+ remoteResult.SetProviderId(MetadataProvider.Tmdb, tv.Id.ToString(_usCulture));
return remoteResult;
}
diff --git a/MediaBrowser.Providers/Tmdb/TmdbUtils.cs b/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs
index 7dacc7404..2f1e8b791 100644
--- a/MediaBrowser.Providers/Tmdb/TmdbUtils.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs
@@ -1,11 +1,11 @@
using System;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Providers.Tmdb.Models.General;
+using MediaBrowser.Providers.Plugins.Tmdb.Models.General;
-namespace MediaBrowser.Providers.Tmdb
+namespace MediaBrowser.Providers.Plugins.Tmdb
{
/// <summary>
- /// Utilities for the TMDb provider
+ /// Utilities for the TMDb provider.
/// </summary>
public static class TmdbUtils
{
diff --git a/MediaBrowser.Providers/Tmdb/Trailers/TmdbTrailerProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Trailers/TmdbTrailerProvider.cs
index b15de0125..ee5128db4 100644
--- a/MediaBrowser.Providers/Tmdb/Trailers/TmdbTrailerProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Trailers/TmdbTrailerProvider.cs
@@ -5,9 +5,9 @@ using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Providers;
-using MediaBrowser.Providers.Tmdb.Movies;
+using MediaBrowser.Providers.Plugins.Tmdb.Movies;
-namespace MediaBrowser.Providers.Tmdb.Trailers
+namespace MediaBrowser.Providers.Plugins.Tmdb.Trailers
{
public class TmdbTrailerProvider : IHasOrder, IRemoteMetadataProvider<Trailer, TrailerInfo>
{
diff --git a/MediaBrowser.Providers/Studios/StudioMetadataService.cs b/MediaBrowser.Providers/Studios/StudioMetadataService.cs
index 847e47f1b..68f02433b 100644
--- a/MediaBrowser.Providers/Studios/StudioMetadataService.cs
+++ b/MediaBrowser.Providers/Studios/StudioMetadataService.cs
@@ -21,7 +21,7 @@ namespace MediaBrowser.Providers.Studios
}
/// <inheritdoc />
- protected override void MergeData(MetadataResult<Studio> source, MetadataResult<Studio> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<Studio> source, MetadataResult<Studio> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}
diff --git a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs
index 127d29c04..c6ffc460c 100644
--- a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs
+++ b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs
@@ -25,7 +25,7 @@ namespace MediaBrowser.Providers.Subtitles
{
public class SubtitleManager : ISubtitleManager
{
- private readonly ILogger _logger;
+ private readonly ILogger<SubtitleManager> _logger;
private readonly IFileSystem _fileSystem;
private readonly ILibraryMonitor _monitor;
private readonly IMediaSourceManager _mediaSourceManager;
diff --git a/MediaBrowser.Providers/TV/EpisodeMetadataService.cs b/MediaBrowser.Providers/TV/EpisodeMetadataService.cs
index 758c47ba0..50a015c7a 100644
--- a/MediaBrowser.Providers/TV/EpisodeMetadataService.cs
+++ b/MediaBrowser.Providers/TV/EpisodeMetadataService.cs
@@ -66,7 +66,7 @@ namespace MediaBrowser.Providers.TV
}
/// <inheritdoc />
- protected override void MergeData(MetadataResult<Episode> source, MetadataResult<Episode> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<Episode> source, MetadataResult<Episode> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
diff --git a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs
index 0721c4bb4..aabad3ada 100644
--- a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs
+++ b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs
@@ -46,7 +46,7 @@ namespace MediaBrowser.Providers.TV
public async Task<bool> Run(Series series, bool addNewItems, CancellationToken cancellationToken)
{
- var tvdbId = series.GetProviderId(MetadataProviders.Tvdb);
+ var tvdbId = series.GetProviderId(MetadataProvider.Tvdb);
if (string.IsNullOrEmpty(tvdbId))
{
return false;
diff --git a/MediaBrowser.Providers/TV/SeasonMetadataService.cs b/MediaBrowser.Providers/TV/SeasonMetadataService.cs
index eb8032e0e..1eb6af2ba 100644
--- a/MediaBrowser.Providers/TV/SeasonMetadataService.cs
+++ b/MediaBrowser.Providers/TV/SeasonMetadataService.cs
@@ -86,7 +86,7 @@ namespace MediaBrowser.Providers.TV
}
/// <inheritdoc />
- protected override void MergeData(MetadataResult<Season> source, MetadataResult<Season> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<Season> source, MetadataResult<Season> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}
diff --git a/MediaBrowser.Providers/TV/SeriesMetadataService.cs b/MediaBrowser.Providers/TV/SeriesMetadataService.cs
index 5e75a8125..7a753e832 100644
--- a/MediaBrowser.Providers/TV/SeriesMetadataService.cs
+++ b/MediaBrowser.Providers/TV/SeriesMetadataService.cs
@@ -42,7 +42,8 @@ namespace MediaBrowser.Providers.TV
await seasonProvider.Run(item, cancellationToken).ConfigureAwait(false);
// TODO why does it not register this itself omg
- var provider = new MissingEpisodeProvider(Logger,
+ var provider = new MissingEpisodeProvider(
+ Logger,
ServerConfigurationManager,
LibraryManager,
_localization,
@@ -74,7 +75,7 @@ namespace MediaBrowser.Providers.TV
}
/// <inheritdoc />
- protected override void MergeData(MetadataResult<Series> source, MetadataResult<Series> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<Series> source, MetadataResult<Series> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
diff --git a/MediaBrowser.Providers/TV/TvExternalIds.cs b/MediaBrowser.Providers/TV/TvExternalIds.cs
index baf854285..bd59606e7 100644
--- a/MediaBrowser.Providers/TV/TvExternalIds.cs
+++ b/MediaBrowser.Providers/TV/TvExternalIds.cs
@@ -11,7 +11,7 @@ namespace MediaBrowser.Providers.TV
public string Name => "Zap2It";
/// <inheritdoc />
- public string Key => MetadataProviders.Zap2It.ToString();
+ public string Key => MetadataProvider.Zap2It.ToString();
/// <inheritdoc />
public string UrlFormatString => "http://tvlistings.zap2it.com/overview.html?programSeriesId={0}";
@@ -26,7 +26,7 @@ namespace MediaBrowser.Providers.TV
public string Name => "TheTVDB";
/// <inheritdoc />
- public string Key => MetadataProviders.Tvdb.ToString();
+ public string Key => MetadataProvider.Tvdb.ToString();
/// <inheritdoc />
public string UrlFormatString => TvdbUtils.TvdbBaseUrl + "?tab=series&id={0}";
@@ -42,7 +42,7 @@ namespace MediaBrowser.Providers.TV
public string Name => "TheTVDB";
/// <inheritdoc />
- public string Key => MetadataProviders.Tvdb.ToString();
+ public string Key => MetadataProvider.Tvdb.ToString();
/// <inheritdoc />
public string UrlFormatString => null;
@@ -57,7 +57,7 @@ namespace MediaBrowser.Providers.TV
public string Name => "TheTVDB";
/// <inheritdoc />
- public string Key => MetadataProviders.Tvdb.ToString();
+ public string Key => MetadataProvider.Tvdb.ToString();
/// <inheritdoc />
public string UrlFormatString => TvdbUtils.TvdbBaseUrl + "?tab=episode&id={0}";
diff --git a/MediaBrowser.Providers/Users/UserMetadataService.cs b/MediaBrowser.Providers/Users/UserMetadataService.cs
deleted file mode 100644
index fb6c91b6c..000000000
--- a/MediaBrowser.Providers/Users/UserMetadataService.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Providers.Manager;
-using Microsoft.Extensions.Logging;
-
-namespace MediaBrowser.Providers.Users
-{
- public class UserMetadataService : MetadataService<User, ItemLookupInfo>
- {
- public UserMetadataService(
- IServerConfigurationManager serverConfigurationManager,
- ILogger<UserMetadataService> logger,
- IProviderManager providerManager,
- IFileSystem fileSystem,
- ILibraryManager libraryManager)
- : base(serverConfigurationManager, logger, providerManager, fileSystem, libraryManager)
- {
- }
-
- /// <inheritdoc />
- protected override void MergeData(MetadataResult<User> source, MetadataResult<User> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
- {
- ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
- }
- }
-}
diff --git a/MediaBrowser.Providers/Videos/VideoMetadataService.cs b/MediaBrowser.Providers/Videos/VideoMetadataService.cs
index 21378ada0..31d698f79 100644
--- a/MediaBrowser.Providers/Videos/VideoMetadataService.cs
+++ b/MediaBrowser.Providers/Videos/VideoMetadataService.cs
@@ -26,7 +26,7 @@ namespace MediaBrowser.Providers.Videos
public override int Order => 10;
/// <inheritdoc />
- protected override void MergeData(MetadataResult<Video> source, MetadataResult<Video> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<Video> source, MetadataResult<Video> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}
diff --git a/MediaBrowser.Providers/Years/YearMetadataService.cs b/MediaBrowser.Providers/Years/YearMetadataService.cs
index 2a0fa19ea..0a8d41946 100644
--- a/MediaBrowser.Providers/Years/YearMetadataService.cs
+++ b/MediaBrowser.Providers/Years/YearMetadataService.cs
@@ -22,7 +22,7 @@ namespace MediaBrowser.Providers.Years
}
/// <inheritdoc />
- protected override void MergeData(MetadataResult<Year> source, MetadataResult<Year> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
+ protected override void MergeData(MetadataResult<Year> source, MetadataResult<Year> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}
diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs
index 133a35527..63cbfd9e4 100644
--- a/MediaBrowser.WebDashboard/Api/DashboardService.cs
+++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs
@@ -52,12 +52,6 @@ namespace MediaBrowser.WebDashboard.Api
public string Name { get; set; }
}
- [Route("/web/Package", "GET", IsHidden = true)]
- public class GetDashboardPackage
- {
- public string Mode { get; set; }
- }
-
[Route("/robots.txt", "GET", IsHidden = true)]
public class GetRobotsTxt
{
@@ -96,7 +90,7 @@ namespace MediaBrowser.WebDashboard.Api
/// Gets or sets the logger.
/// </summary>
/// <value>The logger.</value>
- private readonly ILogger _logger;
+ private readonly ILogger<DashboardService> _logger;
/// <summary>
/// Gets or sets the HTTP result factory.
@@ -225,7 +219,7 @@ namespace MediaBrowser.WebDashboard.Api
return _resultFactory.GetStaticResult(Request, plugin.Version.ToString().GetMD5(), null, null, MimeTypes.GetMimeType("page.html"), () => Task.FromResult(stream));
}
- return _resultFactory.GetStaticResult(Request, plugin.Version.ToString().GetMD5(), null, null, MimeTypes.GetMimeType("page.html"), () => PackageCreator.ModifyHtml(false, stream, null, _appHost.ApplicationVersionString, null));
+ return _resultFactory.GetStaticResult(Request, plugin.Version.ToString().GetMD5(), null, null, MimeTypes.GetMimeType("page.html"), () => Task.FromResult(stream));
}
throw new ResourceNotFoundException();
@@ -328,154 +322,19 @@ namespace MediaBrowser.WebDashboard.Api
throw new ResourceNotFoundException();
}
- var path = request.ResourceName;
-
- var contentType = MimeTypes.GetMimeType(path);
+ var path = request?.ResourceName;
var basePath = DashboardUIPath;
// Bounce them to the startup wizard if it hasn't been completed yet
- if (!_serverConfigurationManager.Configuration.IsStartupWizardCompleted &&
- Request.RawUrl.IndexOf("wizard", StringComparison.OrdinalIgnoreCase) == -1 &&
- PackageCreator.IsCoreHtml(path))
- {
- // But don't redirect if an html import is being requested.
- if (path.IndexOf("bower_components", StringComparison.OrdinalIgnoreCase) == -1)
- {
- Request.Response.Redirect("index.html?start=wizard#!/wizardstart.html");
- return null;
- }
- }
-
- var localizationCulture = GetLocalizationCulture();
-
- // Don't cache if not configured to do so
- // But always cache images to simulate production
- if (!_serverConfigurationManager.Configuration.EnableDashboardResponseCaching &&
- !contentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase) &&
- !contentType.StartsWith("font/", StringComparison.OrdinalIgnoreCase))
- {
- var stream = await GetResourceStream(basePath, path, localizationCulture).ConfigureAwait(false);
- return _resultFactory.GetResult(Request, stream, contentType);
- }
-
- TimeSpan? cacheDuration = null;
-
- // Cache images unconditionally - updates to image files will require new filename
- // If there's a version number in the query string we can cache this unconditionally
- if (contentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase) || contentType.StartsWith("font/", StringComparison.OrdinalIgnoreCase) || !string.IsNullOrEmpty(request.V))
+ if (!_serverConfigurationManager.Configuration.IsStartupWizardCompleted
+ && !Request.RawUrl.Contains("wizard", StringComparison.OrdinalIgnoreCase)
+ && Request.RawUrl.Contains("index", StringComparison.OrdinalIgnoreCase))
{
- cacheDuration = TimeSpan.FromDays(365);
- }
-
- var cacheKey = (_appHost.ApplicationVersionString + (localizationCulture ?? string.Empty) + path).GetMD5();
-
- // html gets modified on the fly
- if (contentType.StartsWith("text/html", StringComparison.OrdinalIgnoreCase))
- {
- return await _resultFactory.GetStaticResult(Request, cacheKey, null, cacheDuration, contentType, () => GetResourceStream(basePath, path, localizationCulture)).ConfigureAwait(false);
+ Request.Response.Redirect("index.html?start=wizard#!/wizardstart.html");
+ return null;
}
return await _resultFactory.GetStaticFileResult(Request, _resourceFileManager.GetResourcePath(basePath, path)).ConfigureAwait(false);
}
-
- private string GetLocalizationCulture()
- {
- return _serverConfigurationManager.Configuration.UICulture;
- }
-
- /// <summary>
- /// Gets the resource stream.
- /// </summary>
- private Task<Stream> GetResourceStream(string basePath, string virtualPath, string localizationCulture)
- {
- return GetPackageCreator(basePath)
- .GetResource(virtualPath, null, localizationCulture, _appHost.ApplicationVersionString);
- }
-
- private PackageCreator GetPackageCreator(string basePath)
- {
- return new PackageCreator(basePath, _resourceFileManager);
- }
-
- public async Task<object> Get(GetDashboardPackage request)
- {
- if (!_appConfig.HostWebClient() || DashboardUIPath == null)
- {
- throw new ResourceNotFoundException();
- }
-
- var mode = request.Mode;
-
- var inputPath = string.IsNullOrWhiteSpace(mode) ?
- DashboardUIPath
- : "C:\\dev\\emby-web-mobile-master\\dist";
-
- var targetPath = !string.IsNullOrWhiteSpace(mode) ?
- inputPath
- : "C:\\dev\\emby-web-mobile\\src";
-
- var packageCreator = GetPackageCreator(inputPath);
-
- if (!string.Equals(inputPath, targetPath, StringComparison.OrdinalIgnoreCase))
- {
- try
- {
- Directory.Delete(targetPath, true);
- }
- catch (IOException ex)
- {
- _logger.LogError(ex, "Error deleting {Path}", targetPath);
- }
-
- CopyDirectory(inputPath, targetPath);
- }
-
- var appVersion = _appHost.ApplicationVersionString;
-
- await DumpHtml(packageCreator, inputPath, targetPath, mode, appVersion).ConfigureAwait(false);
-
- return string.Empty;
- }
-
- private async Task DumpHtml(PackageCreator packageCreator, string source, string destination, string mode, string appVersion)
- {
- foreach (var file in _fileSystem.GetFiles(source))
- {
- var filename = file.Name;
-
- if (!string.Equals(file.Extension, ".html", StringComparison.OrdinalIgnoreCase))
- {
- continue;
- }
-
- await DumpFile(packageCreator, filename, Path.Combine(destination, filename), mode, appVersion).ConfigureAwait(false);
- }
- }
-
- private async Task DumpFile(PackageCreator packageCreator, string resourceVirtualPath, string destinationFilePath, string mode, string appVersion)
- {
- using (var stream = await packageCreator.GetResource(resourceVirtualPath, mode, null, appVersion).ConfigureAwait(false))
- using (var fs = new FileStream(destinationFilePath, FileMode.Create, FileAccess.Write, FileShare.Read))
- {
- await stream.CopyToAsync(fs).ConfigureAwait(false);
- }
- }
-
- private void CopyDirectory(string source, string destination)
- {
- Directory.CreateDirectory(destination);
-
- // Now Create all of the directories
- foreach (var dirPath in _fileSystem.GetDirectories(source, true))
- {
- Directory.CreateDirectory(dirPath.FullName.Replace(source, destination, StringComparison.Ordinal));
- }
-
- // Copy all the files & Replaces any files with the same name
- foreach (var newPath in _fileSystem.GetFiles(source, true))
- {
- File.Copy(newPath.FullName, newPath.FullName.Replace(source, destination, StringComparison.Ordinal), true);
- }
- }
}
}
diff --git a/MediaBrowser.WebDashboard/Api/PackageCreator.cs b/MediaBrowser.WebDashboard/Api/PackageCreator.cs
deleted file mode 100644
index b7c15a840..000000000
--- a/MediaBrowser.WebDashboard/Api/PackageCreator.cs
+++ /dev/null
@@ -1,161 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Globalization;
-using System.IO;
-using System.Text;
-using System.Threading.Tasks;
-using MediaBrowser.Controller;
-
-namespace MediaBrowser.WebDashboard.Api
-{
- public class PackageCreator
- {
- private readonly string _basePath;
- private readonly IResourceFileManager _resourceFileManager;
-
- public PackageCreator(string basePath, IResourceFileManager resourceFileManager)
- {
- _basePath = basePath;
- _resourceFileManager = resourceFileManager;
- }
-
- public async Task<Stream> GetResource(
- string virtualPath,
- string mode,
- string localizationCulture,
- string appVersion)
- {
- var resourcePath = _resourceFileManager.GetResourcePath(_basePath, virtualPath);
- Stream resourceStream = File.OpenRead(resourcePath);
-
- if (resourceStream != null && IsCoreHtml(virtualPath))
- {
- bool isMainIndexPage = string.Equals(virtualPath, "index.html", StringComparison.OrdinalIgnoreCase);
- resourceStream = await ModifyHtml(isMainIndexPage, resourceStream, mode, appVersion, localizationCulture).ConfigureAwait(false);
- }
-
- return resourceStream;
- }
-
- public static bool IsCoreHtml(string path)
- {
- if (path.IndexOf(".template.html", StringComparison.OrdinalIgnoreCase) != -1)
- {
- return false;
- }
-
- return string.Equals(Path.GetExtension(path), ".html", StringComparison.OrdinalIgnoreCase);
- }
-
- /// <summary>
- /// Modifies the source HTML stream by adding common meta tags, css and js.
- /// </summary>
- /// <param name="isMainIndexPage">True if the stream contains content for the main index page.</param>
- /// <param name="sourceStream">The stream whose content should be modified.</param>
- /// <param name="mode">The client mode ('cordova', 'android', etc).</param>
- /// <param name="appVersion">The application version.</param>
- /// <param name="localizationCulture">The localization culture.</param>
- /// <returns>
- /// A task that represents the async operation to read and modify the input stream.
- /// The task result contains a stream containing the modified HTML content.
- /// </returns>
- public static async Task<Stream> ModifyHtml(
- bool isMainIndexPage,
- Stream sourceStream,
- string mode,
- string appVersion,
- string localizationCulture)
- {
- string html;
- using (var reader = new StreamReader(sourceStream, Encoding.UTF8))
- {
- html = await reader.ReadToEndAsync().ConfigureAwait(false);
- }
-
- if (isMainIndexPage && !string.IsNullOrWhiteSpace(localizationCulture))
- {
- var lang = localizationCulture.Split('-')[0];
-
- html = html.Replace("<html", "<html data-culture=\"" + localizationCulture + "\" lang=\"" + lang + "\"", StringComparison.Ordinal);
- }
-
- if (isMainIndexPage)
- {
- html = html.Replace("<head>", "<head>" + GetMetaTags(mode), StringComparison.Ordinal);
- }
-
- // Disable embedded scripts from plugins. We'll run them later once resources have loaded
- if (html.IndexOf("<script", StringComparison.OrdinalIgnoreCase) != -1)
- {
- html = html.Replace("<script", "<!--<script", StringComparison.Ordinal);
- html = html.Replace("</script>", "</script>-->", StringComparison.Ordinal);
- }
-
- if (isMainIndexPage)
- {
- html = html.Replace("</body>", GetCommonJavascript(mode, appVersion) + "</body>", StringComparison.Ordinal);
- }
-
- var bytes = Encoding.UTF8.GetBytes(html);
-
- return new MemoryStream(bytes);
- }
-
- /// <summary>
- /// Gets the meta tags.
- /// </summary>
- /// <returns>System.String.</returns>
- private static string GetMetaTags(string mode)
- {
- var sb = new StringBuilder();
-
- if (string.Equals(mode, "cordova", StringComparison.OrdinalIgnoreCase)
- || string.Equals(mode, "android", StringComparison.OrdinalIgnoreCase))
- {
- sb.Append("<meta http-equiv=\"Content-Security-Policy\" content=\"default-src * 'self' 'unsafe-inline' 'unsafe-eval' data: gap: file: filesystem: ws: wss:;\">");
- }
-
- return sb.ToString();
- }
-
- /// <summary>
- /// Gets the common javascript.
- /// </summary>
- /// <param name="mode">The mode.</param>
- /// <param name="version">The version.</param>
- /// <returns>System.String.</returns>
- private static string GetCommonJavascript(string mode, string version)
- {
- var builder = new StringBuilder();
-
- builder.Append("<script>");
- if (!string.IsNullOrWhiteSpace(mode))
- {
- builder.AppendFormat(CultureInfo.InvariantCulture, "window.appMode='{0}';", mode);
- }
- else
- {
- builder.AppendFormat(CultureInfo.InvariantCulture, "window.dashboardVersion='{0}';", version);
- }
-
- builder.Append("</script>");
-
- if (string.Equals(mode, "cordova", StringComparison.OrdinalIgnoreCase))
- {
- builder.Append("<script src=\"cordova.js\" defer></script>");
- }
-
- builder.Append("<script src=\"scripts/apploader.js");
- if (!string.IsNullOrWhiteSpace(version))
- {
- builder.Append("?v=");
- builder.Append(version);
- }
-
- builder.Append("\" defer></script>");
-
- return builder.ToString();
- }
- }
-}
diff --git a/MediaBrowser.XbmcMetadata/EntryPoint.cs b/MediaBrowser.XbmcMetadata/EntryPoint.cs
index 571953b47..11b36285c 100644
--- a/MediaBrowser.XbmcMetadata/EntryPoint.cs
+++ b/MediaBrowser.XbmcMetadata/EntryPoint.cs
@@ -17,7 +17,7 @@ namespace MediaBrowser.XbmcMetadata
public sealed class EntryPoint : IServerEntryPoint
{
private readonly IUserDataManager _userDataManager;
- private readonly ILogger _logger;
+ private readonly ILogger<EntryPoint> _logger;
private readonly IProviderManager _providerManager;
private readonly IConfigurationManager _config;
diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
index 5c8de80f1..b2d99c1a1 100644
--- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
+++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
@@ -212,7 +212,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
var m = Regex.Match(xml, "tt([0-9]{7,8})", RegexOptions.IgnoreCase);
if (m.Success)
{
- item.SetProviderId(MetadataProviders.Imdb, m.Value);
+ item.SetProviderId(MetadataProvider.Imdb, m.Value);
}
// Support Tmdb
@@ -225,7 +225,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
var tmdbId = xml.Substring(index + srch.Length).TrimEnd('/').Split('-')[0];
if (!string.IsNullOrWhiteSpace(tmdbId) && int.TryParse(tmdbId, NumberStyles.Integer, CultureInfo.InvariantCulture, out var value))
{
- item.SetProviderId(MetadataProviders.Tmdb, value.ToString(UsCulture));
+ item.SetProviderId(MetadataProvider.Tmdb, value.ToString(UsCulture));
}
}
@@ -240,7 +240,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
var tvdbId = xml.Substring(index + srch.Length).TrimEnd('/');
if (!string.IsNullOrWhiteSpace(tvdbId) && int.TryParse(tvdbId, NumberStyles.Integer, CultureInfo.InvariantCulture, out var value))
{
- item.SetProviderId(MetadataProviders.Tvdb, value.ToString(UsCulture));
+ item.SetProviderId(MetadataProvider.Tvdb, value.ToString(UsCulture));
}
}
}
@@ -360,9 +360,9 @@ namespace MediaBrowser.XbmcMetadata.Parsers
{
item.LockedFields = val.Split('|').Select(i =>
{
- if (Enum.TryParse(i, true, out MetadataFields field))
+ if (Enum.TryParse(i, true, out MetadataField field))
{
- return (MetadataFields?)field;
+ return (MetadataField?)field;
}
return null;
diff --git a/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs
index c17212f31..b74a9fd8a 100644
--- a/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs
+++ b/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs
@@ -49,12 +49,12 @@ namespace MediaBrowser.XbmcMetadata.Parsers
if (!string.IsNullOrWhiteSpace(imdbId))
{
- item.SetProviderId(MetadataProviders.Imdb, imdbId);
+ item.SetProviderId(MetadataProvider.Imdb, imdbId);
}
if (!string.IsNullOrWhiteSpace(tmdbId))
{
- item.SetProviderId(MetadataProviders.Tmdb, tmdbId);
+ item.SetProviderId(MetadataProvider.Tmdb, tmdbId);
}
break;
@@ -67,7 +67,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
var tmdbcolid = reader.GetAttribute("tmdbcolid");
if (!string.IsNullOrWhiteSpace(tmdbcolid) && movie != null)
{
- movie.SetProviderId(MetadataProviders.TmdbCollection, tmdbcolid);
+ movie.SetProviderId(MetadataProvider.TmdbCollection, tmdbcolid);
}
var val = reader.ReadInnerXml();
diff --git a/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs
index 0954ae206..f079d4a7e 100644
--- a/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs
+++ b/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs
@@ -51,17 +51,17 @@ namespace MediaBrowser.XbmcMetadata.Parsers
if (!string.IsNullOrWhiteSpace(imdbId))
{
- item.SetProviderId(MetadataProviders.Imdb, imdbId);
+ item.SetProviderId(MetadataProvider.Imdb, imdbId);
}
if (!string.IsNullOrWhiteSpace(tmdbId))
{
- item.SetProviderId(MetadataProviders.Tmdb, tmdbId);
+ item.SetProviderId(MetadataProvider.Tmdb, tmdbId);
}
if (!string.IsNullOrWhiteSpace(tvdbId))
{
- item.SetProviderId(MetadataProviders.Tvdb, tvdbId);
+ item.SetProviderId(MetadataProvider.Tvdb, tvdbId);
}
break;
diff --git a/MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs
index 4b1ee4c9c..433a936d9 100644
--- a/MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs
+++ b/MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs
@@ -14,7 +14,7 @@ namespace MediaBrowser.XbmcMetadata.Providers
/// </summary>
public class AlbumNfoProvider : BaseNfoProvider<MusicAlbum>
{
- private readonly ILogger _logger;
+ private readonly ILogger<AlbumNfoProvider> _logger;
private readonly IConfigurationManager _config;
private readonly IProviderManager _providerManager;
diff --git a/MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs
index 3bbfa6e83..d69cdc90a 100644
--- a/MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs
+++ b/MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs
@@ -14,7 +14,7 @@ namespace MediaBrowser.XbmcMetadata.Providers
/// </summary>
public class ArtistNfoProvider : BaseNfoProvider<MusicArtist>
{
- private readonly ILogger _logger;
+ private readonly ILogger<ArtistNfoProvider> _logger;
private readonly IConfigurationManager _config;
private readonly IProviderManager _providerManager;
diff --git a/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs
index 84c99664a..2b1589d47 100644
--- a/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs
+++ b/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs
@@ -15,12 +15,12 @@ namespace MediaBrowser.XbmcMetadata.Providers
public abstract class BaseVideoNfoProvider<T> : BaseNfoProvider<T>
where T : Video, new()
{
- private readonly ILogger _logger;
+ private readonly ILogger<BaseVideoNfoProvider<T>> _logger;
private readonly IConfigurationManager _config;
private readonly IProviderManager _providerManager;
public BaseVideoNfoProvider(
- ILogger logger,
+ ILogger<BaseVideoNfoProvider<T>> logger,
IFileSystem fileSystem,
IConfigurationManager config,
IProviderManager providerManager)
diff --git a/MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs
index b2dc2e38e..26983b1a6 100644
--- a/MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs
+++ b/MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs
@@ -14,7 +14,7 @@ namespace MediaBrowser.XbmcMetadata.Providers
/// </summary>
public class EpisodeNfoProvider : BaseNfoProvider<Episode>
{
- private readonly ILogger _logger;
+ private readonly ILogger<EpisodeNfoProvider> _logger;
private readonly IConfigurationManager _config;
private readonly IProviderManager _providerManager;
diff --git a/MediaBrowser.XbmcMetadata/Providers/SeasonNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/SeasonNfoProvider.cs
index 63ddd6025..0603fd0d1 100644
--- a/MediaBrowser.XbmcMetadata/Providers/SeasonNfoProvider.cs
+++ b/MediaBrowser.XbmcMetadata/Providers/SeasonNfoProvider.cs
@@ -14,7 +14,7 @@ namespace MediaBrowser.XbmcMetadata.Providers
/// </summary>
public class SeasonNfoProvider : BaseNfoProvider<Season>
{
- private readonly ILogger _logger;
+ private readonly ILogger<SeasonNfoProvider> _logger;
private readonly IConfigurationManager _config;
private readonly IProviderManager _providerManager;
diff --git a/MediaBrowser.XbmcMetadata/Providers/SeriesNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/SeriesNfoProvider.cs
index d65914542..7e059e0aa 100644
--- a/MediaBrowser.XbmcMetadata/Providers/SeriesNfoProvider.cs
+++ b/MediaBrowser.XbmcMetadata/Providers/SeriesNfoProvider.cs
@@ -14,7 +14,7 @@ namespace MediaBrowser.XbmcMetadata.Providers
/// </summary>
public class SeriesNfoProvider : BaseNfoProvider<Series>
{
- private readonly ILogger _logger;
+ private readonly ILogger<SeriesNfoProvider> _logger;
private readonly IConfigurationManager _config;
private readonly IProviderManager _providerManager;
diff --git a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs
index 90e8b4b99..d8230d188 100644
--- a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs
+++ b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs
@@ -105,7 +105,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
ILibraryManager libraryManager,
IUserManager userManager,
IUserDataManager userDataManager,
- ILogger logger)
+ ILogger<BaseNfoSaver> logger)
{
Logger = logger;
UserDataManager = userDataManager;
@@ -125,7 +125,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
protected IUserDataManager UserDataManager { get; }
- protected ILogger Logger { get; }
+ protected ILogger<BaseNfoSaver> Logger { get; }
protected ItemUpdateType MinimumUpdateType
{
@@ -543,15 +543,15 @@ namespace MediaBrowser.XbmcMetadata.Savers
writer.WriteElementString("aspectratio", hasAspectRatio.AspectRatio);
}
- var tmdbCollection = item.GetProviderId(MetadataProviders.TmdbCollection);
+ var tmdbCollection = item.GetProviderId(MetadataProvider.TmdbCollection);
if (!string.IsNullOrEmpty(tmdbCollection))
{
writer.WriteElementString("collectionnumber", tmdbCollection);
- writtenProviderIds.Add(MetadataProviders.TmdbCollection.ToString());
+ writtenProviderIds.Add(MetadataProvider.TmdbCollection.ToString());
}
- var imdb = item.GetProviderId(MetadataProviders.Imdb);
+ var imdb = item.GetProviderId(MetadataProvider.Imdb);
if (!string.IsNullOrEmpty(imdb))
{
if (item is Series)
@@ -563,25 +563,25 @@ namespace MediaBrowser.XbmcMetadata.Savers
writer.WriteElementString("imdbid", imdb);
}
- writtenProviderIds.Add(MetadataProviders.Imdb.ToString());
+ writtenProviderIds.Add(MetadataProvider.Imdb.ToString());
}
// Series xml saver already saves this
if (!(item is Series))
{
- var tvdb = item.GetProviderId(MetadataProviders.Tvdb);
+ var tvdb = item.GetProviderId(MetadataProvider.Tvdb);
if (!string.IsNullOrEmpty(tvdb))
{
writer.WriteElementString("tvdbid", tvdb);
- writtenProviderIds.Add(MetadataProviders.Tvdb.ToString());
+ writtenProviderIds.Add(MetadataProvider.Tvdb.ToString());
}
}
- var tmdb = item.GetProviderId(MetadataProviders.Tmdb);
+ var tmdb = item.GetProviderId(MetadataProvider.Tmdb);
if (!string.IsNullOrEmpty(tmdb))
{
writer.WriteElementString("tmdbid", tmdb);
- writtenProviderIds.Add(MetadataProviders.Tmdb.ToString());
+ writtenProviderIds.Add(MetadataProvider.Tmdb.ToString());
}
if (!string.IsNullOrEmpty(item.PreferredMetadataLanguage))
@@ -686,67 +686,67 @@ namespace MediaBrowser.XbmcMetadata.Savers
}
}
- var externalId = item.GetProviderId(MetadataProviders.AudioDbArtist);
+ var externalId = item.GetProviderId(MetadataProvider.AudioDbArtist);
if (!string.IsNullOrEmpty(externalId))
{
writer.WriteElementString("audiodbartistid", externalId);
- writtenProviderIds.Add(MetadataProviders.AudioDbArtist.ToString());
+ writtenProviderIds.Add(MetadataProvider.AudioDbArtist.ToString());
}
- externalId = item.GetProviderId(MetadataProviders.AudioDbAlbum);
+ externalId = item.GetProviderId(MetadataProvider.AudioDbAlbum);
if (!string.IsNullOrEmpty(externalId))
{
writer.WriteElementString("audiodbalbumid", externalId);
- writtenProviderIds.Add(MetadataProviders.AudioDbAlbum.ToString());
+ writtenProviderIds.Add(MetadataProvider.AudioDbAlbum.ToString());
}
- externalId = item.GetProviderId(MetadataProviders.Zap2It);
+ externalId = item.GetProviderId(MetadataProvider.Zap2It);
if (!string.IsNullOrEmpty(externalId))
{
writer.WriteElementString("zap2itid", externalId);
- writtenProviderIds.Add(MetadataProviders.Zap2It.ToString());
+ writtenProviderIds.Add(MetadataProvider.Zap2It.ToString());
}
- externalId = item.GetProviderId(MetadataProviders.MusicBrainzAlbum);
+ externalId = item.GetProviderId(MetadataProvider.MusicBrainzAlbum);
if (!string.IsNullOrEmpty(externalId))
{
writer.WriteElementString("musicbrainzalbumid", externalId);
- writtenProviderIds.Add(MetadataProviders.MusicBrainzAlbum.ToString());
+ writtenProviderIds.Add(MetadataProvider.MusicBrainzAlbum.ToString());
}
- externalId = item.GetProviderId(MetadataProviders.MusicBrainzAlbumArtist);
+ externalId = item.GetProviderId(MetadataProvider.MusicBrainzAlbumArtist);
if (!string.IsNullOrEmpty(externalId))
{
writer.WriteElementString("musicbrainzalbumartistid", externalId);
- writtenProviderIds.Add(MetadataProviders.MusicBrainzAlbumArtist.ToString());
+ writtenProviderIds.Add(MetadataProvider.MusicBrainzAlbumArtist.ToString());
}
- externalId = item.GetProviderId(MetadataProviders.MusicBrainzArtist);
+ externalId = item.GetProviderId(MetadataProvider.MusicBrainzArtist);
if (!string.IsNullOrEmpty(externalId))
{
writer.WriteElementString("musicbrainzartistid", externalId);
- writtenProviderIds.Add(MetadataProviders.MusicBrainzArtist.ToString());
+ writtenProviderIds.Add(MetadataProvider.MusicBrainzArtist.ToString());
}
- externalId = item.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup);
+ externalId = item.GetProviderId(MetadataProvider.MusicBrainzReleaseGroup);
if (!string.IsNullOrEmpty(externalId))
{
writer.WriteElementString("musicbrainzreleasegroupid", externalId);
- writtenProviderIds.Add(MetadataProviders.MusicBrainzReleaseGroup.ToString());
+ writtenProviderIds.Add(MetadataProvider.MusicBrainzReleaseGroup.ToString());
}
- externalId = item.GetProviderId(MetadataProviders.TvRage);
+ externalId = item.GetProviderId(MetadataProvider.TvRage);
if (!string.IsNullOrEmpty(externalId))
{
writer.WriteElementString("tvrageid", externalId);
- writtenProviderIds.Add(MetadataProviders.TvRage.ToString());
+ writtenProviderIds.Add(MetadataProvider.TvRage.ToString());
}
if (item.ProviderIds != null)
@@ -974,7 +974,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
=> string.Equals(person.Type, type, StringComparison.OrdinalIgnoreCase)
|| string.Equals(person.Role, type, StringComparison.OrdinalIgnoreCase);
- private void AddCustomTags(string path, List<string> xmlTagsUsed, XmlWriter writer, ILogger logger)
+ private void AddCustomTags(string path, List<string> xmlTagsUsed, XmlWriter writer, ILogger<BaseNfoSaver> logger)
{
var settings = new XmlReaderSettings()
{
diff --git a/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs
index eef989a5b..dca796415 100644
--- a/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs
+++ b/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs
@@ -93,7 +93,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
/// <inheritdoc />
protected override void WriteCustomElements(BaseItem item, XmlWriter writer)
{
- var imdb = item.GetProviderId(MetadataProviders.Imdb);
+ var imdb = item.GetProviderId(MetadataProvider.Imdb);
if (!string.IsNullOrEmpty(imdb))
{
diff --git a/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs
index 2a5d36d40..42285db76 100644
--- a/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs
+++ b/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs
@@ -54,7 +54,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
{
var series = (Series)item;
- var tvdb = item.GetProviderId(MetadataProviders.Tvdb);
+ var tvdb = item.GetProviderId(MetadataProvider.Tvdb);
if (!string.IsNullOrEmpty(tvdb))
{
diff --git a/RSSDP/RSSDP.csproj b/RSSDP/RSSDP.csproj
index e3f3127b6..553693171 100644
--- a/RSSDP/RSSDP.csproj
+++ b/RSSDP/RSSDP.csproj
@@ -14,6 +14,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+ <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
</Project>
diff --git a/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs b/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs
index 3b3d03c8b..4245c3249 100644
--- a/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs
+++ b/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs
@@ -7,6 +7,8 @@ using AutoFixture;
using AutoFixture.AutoMoq;
using Jellyfin.Api.Auth;
using Jellyfin.Api.Constants;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Net;
using Microsoft.AspNetCore.Authentication;
@@ -124,7 +126,7 @@ namespace Jellyfin.Api.Tests.Auth
var user = SetupUser();
var authenticateResult = await _sut.AuthenticateAsync();
- Assert.True(authenticateResult.Principal.HasClaim(ClaimTypes.Name, user.Name));
+ Assert.True(authenticateResult.Principal.HasClaim(ClaimTypes.Name, user.Username));
}
[Theory]
@@ -135,7 +137,7 @@ namespace Jellyfin.Api.Tests.Auth
var user = SetupUser(isAdmin);
var authenticateResult = await _sut.AuthenticateAsync();
- var expectedRole = user.Policy.IsAdministrator ? UserRoles.Administrator : UserRoles.User;
+ var expectedRole = user.HasPermission(PermissionKind.IsAdministrator) ? UserRoles.Administrator : UserRoles.User;
Assert.True(authenticateResult.Principal.HasClaim(ClaimTypes.Role, expectedRole));
}
@@ -151,7 +153,7 @@ namespace Jellyfin.Api.Tests.Auth
private User SetupUser(bool isAdmin = false)
{
var user = _fixture.Create<User>();
- user.Policy.IsAdministrator = isAdmin;
+ user.SetPermission(PermissionKind.IsAdministrator, isAdmin);
_jellyfinAuthServiceMock.Setup(
a => a.Authenticate(
diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
index 9c4b7b0b0..269ff0986 100644
--- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
+++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
@@ -16,12 +16,12 @@
<PackageReference Include="AutoFixture" Version="4.11.0" />
<PackageReference Include="AutoFixture.AutoMoq" Version="4.11.0" />
<PackageReference Include="AutoFixture.Xunit2" Version="4.11.0" />
- <PackageReference Include="Microsoft.Extensions.Options" Version="3.1.4" />
- <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
+ <PackageReference Include="Microsoft.Extensions.Options" Version="3.1.5" />
+ <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
<PackageReference Include="coverlet.collector" Version="1.2.1" />
- <PackageReference Include="Moq" Version="4.13.1" />
+ <PackageReference Include="Moq" Version="4.14.1" />
</ItemGroup>
<ItemGroup>
diff --git a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj
index cd41c5604..003e29a66 100644
--- a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj
+++ b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj
@@ -13,7 +13,7 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
+ <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
<PackageReference Include="coverlet.collector" Version="1.2.1" />
diff --git a/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj b/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj
index 407fe2eda..3c806be17 100644
--- a/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj
+++ b/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj
@@ -13,7 +13,7 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
+ <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
<PackageReference Include="coverlet.collector" Version="1.2.1" />
diff --git a/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj b/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj
index 276c50ca3..c2e7bf54c 100644
--- a/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj
+++ b/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj
@@ -19,7 +19,7 @@
</ItemGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
+ <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
<PackageReference Include="coverlet.collector" Version="1.2.1" />
diff --git a/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj b/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj
index 8b14cf800..43b34dcee 100644
--- a/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj
+++ b/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj
@@ -12,7 +12,7 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
+ <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
<PackageReference Include="coverlet.collector" Version="1.2.1" />
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 ba7ecb3d1..3bad6bb7b 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj
+++ b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj
@@ -16,7 +16,7 @@
<ItemGroup>
<PackageReference Include="AutoFixture" Version="4.11.0" />
<PackageReference Include="AutoFixture.AutoMoq" Version="4.11.0" />
- <PackageReference Include="Moq" Version="4.13.1" />
+ <PackageReference Include="Moq" Version="4.14.1" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
<PackageReference Include="coverlet.collector" Version="1.2.1" />
diff --git a/tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.csproj b/tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.csproj
index 60c392314..c845ca6cf 100644
--- a/tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.csproj
+++ b/tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.csproj
@@ -8,8 +8,8 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="3.1.4" />
- <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
+ <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="3.1.5" />
+ <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
<PackageReference Include="coverlet.collector" Version="1.2.1" />