aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.ci/azure-pipelines-package.yml31
-rw-r--r--Emby.Dlna/Common/ServiceAction.cs2
-rw-r--r--Emby.Dlna/Common/StateVariable.cs3
-rw-r--r--Emby.Dlna/ConfigurationExtension.cs16
-rw-r--r--Emby.Dlna/ConnectionManager/ConnectionManagerService.cs (renamed from Emby.Dlna/ConnectionManager/ConnectionManager.cs)10
-rw-r--r--Emby.Dlna/ConnectionManager/ConnectionManagerXmlBuilder.cs4
-rw-r--r--Emby.Dlna/ContentDirectory/ContentDirectoryService.cs (renamed from Emby.Dlna/ContentDirectory/ContentDirectory.cs)6
-rw-r--r--Emby.Dlna/ContentDirectory/ContentDirectoryXmlBuilder.cs5
-rw-r--r--Emby.Dlna/ContentDirectory/ControlHandler.cs158
-rw-r--r--Emby.Dlna/ContentDirectory/ServerItem.cs23
-rw-r--r--Emby.Dlna/ContentDirectory/StubType.cs28
-rw-r--r--Emby.Dlna/ControlRequest.cs12
-rw-r--r--Emby.Dlna/ControlResponse.cs2
-rw-r--r--Emby.Dlna/Didl/DidlBuilder.cs157
-rw-r--r--Emby.Dlna/Didl/Filter.cs4
-rw-r--r--Emby.Dlna/Didl/StringWriterWithEncoding.cs2
-rw-r--r--Emby.Dlna/DlnaConfigurationFactory.cs24
-rw-r--r--Emby.Dlna/DlnaManager.cs31
-rw-r--r--Emby.Dlna/Emby.Dlna.csproj2
-rw-r--r--Emby.Dlna/EventSubscriptionResponse.cs2
-rw-r--r--Emby.Dlna/Eventing/DlnaEventManager.cs (renamed from Emby.Dlna/Eventing/EventManager.cs)12
-rw-r--r--Emby.Dlna/IConnectionManager.cs2
-rw-r--r--Emby.Dlna/IContentDirectory.cs2
-rw-r--r--Emby.Dlna/IDlnaEventManager.cs (renamed from Emby.Dlna/IEventManager.cs)12
-rw-r--r--Emby.Dlna/IMediaReceiverRegistrar.cs2
-rw-r--r--Emby.Dlna/Main/DlnaEntryPoint.cs56
-rw-r--r--Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarService.cs (renamed from Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrar.cs)6
-rw-r--r--Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs3
-rw-r--r--Emby.Dlna/PlayTo/Device.cs225
-rw-r--r--Emby.Dlna/PlayTo/DeviceInfo.cs5
-rw-r--r--Emby.Dlna/PlayTo/MediaChangedEventArgs.cs13
-rw-r--r--Emby.Dlna/PlayTo/PlayToController.cs251
-rw-r--r--Emby.Dlna/PlayTo/PlayToManager.cs57
-rw-r--r--Emby.Dlna/PlayTo/PlaybackProgressEventArgs.cs2
-rw-r--r--Emby.Dlna/PlayTo/PlaybackStartEventArgs.cs2
-rw-r--r--Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs9
-rw-r--r--Emby.Dlna/PlayTo/TRANSPORTSTATE.cs13
-rw-r--r--Emby.Dlna/PlayTo/TransportCommands.cs70
-rw-r--r--Emby.Dlna/PlayTo/TransportState.cs14
-rw-r--r--Emby.Dlna/PlayTo/UpnpContainer.cs16
-rw-r--r--Emby.Dlna/PlayTo/uBaseObject.cs25
-rw-r--r--Emby.Dlna/PlayTo/uPnpNamespaces.cs90
-rw-r--r--Emby.Dlna/Profiles/DefaultProfile.cs4
-rw-r--r--Emby.Dlna/Profiles/DishHopperJoeyProfile.cs12
-rw-r--r--Emby.Dlna/Profiles/LgTvProfile.cs4
-rw-r--r--Emby.Dlna/Profiles/LinksysDMA2100Profile.cs2
-rw-r--r--Emby.Dlna/Profiles/PanasonicVieraProfile.cs2
-rw-r--r--Emby.Dlna/Profiles/PopcornHourProfile.cs14
-rw-r--r--Emby.Dlna/Profiles/SamsungSmartTvProfile.cs2
-rw-r--r--Emby.Dlna/Profiles/SonyBlurayPlayer2013.cs6
-rw-r--r--Emby.Dlna/Profiles/SonyBlurayPlayer2014.cs6
-rw-r--r--Emby.Dlna/Profiles/SonyBlurayPlayer2015.cs6
-rw-r--r--Emby.Dlna/Profiles/SonyBlurayPlayer2016.cs6
-rw-r--r--Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs10
-rw-r--r--Emby.Dlna/Profiles/SonyBravia2010Profile.cs44
-rw-r--r--Emby.Dlna/Profiles/SonyBravia2011Profile.cs42
-rw-r--r--Emby.Dlna/Profiles/SonyBravia2012Profile.cs34
-rw-r--r--Emby.Dlna/Profiles/SonyBravia2013Profile.cs37
-rw-r--r--Emby.Dlna/Profiles/SonyBravia2014Profile.cs37
-rw-r--r--Emby.Dlna/Profiles/SonyPs3Profile.cs14
-rw-r--r--Emby.Dlna/Profiles/SonyPs4Profile.cs14
-rw-r--r--Emby.Dlna/Profiles/WdtvLiveProfile.cs8
-rw-r--r--Emby.Dlna/Profiles/XboxOneProfile.cs14
-rw-r--r--Emby.Dlna/Service/BaseControlHandler.cs34
-rw-r--r--Emby.Dlna/Service/BaseService.cs14
-rw-r--r--Emby.Dlna/Service/ControlErrorHandler.cs10
-rw-r--r--Emby.Dlna/Service/ServiceXmlBuilder.cs2
-rw-r--r--Emby.Dlna/Ssdp/DeviceDiscovery.cs19
-rw-r--r--Emby.Dlna/Ssdp/SsdpExtensions.cs (renamed from Emby.Dlna/Ssdp/Extensions.cs)2
-rw-r--r--Emby.Naming/Emby.Naming.csproj3
-rw-r--r--Emby.Notifications/NotificationEntryPoint.cs2
-rw-r--r--Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs590
-rw-r--r--Emby.Server.Implementations/ApplicationHost.cs174
-rw-r--r--Emby.Server.Implementations/Channels/ChannelManager.cs21
-rw-r--r--Emby.Server.Implementations/Collections/CollectionManager.cs51
-rw-r--r--Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs2
-rw-r--r--Emby.Server.Implementations/Data/SqliteItemRepository.cs11
-rw-r--r--Emby.Server.Implementations/Devices/DeviceManager.cs2
-rw-r--r--Emby.Server.Implementations/Emby.Server.Implementations.csproj6
-rw-r--r--Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs2
-rw-r--r--Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs2
-rw-r--r--Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs9
-rw-r--r--Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs210
-rw-r--r--Emby.Server.Implementations/HttpServer/HttpListenerHost.cs2
-rw-r--r--Emby.Server.Implementations/HttpServer/WebSocketConnection.cs8
-rw-r--r--Emby.Server.Implementations/IO/LibraryMonitor.cs27
-rw-r--r--Emby.Server.Implementations/IO/LibraryMonitorStartup.cs35
-rw-r--r--Emby.Server.Implementations/Library/LibraryManager.cs41
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs5
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvManager.cs124
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs23
-rw-r--r--Emby.Server.Implementations/Localization/Core/id.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/ru.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/th.json5
-rw-r--r--Emby.Server.Implementations/Playlists/PlaylistManager.cs20
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs150
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/TaskManager.cs3
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs64
-rw-r--r--Emby.Server.Implementations/Services/ServicePath.cs8
-rw-r--r--Emby.Server.Implementations/Session/SessionManager.cs117
-rw-r--r--Emby.Server.Implementations/Session/SessionWebSocketListener.cs2
-rw-r--r--Jellyfin.Api/Controllers/AudioController.cs4
-rw-r--r--Jellyfin.Api/Controllers/CollectionController.cs15
-rw-r--r--Jellyfin.Api/Controllers/ConfigurationController.cs2
-rw-r--r--Jellyfin.Api/Controllers/DlnaServerController.cs11
-rw-r--r--Jellyfin.Api/Controllers/DynamicHlsController.cs2
-rw-r--r--Jellyfin.Api/Controllers/ImageController.cs20
-rw-r--r--Jellyfin.Api/Controllers/ItemUpdateController.cs7
-rw-r--r--Jellyfin.Api/Controllers/ItemsController.cs6
-rw-r--r--Jellyfin.Api/Controllers/LibraryController.cs13
-rw-r--r--Jellyfin.Api/Controllers/LibraryStructureController.cs12
-rw-r--r--Jellyfin.Api/Controllers/MediaInfoController.cs4
-rw-r--r--Jellyfin.Api/Controllers/MoviesController.cs3
-rw-r--r--Jellyfin.Api/Controllers/PlaylistsController.cs14
-rw-r--r--Jellyfin.Api/Controllers/PluginsController.cs8
-rw-r--r--Jellyfin.Api/Controllers/RemoteImageController.cs2
-rw-r--r--Jellyfin.Api/Controllers/SessionController.cs16
-rw-r--r--Jellyfin.Api/Controllers/StartupController.cs27
-rw-r--r--Jellyfin.Api/Controllers/UniversalAudioController.cs4
-rw-r--r--Jellyfin.Api/Controllers/VideosController.cs14
-rw-r--r--Jellyfin.Api/Helpers/MediaInfoHelper.cs6
-rw-r--r--Jellyfin.Api/Models/LibraryStructureDto/AddVirtualFolderDto.cs (renamed from Jellyfin.Api/Models/LibraryStructureDto/LibraryOptionsDto.cs)6
-rw-r--r--Jellyfin.Api/Models/LibraryStructureDto/UpdateLibraryOptionsDto.cs21
-rw-r--r--Jellyfin.Api/Models/StartupDtos/StartupRemoteAccessDto.cs22
-rw-r--r--Jellyfin.Api/WebSocketListeners/ActivityLogWebSocketListener.cs2
-rw-r--r--Jellyfin.Api/WebSocketListeners/ScheduledTasksWebSocketListener.cs2
-rw-r--r--Jellyfin.Data/DayOfWeekHelper.cs2
-rw-r--r--Jellyfin.Data/Entities/ActivityLog.cs5
-rw-r--r--Jellyfin.Data/Entities/Artwork.cs208
-rw-r--r--Jellyfin.Data/Entities/Book.cs68
-rw-r--r--Jellyfin.Data/Entities/BookMetadata.cs119
-rw-r--r--Jellyfin.Data/Entities/Chapter.cs275
-rw-r--r--Jellyfin.Data/Entities/Collection.cs121
-rw-r--r--Jellyfin.Data/Entities/CollectionItem.cs154
-rw-r--r--Jellyfin.Data/Entities/Company.cs157
-rw-r--r--Jellyfin.Data/Entities/CompanyMetadata.cs230
-rw-r--r--Jellyfin.Data/Entities/CustomItem.cs67
-rw-r--r--Jellyfin.Data/Entities/CustomItemMetadata.cs77
-rw-r--r--Jellyfin.Data/Entities/Episode.cs114
-rw-r--r--Jellyfin.Data/Entities/EpisodeMetadata.cs192
-rw-r--r--Jellyfin.Data/Entities/Genre.cs160
-rw-r--r--Jellyfin.Data/Entities/Group.cs5
-rw-r--r--Jellyfin.Data/Entities/ImageInfo.cs4
-rw-r--r--Jellyfin.Data/Entities/ItemDisplayPreferences.cs4
-rw-r--r--Jellyfin.Data/Entities/Libraries/Artwork.cs81
-rw-r--r--Jellyfin.Data/Entities/Libraries/Book.cs28
-rw-r--r--Jellyfin.Data/Entities/Libraries/BookMetadata.cs55
-rw-r--r--Jellyfin.Data/Entities/Libraries/Chapter.cs102
-rw-r--r--Jellyfin.Data/Entities/Libraries/Collection.cs55
-rw-r--r--Jellyfin.Data/Entities/Libraries/CollectionItem.cs94
-rw-r--r--Jellyfin.Data/Entities/Libraries/Company.cs67
-rw-r--r--Jellyfin.Data/Entities/Libraries/CompanyMetadata.cs74
-rw-r--r--Jellyfin.Data/Entities/Libraries/CustomItem.cs28
-rw-r--r--Jellyfin.Data/Entities/Libraries/CustomItemMetadata.cs36
-rw-r--r--Jellyfin.Data/Entities/Libraries/Episode.cs52
-rw-r--r--Jellyfin.Data/Entities/Libraries/EpisodeMetadata.cs67
-rw-r--r--Jellyfin.Data/Entities/Libraries/Genre.cs75
-rw-r--r--Jellyfin.Data/Entities/Libraries/Library.cs76
-rw-r--r--Jellyfin.Data/Entities/Libraries/LibraryItem.cs63
-rw-r--r--Jellyfin.Data/Entities/Libraries/MediaFile.cs94
-rw-r--r--Jellyfin.Data/Entities/Libraries/MediaFileStream.cs67
-rw-r--r--Jellyfin.Data/Entities/Libraries/Metadata.cs165
-rw-r--r--Jellyfin.Data/Entities/Libraries/MetadataProvider.cs67
-rw-r--r--Jellyfin.Data/Entities/Libraries/MetadataProviderId.cs83
-rw-r--r--Jellyfin.Data/Entities/Libraries/Movie.cs28
-rw-r--r--Jellyfin.Data/Entities/Libraries/MovieMetadata.cs85
-rw-r--r--Jellyfin.Data/Entities/Libraries/MusicAlbum.cs29
-rw-r--r--Jellyfin.Data/Entities/Libraries/MusicAlbumMetadata.cs69
-rw-r--r--Jellyfin.Data/Entities/Libraries/Person.cs103
-rw-r--r--Jellyfin.Data/Entities/Libraries/PersonRole.cs98
-rw-r--r--Jellyfin.Data/Entities/Libraries/Photo.cs28
-rw-r--r--Jellyfin.Data/Entities/Libraries/PhotoMetadata.cs36
-rw-r--r--Jellyfin.Data/Entities/Libraries/Rating.cs78
-rw-r--r--Jellyfin.Data/Entities/Libraries/RatingSource.cs92
-rw-r--r--Jellyfin.Data/Entities/Libraries/Release.cs84
-rw-r--r--Jellyfin.Data/Entities/Libraries/Season.cs53
-rw-r--r--Jellyfin.Data/Entities/Libraries/SeasonMetadata.cs47
-rw-r--r--Jellyfin.Data/Entities/Libraries/Series.cs46
-rw-r--r--Jellyfin.Data/Entities/Libraries/SeriesMetadata.cs91
-rw-r--r--Jellyfin.Data/Entities/Libraries/Track.cs52
-rw-r--r--Jellyfin.Data/Entities/Libraries/TrackMetadata.cs36
-rw-r--r--Jellyfin.Data/Entities/Library.cs151
-rw-r--r--Jellyfin.Data/Entities/LibraryItem.cs172
-rw-r--r--Jellyfin.Data/Entities/LibraryRoot.cs197
-rw-r--r--Jellyfin.Data/Entities/MediaFile.cs210
-rw-r--r--Jellyfin.Data/Entities/MediaFileStream.cs153
-rw-r--r--Jellyfin.Data/Entities/Metadata.cs395
-rw-r--r--Jellyfin.Data/Entities/MetadataProvider.cs151
-rw-r--r--Jellyfin.Data/Entities/MetadataProviderId.cs199
-rw-r--r--Jellyfin.Data/Entities/Movie.cs68
-rw-r--r--Jellyfin.Data/Entities/MovieMetadata.cs238
-rw-r--r--Jellyfin.Data/Entities/MusicAlbum.cs67
-rw-r--r--Jellyfin.Data/Entities/MusicAlbumMetadata.cs201
-rw-r--r--Jellyfin.Data/Entities/Permission.cs5
-rw-r--r--Jellyfin.Data/Entities/Person.cs311
-rw-r--r--Jellyfin.Data/Entities/PersonRole.cs215
-rw-r--r--Jellyfin.Data/Entities/Photo.cs67
-rw-r--r--Jellyfin.Data/Entities/PhotoMetadata.cs78
-rw-r--r--Jellyfin.Data/Entities/Preference.cs3
-rw-r--r--Jellyfin.Data/Entities/ProviderMapping.cs2
-rw-r--r--Jellyfin.Data/Entities/Rating.cs192
-rw-r--r--Jellyfin.Data/Entities/RatingSource.cs237
-rw-r--r--Jellyfin.Data/Entities/Release.cs217
-rw-r--r--Jellyfin.Data/Entities/Season.cs115
-rw-r--r--Jellyfin.Data/Entities/SeasonMetadata.cs117
-rw-r--r--Jellyfin.Data/Entities/Series.cs161
-rw-r--r--Jellyfin.Data/Entities/SeriesMetadata.cs238
-rw-r--r--Jellyfin.Data/Entities/Track.cs116
-rw-r--r--Jellyfin.Data/Entities/TrackMetadata.cs78
-rw-r--r--Jellyfin.Data/Entities/User.cs5
-rw-r--r--Jellyfin.Data/Enums/ArtKind.cs2
-rw-r--r--Jellyfin.Data/Enums/IndexingKind.cs4
-rw-r--r--Jellyfin.Data/Enums/MediaFileKind.cs2
-rw-r--r--Jellyfin.Data/Enums/PersonRoleType.cs2
-rw-r--r--Jellyfin.Data/Events/GenericEventArgs.cs (renamed from MediaBrowser.Model/Events/GenericEventArgs.cs)16
-rw-r--r--Jellyfin.Data/Events/System/PendingRestartEventArgs.cs11
-rw-r--r--Jellyfin.Data/Events/Users/UserCreatedEventArgs.cs18
-rw-r--r--Jellyfin.Data/Events/Users/UserDeletedEventArgs.cs18
-rw-r--r--Jellyfin.Data/Events/Users/UserLockedOutEventArgs.cs18
-rw-r--r--Jellyfin.Data/Events/Users/UserPasswordChangedEventArgs.cs18
-rw-r--r--Jellyfin.Data/Events/Users/UserUpdatedEventArgs.cs18
-rw-r--r--Jellyfin.Data/ISavingChanges.cs9
-rw-r--r--Jellyfin.Data/Interfaces/IHasArtwork.cs16
-rw-r--r--Jellyfin.Data/Interfaces/IHasCompanies.cs16
-rw-r--r--Jellyfin.Data/Interfaces/IHasConcurrencyToken.cs18
-rw-r--r--Jellyfin.Data/Interfaces/IHasPermissions.cs (renamed from Jellyfin.Data/IHasPermissions.cs)0
-rw-r--r--Jellyfin.Data/Interfaces/IHasReleases.cs16
-rw-r--r--Jellyfin.Data/Jellyfin.Data.csproj9
-rw-r--r--Jellyfin.Server.Implementations/Activity/ActivityManager.cs12
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Library/SubtitleDownloadFailureLogger.cs102
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationFailedLogger.cs52
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationSucceededLogger.cs49
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStartLogger.cs104
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStopLogger.cs106
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Session/SessionEndedLogger.cs54
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Session/SessionStartedLogger.cs54
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/System/PendingRestartNotifier.cs31
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/System/TaskCompletedLogger.cs158
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/System/TaskCompletedNotifier.cs31
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationCancelledNotifier.cs31
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationFailedLogger.cs51
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationFailedNotifier.cs31
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstalledLogger.cs50
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstalledNotifier.cs31
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallingNotifier.cs31
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUninstalledLogger.cs45
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUninstalledNotifier.cs31
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUpdatedLogger.cs51
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Users/UserCreatedLogger.cs43
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Users/UserDeletedLogger.cs44
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Users/UserDeletedNotifier.cs38
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Users/UserLockedOutLogger.cs47
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Users/UserPasswordChangedLogger.cs43
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Users/UserUpdatedNotifier.cs41
-rw-r--r--Jellyfin.Server.Implementations/Events/EventManager.cs60
-rw-r--r--Jellyfin.Server.Implementations/Events/EventingServiceCollectionExtensions.cs72
-rw-r--r--Jellyfin.Server.Implementations/JellyfinDb.cs4
-rw-r--r--Jellyfin.Server.Implementations/Users/DeviceAccessEntryPoint.cs2
-rw-r--r--Jellyfin.Server.Implementations/Users/UserManager.cs43
-rw-r--r--Jellyfin.Server/CoreAppHost.cs27
-rw-r--r--Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs21
-rw-r--r--Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs5
-rw-r--r--Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs13
-rw-r--r--Jellyfin.Server/Program.cs7
-rw-r--r--Jellyfin.Server/Startup.cs2
-rw-r--r--MediaBrowser.Common/IApplicationHost.cs10
-rw-r--r--MediaBrowser.Common/Json/Converters/JsonDoubleConverter.cs56
-rw-r--r--MediaBrowser.Common/Json/Converters/JsonInt32Converter.cs40
-rw-r--r--MediaBrowser.Common/Json/Converters/JsonInt64Converter.cs56
-rw-r--r--MediaBrowser.Common/Json/Converters/JsonNonStringKeyDictionaryConverter.cs82
-rw-r--r--MediaBrowser.Common/Json/Converters/JsonNonStringKeyDictionaryConverterFactory.cs59
-rw-r--r--MediaBrowser.Common/Json/JsonDefaults.cs8
-rw-r--r--MediaBrowser.Common/MediaBrowser.Common.csproj3
-rw-r--r--MediaBrowser.Common/Plugins/BasePlugin.cs11
-rw-r--r--MediaBrowser.Common/Plugins/IPlugin.cs13
-rw-r--r--MediaBrowser.Common/Updates/InstallationEventArgs.cs3
-rw-r--r--MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs4
-rw-r--r--MediaBrowser.Controller/Authentication/IPasswordResetProvider.cs3
-rw-r--r--MediaBrowser.Controller/Channels/Channel.cs2
-rw-r--r--MediaBrowser.Controller/Channels/ChannelItemInfo.cs2
-rw-r--r--MediaBrowser.Controller/Channels/ChannelItemResult.cs2
-rw-r--r--MediaBrowser.Controller/Channels/ChannelItemType.cs2
-rw-r--r--MediaBrowser.Controller/Channels/ChannelParentalRating.cs2
-rw-r--r--MediaBrowser.Controller/Channels/ChannelSearchInfo.cs2
-rw-r--r--MediaBrowser.Controller/Channels/IChannel.cs2
-rw-r--r--MediaBrowser.Controller/Channels/IChannelManager.cs3
-rw-r--r--MediaBrowser.Controller/Channels/IHasCacheKey.cs2
-rw-r--r--MediaBrowser.Controller/Channels/IRequiresMediaInfoCallback.cs2
-rw-r--r--MediaBrowser.Controller/Channels/ISearchableChannel.cs3
-rw-r--r--MediaBrowser.Controller/Channels/InternalChannelFeatures.cs18
-rw-r--r--MediaBrowser.Controller/Channels/InternalChannelItemQuery.cs3
-rw-r--r--MediaBrowser.Controller/Collections/CollectionCreationOptions.cs16
-rw-r--r--MediaBrowser.Controller/Collections/CollectionEvents.cs2
-rw-r--r--MediaBrowser.Controller/Collections/ICollectionManager.cs14
-rw-r--r--MediaBrowser.Controller/Devices/IDeviceManager.cs8
-rw-r--r--MediaBrowser.Controller/Dlna/IDlnaManager.cs2
-rw-r--r--MediaBrowser.Controller/Drawing/IImageEncoder.cs2
-rw-r--r--MediaBrowser.Controller/Drawing/IImageProcessor.cs2
-rw-r--r--MediaBrowser.Controller/Drawing/ImageCollageOptions.cs5
-rw-r--r--MediaBrowser.Controller/Drawing/ImageHelper.cs2
-rw-r--r--MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs2
-rw-r--r--MediaBrowser.Controller/Drawing/ImageProcessorExtensions.cs2
-rw-r--r--MediaBrowser.Controller/Drawing/ImageStream.cs3
-rw-r--r--MediaBrowser.Controller/Dto/DtoOptions.cs2
-rw-r--r--MediaBrowser.Controller/Entities/AggregateFolder.cs3
-rw-r--r--MediaBrowser.Controller/Entities/Audio/Audio.cs2
-rw-r--r--MediaBrowser.Controller/Entities/Audio/IHasAlbumArtist.cs2
-rw-r--r--MediaBrowser.Controller/Entities/Audio/IHasMusicGenres.cs2
-rw-r--r--MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs2
-rw-r--r--MediaBrowser.Controller/Entities/Audio/MusicArtist.cs2
-rw-r--r--MediaBrowser.Controller/Entities/Audio/MusicGenre.cs2
-rw-r--r--MediaBrowser.Controller/Entities/AudioBook.cs4
-rw-r--r--MediaBrowser.Controller/Entities/BaseItem.cs22
-rw-r--r--MediaBrowser.Controller/Entities/BaseItemExtensions.cs2
-rw-r--r--MediaBrowser.Controller/Entities/BasePluginFolder.cs10
-rw-r--r--MediaBrowser.Controller/Entities/Book.cs4
-rw-r--r--MediaBrowser.Controller/Entities/CollectionFolder.cs2
-rw-r--r--MediaBrowser.Controller/Entities/Folder.cs4
-rw-r--r--MediaBrowser.Controller/Entities/Genre.cs2
-rw-r--r--MediaBrowser.Controller/Entities/ICollectionFolder.cs2
-rw-r--r--MediaBrowser.Controller/Entities/IHasMediaSources.cs12
-rw-r--r--MediaBrowser.Controller/Entities/IHasProgramAttributes.cs2
-rw-r--r--MediaBrowser.Controller/Entities/IHasSeries.cs9
-rw-r--r--MediaBrowser.Controller/Entities/IHasSpecialFeatures.cs5
-rw-r--r--MediaBrowser.Controller/Entities/IHasStartDate.cs2
-rw-r--r--MediaBrowser.Controller/Entities/IHasTrailers.cs2
-rw-r--r--MediaBrowser.Controller/Entities/IItemByName.cs2
-rw-r--r--MediaBrowser.Controller/Entities/IMetadataContainer.cs2
-rw-r--r--MediaBrowser.Controller/Entities/ISupportsPlaceHolders.cs2
-rw-r--r--MediaBrowser.Controller/Entities/InternalItemsQuery.cs2
-rw-r--r--MediaBrowser.Controller/Entities/InternalPeopleQuery.cs2
-rw-r--r--MediaBrowser.Controller/Entities/ItemImageInfo.cs2
-rw-r--r--MediaBrowser.Controller/Entities/LinkedChild.cs2
-rw-r--r--MediaBrowser.Controller/Entities/Movies/BoxSet.cs2
-rw-r--r--MediaBrowser.Controller/Entities/Movies/Movie.cs19
-rw-r--r--MediaBrowser.Controller/Entities/MusicVideo.cs2
-rw-r--r--MediaBrowser.Controller/Entities/PeopleHelper.cs2
-rw-r--r--MediaBrowser.Controller/Entities/Person.cs2
-rw-r--r--MediaBrowser.Controller/Entities/PersonInfo.cs2
-rw-r--r--MediaBrowser.Controller/Entities/Photo.cs2
-rw-r--r--MediaBrowser.Controller/Entities/PhotoAlbum.cs2
-rw-r--r--MediaBrowser.Controller/Entities/Share.cs2
-rw-r--r--MediaBrowser.Controller/Entities/SourceType.cs2
-rw-r--r--MediaBrowser.Controller/Entities/Studio.cs2
-rw-r--r--MediaBrowser.Controller/Entities/TV/Episode.cs2
-rw-r--r--MediaBrowser.Controller/Entities/TV/Season.cs2
-rw-r--r--MediaBrowser.Controller/Entities/TV/Series.cs4
-rw-r--r--MediaBrowser.Controller/Entities/TagExtensions.cs2
-rw-r--r--MediaBrowser.Controller/Entities/Trailer.cs5
-rw-r--r--MediaBrowser.Controller/Entities/UserItemData.cs5
-rw-r--r--MediaBrowser.Controller/Entities/UserRootFolder.cs2
-rw-r--r--MediaBrowser.Controller/Entities/UserView.cs3
-rw-r--r--MediaBrowser.Controller/Entities/UserViewBuilder.cs15
-rw-r--r--MediaBrowser.Controller/Entities/Video.cs9
-rw-r--r--MediaBrowser.Controller/Entities/Year.cs2
-rw-r--r--MediaBrowser.Controller/Events/IEventConsumer.cs20
-rw-r--r--MediaBrowser.Controller/Events/IEventManager.cs28
-rw-r--r--MediaBrowser.Controller/Events/Session/SessionEndedEventArgs.cs19
-rw-r--r--MediaBrowser.Controller/Events/Session/SessionStartedEventArgs.cs19
-rw-r--r--MediaBrowser.Controller/Events/Updates/PluginInstallationCancelledEventArgs.cs19
-rw-r--r--MediaBrowser.Controller/Events/Updates/PluginInstalledEventArgs.cs19
-rw-r--r--MediaBrowser.Controller/Events/Updates/PluginInstallingEventArgs.cs19
-rw-r--r--MediaBrowser.Controller/Events/Updates/PluginUninstalledEventArgs.cs19
-rw-r--r--MediaBrowser.Controller/Events/Updates/PluginUpdatedEventArgs.cs19
-rw-r--r--MediaBrowser.Controller/Extensions/StringExtensions.cs2
-rw-r--r--MediaBrowser.Controller/IDisplayPreferencesManager.cs2
-rw-r--r--MediaBrowser.Controller/IO/FileData.cs15
-rw-r--r--MediaBrowser.Controller/IResourceFileManager.cs2
-rw-r--r--MediaBrowser.Controller/IServerApplicationHost.cs18
-rw-r--r--MediaBrowser.Controller/IServerApplicationPaths.cs6
-rw-r--r--MediaBrowser.Controller/Library/DeleteOptions.cs2
-rw-r--r--MediaBrowser.Controller/Library/ILibraryManager.cs44
-rw-r--r--MediaBrowser.Controller/Library/ILibraryMonitor.cs2
-rw-r--r--MediaBrowser.Controller/Library/ILiveStream.cs8
-rw-r--r--MediaBrowser.Controller/Library/IMediaSourceManager.cs2
-rw-r--r--MediaBrowser.Controller/Library/IMetadataFileSaver.cs2
-rw-r--r--MediaBrowser.Controller/Library/IMusicManager.cs2
-rw-r--r--MediaBrowser.Controller/Library/IUserDataManager.cs3
-rw-r--r--MediaBrowser.Controller/Library/IUserManager.cs24
-rw-r--r--MediaBrowser.Controller/Library/IUserViewManager.cs3
-rw-r--r--MediaBrowser.Controller/Library/IntroInfo.cs2
-rw-r--r--MediaBrowser.Controller/Library/ItemChangeEventArgs.cs2
-rw-r--r--MediaBrowser.Controller/Library/ItemResolveArgs.cs2
-rw-r--r--MediaBrowser.Controller/Library/ItemUpdateType.cs2
-rw-r--r--MediaBrowser.Controller/Library/LibraryManagerExtensions.cs2
-rw-r--r--MediaBrowser.Controller/Library/MetadataConfigurationStore.cs2
-rw-r--r--MediaBrowser.Controller/Library/NameExtensions.cs2
-rw-r--r--MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs12
-rw-r--r--MediaBrowser.Controller/Library/PlaybackStartEventArgs.cs9
-rw-r--r--MediaBrowser.Controller/Library/PlaybackStopEventArgs.cs2
-rw-r--r--MediaBrowser.Controller/Library/Profiler.cs17
-rw-r--r--MediaBrowser.Controller/Library/UserDataSaveEventArgs.cs2
-rw-r--r--MediaBrowser.Controller/LiveTv/ChannelInfo.cs2
-rw-r--r--MediaBrowser.Controller/LiveTv/IListingsProvider.cs2
-rw-r--r--MediaBrowser.Controller/LiveTv/ILiveTvManager.cs5
-rw-r--r--MediaBrowser.Controller/LiveTv/ILiveTvService.cs2
-rw-r--r--MediaBrowser.Controller/LiveTv/ITunerHost.cs16
-rw-r--r--MediaBrowser.Controller/LiveTv/LiveTvChannel.cs4
-rw-r--r--MediaBrowser.Controller/LiveTv/LiveTvConflictException.cs2
-rw-r--r--MediaBrowser.Controller/LiveTv/LiveTvProgram.cs4
-rw-r--r--MediaBrowser.Controller/LiveTv/LiveTvServiceStatusInfo.cs14
-rw-r--r--MediaBrowser.Controller/LiveTv/LiveTvTunerInfo.cs12
-rw-r--r--MediaBrowser.Controller/LiveTv/ProgramInfo.cs2
-rw-r--r--MediaBrowser.Controller/LiveTv/RecordingInfo.cs2
-rw-r--r--MediaBrowser.Controller/LiveTv/RecordingStatusChangedEventArgs.cs2
-rw-r--r--MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs2
-rw-r--r--MediaBrowser.Controller/LiveTv/TimerInfo.cs2
-rw-r--r--MediaBrowser.Controller/LiveTv/TunerChannelMapping.cs2
-rw-r--r--MediaBrowser.Controller/MediaBrowser.Controller.csproj4
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs10
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs2
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs2
-rw-r--r--MediaBrowser.Controller/MediaEncoding/IAttachmentExtractor.cs2
-rw-r--r--MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs2
-rw-r--r--MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs6
-rw-r--r--MediaBrowser.Controller/MediaEncoding/ImageEncodingOptions.cs2
-rw-r--r--MediaBrowser.Controller/MediaEncoding/JobLogger.cs2
-rw-r--r--MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs2
-rw-r--r--MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs2
-rw-r--r--MediaBrowser.Controller/Net/AuthenticatedAttribute.cs6
-rw-r--r--MediaBrowser.Controller/Net/AuthorizationInfo.cs2
-rw-r--r--MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs2
-rw-r--r--MediaBrowser.Controller/Net/IHttpResultFactory.cs4
-rw-r--r--MediaBrowser.Controller/Net/IHttpServer.cs2
-rw-r--r--MediaBrowser.Controller/Net/ISessionContext.cs4
-rw-r--r--MediaBrowser.Controller/Net/IWebSocketConnection.cs2
-rw-r--r--MediaBrowser.Controller/Net/SecurityException.cs2
-rw-r--r--MediaBrowser.Controller/Net/StaticResultOptions.cs2
-rw-r--r--MediaBrowser.Controller/Notifications/INotificationManager.cs2
-rw-r--r--MediaBrowser.Controller/Notifications/INotificationService.cs2
-rw-r--r--MediaBrowser.Controller/Notifications/INotificationTypeFactory.cs2
-rw-r--r--MediaBrowser.Controller/Notifications/UserNotification.cs2
-rw-r--r--MediaBrowser.Controller/Persistence/IItemRepository.cs10
-rw-r--r--MediaBrowser.Controller/Persistence/IUserDataRepository.cs8
-rw-r--r--MediaBrowser.Controller/Persistence/MediaAttachmentQuery.cs2
-rw-r--r--MediaBrowser.Controller/Persistence/MediaStreamQuery.cs2
-rw-r--r--MediaBrowser.Controller/Playlists/IPlaylistManager.cs8
-rw-r--r--MediaBrowser.Controller/Playlists/Playlist.cs2
-rw-r--r--MediaBrowser.Controller/Plugins/ILocalizablePlugin.cs2
-rw-r--r--MediaBrowser.Controller/Plugins/IPluginConfigurationPage.cs1
-rw-r--r--MediaBrowser.Controller/Providers/AlbumInfo.cs2
-rw-r--r--MediaBrowser.Controller/Providers/ArtistInfo.cs2
-rw-r--r--MediaBrowser.Controller/Providers/BookInfo.cs2
-rw-r--r--MediaBrowser.Controller/Providers/BoxSetInfo.cs2
-rw-r--r--MediaBrowser.Controller/Providers/DirectoryService.cs2
-rw-r--r--MediaBrowser.Controller/Providers/DynamicImageResponse.cs2
-rw-r--r--MediaBrowser.Controller/Providers/EpisodeInfo.cs2
-rw-r--r--MediaBrowser.Controller/Providers/ICustomMetadataProvider.cs2
-rw-r--r--MediaBrowser.Controller/Providers/IDirectoryService.cs2
-rw-r--r--MediaBrowser.Controller/Providers/IDynamicImageProvider.cs2
-rw-r--r--MediaBrowser.Controller/Providers/IHasItemChangeMonitor.cs2
-rw-r--r--MediaBrowser.Controller/Providers/IHasLookupInfo.cs2
-rw-r--r--MediaBrowser.Controller/Providers/IHasOrder.cs2
-rw-r--r--MediaBrowser.Controller/Providers/ILocalImageProvider.cs2
-rw-r--r--MediaBrowser.Controller/Providers/ILocalMetadataProvider.cs2
-rw-r--r--MediaBrowser.Controller/Providers/IMetadataProvider.cs2
-rw-r--r--MediaBrowser.Controller/Providers/IMetadataService.cs2
-rw-r--r--MediaBrowser.Controller/Providers/IPreRefreshProvider.cs2
-rw-r--r--MediaBrowser.Controller/Providers/IProviderManager.cs6
-rw-r--r--MediaBrowser.Controller/Providers/IRemoteMetadataProvider.cs2
-rw-r--r--MediaBrowser.Controller/Providers/IRemoteSearchProvider.cs2
-rw-r--r--MediaBrowser.Controller/Providers/ImageRefreshOptions.cs2
-rw-r--r--MediaBrowser.Controller/Providers/ItemInfo.cs2
-rw-r--r--MediaBrowser.Controller/Providers/ItemLookupInfo.cs2
-rw-r--r--MediaBrowser.Controller/Providers/LocalImageInfo.cs2
-rw-r--r--MediaBrowser.Controller/Providers/MetadataRefreshMode.cs2
-rw-r--r--MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs2
-rw-r--r--MediaBrowser.Controller/Providers/MetadataResult.cs2
-rw-r--r--MediaBrowser.Controller/Providers/MovieInfo.cs2
-rw-r--r--MediaBrowser.Controller/Providers/MusicVideoInfo.cs2
-rw-r--r--MediaBrowser.Controller/Providers/PersonLookupInfo.cs2
-rw-r--r--MediaBrowser.Controller/Providers/RemoteSearchQuery.cs2
-rw-r--r--MediaBrowser.Controller/Providers/SeasonInfo.cs2
-rw-r--r--MediaBrowser.Controller/Providers/SeriesInfo.cs2
-rw-r--r--MediaBrowser.Controller/Providers/SongInfo.cs2
-rw-r--r--MediaBrowser.Controller/Providers/TrailerInfo.cs2
-rw-r--r--MediaBrowser.Controller/Resolvers/IItemResolver.cs2
-rw-r--r--MediaBrowser.Controller/Resolvers/ResolverPriority.cs7
-rw-r--r--MediaBrowser.Controller/Security/AuthenticationInfo.cs2
-rw-r--r--MediaBrowser.Controller/Security/AuthenticationInfoQuery.cs2
-rw-r--r--MediaBrowser.Controller/Security/IAuthenticationRepository.cs3
-rw-r--r--MediaBrowser.Controller/Session/AuthenticationRequest.cs2
-rw-r--r--MediaBrowser.Controller/Session/ISessionController.cs2
-rw-r--r--MediaBrowser.Controller/Session/ISessionManager.cs4
-rw-r--r--MediaBrowser.Controller/Session/SessionEventArgs.cs2
-rw-r--r--MediaBrowser.Controller/Session/SessionInfo.cs2
-rw-r--r--MediaBrowser.Controller/Sorting/AlphanumComparator.cs4
-rw-r--r--MediaBrowser.Controller/Sorting/SortExtensions.cs3
-rw-r--r--MediaBrowser.Controller/Subtitles/ISubtitleManager.cs2
-rw-r--r--MediaBrowser.Controller/Subtitles/ISubtitleProvider.cs2
-rw-r--r--MediaBrowser.Controller/Subtitles/SubtitleDownloadEventArgs.cs27
-rw-r--r--MediaBrowser.Controller/Subtitles/SubtitleDownloadFailureEventArgs.cs26
-rw-r--r--MediaBrowser.Controller/Subtitles/SubtitleResponse.cs2
-rw-r--r--MediaBrowser.Controller/Subtitles/SubtitleSearchRequest.cs2
-rw-r--r--MediaBrowser.Controller/Sync/IHasDynamicAccess.cs2
-rw-r--r--MediaBrowser.Controller/Sync/IServerSyncProvider.cs2
-rw-r--r--MediaBrowser.Controller/Sync/ISyncProvider.cs2
-rw-r--r--MediaBrowser.Controller/Sync/SyncedFileInfo.cs15
-rw-r--r--MediaBrowser.Controller/SyncPlay/ISyncPlayController.cs2
-rw-r--r--MediaBrowser.Controller/TV/ITVSeriesManager.cs2
-rw-r--r--MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs4
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs101
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs14
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs27
-rw-r--r--MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj2
-rw-r--r--MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs1
-rw-r--r--MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs9
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/AssParser.cs8
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs95
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs19
-rw-r--r--MediaBrowser.Model/Activity/IActivityManager.cs4
-rw-r--r--MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs2
-rw-r--r--MediaBrowser.Model/Dlna/DlnaMaps.cs8
-rw-r--r--MediaBrowser.Model/Dlna/IDeviceDiscovery.cs2
-rw-r--r--MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs21
-rw-r--r--MediaBrowser.Model/Dlna/StreamInfo.cs12
-rw-r--r--MediaBrowser.Model/MediaBrowser.Model.csproj5
-rw-r--r--MediaBrowser.Model/Session/PlayRequest.cs3
-rw-r--r--MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs2
-rw-r--r--MediaBrowser.Model/Tasks/ITaskManager.cs2
-rw-r--r--MediaBrowser.Model/Users/UserPolicy.cs12
-rw-r--r--MediaBrowser.Providers/Manager/ImageSaver.cs4
-rw-r--r--MediaBrowser.Providers/Manager/ItemImageProvider.cs57
-rw-r--r--MediaBrowser.Providers/Manager/MetadataService.cs77
-rw-r--r--MediaBrowser.Providers/Manager/ProviderManager.cs39
-rw-r--r--MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs15
-rw-r--r--MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs21
-rw-r--r--MediaBrowser.Providers/Music/Extensions.cs4
-rw-r--r--MediaBrowser.Providers/Plugins/MusicBrainz/AlbumProvider.cs6
-rw-r--r--MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs4
-rw-r--r--MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs3
-rw-r--r--MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs2
-rw-r--r--MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs25
-rw-r--r--MediaBrowser.Providers/Plugins/TheTvdb/TvdbClientManager.cs31
-rw-r--r--MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeImageProvider.cs7
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs4
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Movies/GenericTmdbMovieInfo.cs2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs16
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbSearch.cs70
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProviderBase.cs4
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs4
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs2
-rw-r--r--MediaBrowser.Providers/Studios/StudiosImageProvider.cs3
-rw-r--r--MediaBrowser.Providers/TV/DummySeasonProvider.cs4
-rwxr-xr-xbump_version18
-rw-r--r--fedora/jellyfin.spec3
-rw-r--r--tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs7
-rw-r--r--tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs26
-rw-r--r--tests/Jellyfin.MediaEncoding.Tests/FFprobeParserTests.cs3
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Library/IgnorePatternsTests.cs3
-rw-r--r--tests/MediaBrowser.Api.Tests/JellyfinApplicationFactory.cs7
557 files changed, 7234 insertions, 9420 deletions
diff --git a/.ci/azure-pipelines-package.yml b/.ci/azure-pipelines-package.yml
index 003d5baf0..4631badae 100644
--- a/.ci/azure-pipelines-package.yml
+++ b/.ci/azure-pipelines-package.yml
@@ -147,19 +147,42 @@ jobs:
displayName: 'Publish NuGet packages'
dependsOn:
- BuildPackage
- condition: and(succeeded('BuildPackage'), startsWith(variables['Build.SourceBranch'], 'refs/tags'))
+ condition: succeeded('BuildPackage')
pool:
vmImage: 'ubuntu-latest'
steps:
- - task: NuGetCommand@2
+ - task: DotNetCoreCLI@2
+ displayName: 'Build Stable Nuget packages'
+ condition: startsWith(variables['Build.SourceBranch'], 'refs/tags')
inputs:
command: 'pack'
- packagesToPack: Jellyfin.Data/Jellyfin.Data.csproj;MediaBrowser.Common/MediaBrowser.Common.csproj;MediaBrowser.Controller/MediaBrowser.Controller.csproj;MediaBrowser.Model/MediaBrowser.Model.csproj;Emby.Naming/Emby.Naming.csproj
- packDestination: '$(Build.ArtifactStagingDirectory)'
+ packagesToPack: 'Jellyfin.Data/Jellyfin.Data.csproj;MediaBrowser.Common/MediaBrowser.Common.csproj;MediaBrowser.Controller/MediaBrowser.Controller.csproj;MediaBrowser.Model/MediaBrowser.Model.csproj;Emby.Naming/Emby.Naming.csproj'
+ versioningScheme: 'off'
+
+ - task: DotNetCoreCLI@2
+ displayName: 'Build Unstable Nuget packages'
+ inputs:
+ command: 'custom'
+ projects: |
+ Jellyfin.Data/Jellyfin.Data.csproj
+ MediaBrowser.Common/MediaBrowser.Common.csproj
+ MediaBrowser.Controller/MediaBrowser.Controller.csproj
+ MediaBrowser.Model/MediaBrowser.Model.csproj
+ Emby.Naming/Emby.Naming.csproj
+ custom: 'pack'
+ arguments: '--version-suffix $(Build.BuildNumber) -o $(Build.ArtifactStagingDirectory)'
+
+ - task: PublishBuildArtifacts@1
+ displayName: 'Publish Nuget packages'
+ inputs:
+ pathToPublish: $(Build.ArtifactStagingDirectory)
+ artifactName: Jellyfin Nuget Packages
- task: NuGetCommand@2
+ displayName: 'Push Nuget packages to feed'
+ condition: startsWith(variables['Build.SourceBranch'], 'refs/tags')
inputs:
command: 'push'
packagesToPush: '$(Build.ArtifactStagingDirectory)/**/*.nupkg'
diff --git a/Emby.Dlna/Common/ServiceAction.cs b/Emby.Dlna/Common/ServiceAction.cs
index db4f27063..d458d7f3f 100644
--- a/Emby.Dlna/Common/ServiceAction.cs
+++ b/Emby.Dlna/Common/ServiceAction.cs
@@ -13,7 +13,7 @@ namespace Emby.Dlna.Common
public string Name { get; set; }
- public List<Argument> ArgumentList { get; set; }
+ public List<Argument> ArgumentList { get; }
/// <inheritdoc />
public override string ToString()
diff --git a/Emby.Dlna/Common/StateVariable.cs b/Emby.Dlna/Common/StateVariable.cs
index a2c2bf5dd..6daf7ab6b 100644
--- a/Emby.Dlna/Common/StateVariable.cs
+++ b/Emby.Dlna/Common/StateVariable.cs
@@ -1,6 +1,7 @@
#pragma warning disable CS1591
using System;
+using System.Collections.Generic;
namespace Emby.Dlna.Common
{
@@ -17,7 +18,7 @@ namespace Emby.Dlna.Common
public bool SendsEvents { get; set; }
- public string[] AllowedValues { get; set; }
+ public IReadOnlyList<string> AllowedValues { get; set; }
/// <inheritdoc />
public override string ToString()
diff --git a/Emby.Dlna/ConfigurationExtension.cs b/Emby.Dlna/ConfigurationExtension.cs
index dba901967..fc02e1751 100644
--- a/Emby.Dlna/ConfigurationExtension.cs
+++ b/Emby.Dlna/ConfigurationExtension.cs
@@ -1,7 +1,6 @@
#nullable enable
#pragma warning disable CS1591
-using System.Collections.Generic;
using Emby.Dlna.Configuration;
using MediaBrowser.Common.Configuration;
@@ -14,19 +13,4 @@ namespace Emby.Dlna
return manager.GetConfiguration<DlnaOptions>("dlna");
}
}
-
- public class DlnaConfigurationFactory : IConfigurationFactory
- {
- public IEnumerable<ConfigurationStore> GetConfigurations()
- {
- return new ConfigurationStore[]
- {
- new ConfigurationStore
- {
- Key = "dlna",
- ConfigurationType = typeof (DlnaOptions)
- }
- };
- }
- }
}
diff --git a/Emby.Dlna/ConnectionManager/ConnectionManager.cs b/Emby.Dlna/ConnectionManager/ConnectionManagerService.cs
index e32cc11bf..12338e2b4 100644
--- a/Emby.Dlna/ConnectionManager/ConnectionManager.cs
+++ b/Emby.Dlna/ConnectionManager/ConnectionManagerService.cs
@@ -9,22 +9,20 @@ using Microsoft.Extensions.Logging;
namespace Emby.Dlna.ConnectionManager
{
- public class ConnectionManager : BaseService, IConnectionManager
+ public class ConnectionManagerService : BaseService, IConnectionManager
{
private readonly IDlnaManager _dlna;
- private readonly ILogger _logger;
private readonly IServerConfigurationManager _config;
- public ConnectionManager(
+ public ConnectionManagerService(
IDlnaManager dlna,
IServerConfigurationManager config,
- ILogger<ConnectionManager> logger,
+ ILogger<ConnectionManagerService> logger,
IHttpClient httpClient)
: base(logger, httpClient)
{
_dlna = dlna;
_config = config;
- _logger = logger;
}
/// <inheritdoc />
@@ -39,7 +37,7 @@ namespace Emby.Dlna.ConnectionManager
var profile = _dlna.GetProfile(request.Headers) ??
_dlna.GetDefaultProfile();
- return new ControlHandler(_config, _logger, profile).ProcessControlRequestAsync(request);
+ return new ControlHandler(_config, Logger, profile).ProcessControlRequestAsync(request);
}
}
}
diff --git a/Emby.Dlna/ConnectionManager/ConnectionManagerXmlBuilder.cs b/Emby.Dlna/ConnectionManager/ConnectionManagerXmlBuilder.cs
index b31d437c3..c8db5a367 100644
--- a/Emby.Dlna/ConnectionManager/ConnectionManagerXmlBuilder.cs
+++ b/Emby.Dlna/ConnectionManager/ConnectionManagerXmlBuilder.cs
@@ -44,7 +44,7 @@ namespace Emby.Dlna.ConnectionManager
DataType = "string",
SendsEvents = false,
- AllowedValues = new string[]
+ AllowedValues = new[]
{
"OK",
"ContentFormatMismatch",
@@ -67,7 +67,7 @@ namespace Emby.Dlna.ConnectionManager
DataType = "string",
SendsEvents = false,
- AllowedValues = new string[]
+ AllowedValues = new[]
{
"Output",
"Input"
diff --git a/Emby.Dlna/ContentDirectory/ContentDirectory.cs b/Emby.Dlna/ContentDirectory/ContentDirectoryService.cs
index b1ce7e8ec..72732823a 100644
--- a/Emby.Dlna/ContentDirectory/ContentDirectory.cs
+++ b/Emby.Dlna/ContentDirectory/ContentDirectoryService.cs
@@ -19,7 +19,7 @@ using Microsoft.Extensions.Logging;
namespace Emby.Dlna.ContentDirectory
{
- public class ContentDirectory : BaseService, IContentDirectory
+ public class ContentDirectoryService : BaseService, IContentDirectory
{
private readonly ILibraryManager _libraryManager;
private readonly IImageProcessor _imageProcessor;
@@ -33,14 +33,14 @@ namespace Emby.Dlna.ContentDirectory
private readonly IMediaEncoder _mediaEncoder;
private readonly ITVSeriesManager _tvSeriesManager;
- public ContentDirectory(
+ public ContentDirectoryService(
IDlnaManager dlna,
IUserDataManager userDataManager,
IImageProcessor imageProcessor,
ILibraryManager libraryManager,
IServerConfigurationManager config,
IUserManager userManager,
- ILogger<ContentDirectory> logger,
+ ILogger<ContentDirectoryService> logger,
IHttpClient httpClient,
ILocalizationManager localization,
IMediaSourceManager mediaSourceManager,
diff --git a/Emby.Dlna/ContentDirectory/ContentDirectoryXmlBuilder.cs b/Emby.Dlna/ContentDirectory/ContentDirectoryXmlBuilder.cs
index 6db4d7cb6..743dcc516 100644
--- a/Emby.Dlna/ContentDirectory/ContentDirectoryXmlBuilder.cs
+++ b/Emby.Dlna/ContentDirectory/ContentDirectoryXmlBuilder.cs
@@ -10,7 +10,8 @@ namespace Emby.Dlna.ContentDirectory
{
public string GetXml()
{
- return new ServiceXmlBuilder().GetXml(new ServiceActionListBuilder().GetActions(),
+ return new ServiceXmlBuilder().GetXml(
+ new ServiceActionListBuilder().GetActions(),
GetStateVariables());
}
@@ -101,7 +102,7 @@ namespace Emby.Dlna.ContentDirectory
DataType = "string",
SendsEvents = false,
- AllowedValues = new string[]
+ AllowedValues = new[]
{
"BrowseMetadata",
"BrowseDirectChildren"
diff --git a/Emby.Dlna/ContentDirectory/ControlHandler.cs b/Emby.Dlna/ContentDirectory/ControlHandler.cs
index 00821bf78..be1ed7872 100644
--- a/Emby.Dlna/ContentDirectory/ControlHandler.cs
+++ b/Emby.Dlna/ContentDirectory/ControlHandler.cs
@@ -40,6 +40,11 @@ namespace Emby.Dlna.ContentDirectory
{
public class ControlHandler : BaseControlHandler
{
+ private const string NsDc = "http://purl.org/dc/elements/1.1/";
+ private const string NsDidl = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/";
+ private const string NsDlna = "urn:schemas-dlna-org:metadata-1-0/";
+ private const string NsUpnp = "urn:schemas-upnp-org:metadata-1-0/upnp/";
+
private readonly ILibraryManager _libraryManager;
private readonly IUserDataManager _userDataManager;
private readonly IServerConfigurationManager _config;
@@ -47,11 +52,6 @@ namespace Emby.Dlna.ContentDirectory
private readonly IUserViewManager _userViewManager;
private readonly ITVSeriesManager _tvSeriesManager;
- private const string NS_DC = "http://purl.org/dc/elements/1.1/";
- private const string NS_DIDL = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/";
- private const string NS_DLNA = "urn:schemas-dlna-org:metadata-1-0/";
- private const string NS_UPNP = "urn:schemas-upnp-org:metadata-1-0/upnp/";
-
private readonly int _systemUpdateId;
private readonly DidlBuilder _didlBuilder;
@@ -181,7 +181,11 @@ namespace Emby.Dlna.ContentDirectory
userdata.PlaybackPositionTicks = TimeSpan.FromSeconds(newbookmark).Ticks;
- _userDataManager.SaveUserData(_user, item, userdata, UserDataSaveReason.TogglePlayed,
+ _userDataManager.SaveUserData(
+ _user,
+ item,
+ userdata,
+ UserDataSaveReason.TogglePlayed,
CancellationToken.None);
}
@@ -253,7 +257,7 @@ namespace Emby.Dlna.ContentDirectory
var id = sparams["ObjectID"];
var flag = sparams["BrowseFlag"];
var filter = new Filter(GetValueOrDefault(sparams, "Filter", "*"));
- var sortCriteria = new SortCriteria(GetValueOrDefault(sparams, "SortCriteria", ""));
+ var sortCriteria = new SortCriteria(GetValueOrDefault(sparams, "SortCriteria", string.Empty));
var provided = 0;
@@ -286,18 +290,17 @@ namespace Emby.Dlna.ContentDirectory
using (var writer = XmlWriter.Create(builder, settings))
{
- writer.WriteStartElement(string.Empty, "DIDL-Lite", NS_DIDL);
+ writer.WriteStartElement(string.Empty, "DIDL-Lite", NsDidl);
- writer.WriteAttributeString("xmlns", "dc", null, NS_DC);
- writer.WriteAttributeString("xmlns", "dlna", null, NS_DLNA);
- writer.WriteAttributeString("xmlns", "upnp", null, NS_UPNP);
+ writer.WriteAttributeString("xmlns", "dc", null, NsDc);
+ writer.WriteAttributeString("xmlns", "dlna", null, NsDlna);
+ writer.WriteAttributeString("xmlns", "upnp", null, NsUpnp);
DidlBuilder.WriteXmlRootAttributes(_profile, writer);
var serverItem = GetItemFromObjectId(id);
var item = serverItem.Item;
-
if (string.Equals(flag, "BrowseMetadata", StringComparison.Ordinal))
{
totalCount = 1;
@@ -362,8 +365,8 @@ namespace Emby.Dlna.ContentDirectory
private void HandleSearch(XmlWriter xmlWriter, IDictionary<string, string> sparams, string deviceId)
{
- var searchCriteria = new SearchCriteria(GetValueOrDefault(sparams, "SearchCriteria", ""));
- var sortCriteria = new SortCriteria(GetValueOrDefault(sparams, "SortCriteria", ""));
+ var searchCriteria = new SearchCriteria(GetValueOrDefault(sparams, "SearchCriteria", string.Empty));
+ var sortCriteria = new SortCriteria(GetValueOrDefault(sparams, "SortCriteria", string.Empty));
var filter = new Filter(GetValueOrDefault(sparams, "Filter", "*"));
// sort example: dc:title, dc:date
@@ -397,11 +400,11 @@ namespace Emby.Dlna.ContentDirectory
using (var writer = XmlWriter.Create(builder, settings))
{
- writer.WriteStartElement(string.Empty, "DIDL-Lite", NS_DIDL);
+ writer.WriteStartElement(string.Empty, "DIDL-Lite", NsDidl);
- writer.WriteAttributeString("xmlns", "dc", null, NS_DC);
- writer.WriteAttributeString("xmlns", "dlna", null, NS_DLNA);
- writer.WriteAttributeString("xmlns", "upnp", null, NS_UPNP);
+ writer.WriteAttributeString("xmlns", "dc", null, NsDc);
+ writer.WriteAttributeString("xmlns", "dlna", null, NsDlna);
+ writer.WriteAttributeString("xmlns", "upnp", null, NsUpnp);
DidlBuilder.WriteXmlRootAttributes(_profile, writer);
@@ -783,11 +786,14 @@ namespace Emby.Dlna.ContentDirectory
})
.ToArray();
- return ApplyPaging(new QueryResult<ServerItem>
- {
- Items = folders,
- TotalRecordCount = folders.Length
- }, startIndex, limit);
+ return ApplyPaging(
+ new QueryResult<ServerItem>
+ {
+ Items = folders,
+ TotalRecordCount = folders.Length
+ },
+ startIndex,
+ limit);
}
private QueryResult<ServerItem> GetTvFolders(BaseItem item, User user, StubType? stubType, SortCriteria sort, int? startIndex, int? limit)
@@ -1135,14 +1141,16 @@ namespace Emby.Dlna.ContentDirectory
{
query.OrderBy = Array.Empty<(string, SortOrder)>();
- var items = _userViewManager.GetLatestItems(new LatestItemsQuery
- {
- UserId = user.Id,
- Limit = 50,
- 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();
+ var items = _userViewManager.GetLatestItems(
+ new LatestItemsQuery
+ {
+ UserId = user.Id,
+ Limit = 50,
+ 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);
}
@@ -1151,12 +1159,15 @@ namespace Emby.Dlna.ContentDirectory
{
query.OrderBy = Array.Empty<(string, SortOrder)>();
- var result = _tvSeriesManager.GetNextUp(new NextUpQuery
- {
- Limit = query.Limit,
- StartIndex = query.StartIndex,
- UserId = query.User.Id
- }, new[] { parent }, query.DtoOptions);
+ var result = _tvSeriesManager.GetNextUp(
+ new NextUpQuery
+ {
+ Limit = query.Limit,
+ StartIndex = query.StartIndex,
+ UserId = query.User.Id
+ },
+ new[] { parent },
+ query.DtoOptions);
return ToResult(result);
}
@@ -1165,14 +1176,16 @@ namespace Emby.Dlna.ContentDirectory
{
query.OrderBy = Array.Empty<(string, SortOrder)>();
- var items = _userViewManager.GetLatestItems(new LatestItemsQuery
- {
- UserId = user.Id,
- Limit = 50,
- 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();
+ var items = _userViewManager.GetLatestItems(
+ new LatestItemsQuery
+ {
+ UserId = user.Id,
+ Limit = 50,
+ 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);
}
@@ -1183,13 +1196,14 @@ namespace Emby.Dlna.ContentDirectory
var items = _userViewManager.GetLatestItems(
new LatestItemsQuery
- {
- UserId = user.Id,
- Limit = 50,
- 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();
+ {
+ UserId = user.Id,
+ Limit = 50,
+ 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);
}
@@ -1354,44 +1368,4 @@ namespace Emby.Dlna.ContentDirectory
return new ServerItem(_libraryManager.GetUserRootFolder());
}
}
-
- internal class ServerItem
- {
- public BaseItem Item { get; set; }
-
- public StubType? StubType { get; set; }
-
- public ServerItem(BaseItem item)
- {
- Item = item;
-
- if (item is IItemByName && !(item is Folder))
- {
- StubType = Dlna.ContentDirectory.StubType.Folder;
- }
- }
- }
-
- public enum StubType
- {
- Folder = 0,
- Latest = 2,
- Playlists = 3,
- Albums = 4,
- AlbumArtists = 5,
- Artists = 6,
- Songs = 7,
- Genres = 8,
- FavoriteSongs = 9,
- FavoriteArtists = 10,
- FavoriteAlbums = 11,
- ContinueWatching = 12,
- Movies = 13,
- Collections = 14,
- Favorites = 15,
- NextUp = 16,
- Series = 17,
- FavoriteSeries = 18,
- FavoriteEpisodes = 19
- }
}
diff --git a/Emby.Dlna/ContentDirectory/ServerItem.cs b/Emby.Dlna/ContentDirectory/ServerItem.cs
new file mode 100644
index 000000000..e40605414
--- /dev/null
+++ b/Emby.Dlna/ContentDirectory/ServerItem.cs
@@ -0,0 +1,23 @@
+#pragma warning disable CS1591
+
+using MediaBrowser.Controller.Entities;
+
+namespace Emby.Dlna.ContentDirectory
+{
+ internal class ServerItem
+ {
+ public ServerItem(BaseItem item)
+ {
+ Item = item;
+
+ if (item is IItemByName && !(item is Folder))
+ {
+ StubType = Dlna.ContentDirectory.StubType.Folder;
+ }
+ }
+
+ public BaseItem Item { get; set; }
+
+ public StubType? StubType { get; set; }
+ }
+}
diff --git a/Emby.Dlna/ContentDirectory/StubType.cs b/Emby.Dlna/ContentDirectory/StubType.cs
new file mode 100644
index 000000000..eee405d3e
--- /dev/null
+++ b/Emby.Dlna/ContentDirectory/StubType.cs
@@ -0,0 +1,28 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1602
+
+namespace Emby.Dlna.ContentDirectory
+{
+ public enum StubType
+ {
+ Folder = 0,
+ Latest = 2,
+ Playlists = 3,
+ Albums = 4,
+ AlbumArtists = 5,
+ Artists = 6,
+ Songs = 7,
+ Genres = 8,
+ FavoriteSongs = 9,
+ FavoriteArtists = 10,
+ FavoriteAlbums = 11,
+ ContinueWatching = 12,
+ Movies = 13,
+ Collections = 14,
+ Favorites = 15,
+ NextUp = 16,
+ Series = 17,
+ FavoriteSeries = 18,
+ FavoriteEpisodes = 19
+ }
+}
diff --git a/Emby.Dlna/ControlRequest.cs b/Emby.Dlna/ControlRequest.cs
index a6e03b7e6..4ea4e4e48 100644
--- a/Emby.Dlna/ControlRequest.cs
+++ b/Emby.Dlna/ControlRequest.cs
@@ -7,17 +7,17 @@ namespace Emby.Dlna
{
public class ControlRequest
{
- public IHeaderDictionary Headers { get; set; }
+ public ControlRequest(IHeaderDictionary headers)
+ {
+ Headers = headers;
+ }
+
+ public IHeaderDictionary Headers { get; }
public Stream InputXml { get; set; }
public string TargetServerUuId { get; set; }
public string RequestedUrl { get; set; }
-
- public ControlRequest()
- {
- Headers = new HeaderDictionary();
- }
}
}
diff --git a/Emby.Dlna/ControlResponse.cs b/Emby.Dlna/ControlResponse.cs
index 243b09786..d827eef26 100644
--- a/Emby.Dlna/ControlResponse.cs
+++ b/Emby.Dlna/ControlResponse.cs
@@ -11,7 +11,7 @@ namespace Emby.Dlna
Headers = new Dictionary<string, string>();
}
- public IDictionary<string, string> Headers { get; set; }
+ public IDictionary<string, string> Headers { get; }
public string Xml { get; set; }
diff --git a/Emby.Dlna/Didl/DidlBuilder.cs b/Emby.Dlna/Didl/DidlBuilder.cs
index 70e358019..bd09a8051 100644
--- a/Emby.Dlna/Didl/DidlBuilder.cs
+++ b/Emby.Dlna/Didl/DidlBuilder.cs
@@ -34,12 +34,12 @@ namespace Emby.Dlna.Didl
{
public class DidlBuilder
{
- private readonly CultureInfo _usCulture = new CultureInfo("en-US");
+ private const string NsDidl = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/";
+ private const string NsDc = "http://purl.org/dc/elements/1.1/";
+ private const string NsUpnp = "urn:schemas-upnp-org:metadata-1-0/upnp/";
+ private const string NsDlna = "urn:schemas-dlna-org:metadata-1-0/";
- private const string NS_DIDL = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/";
- private const string NS_DC = "http://purl.org/dc/elements/1.1/";
- private const string NS_UPNP = "urn:schemas-upnp-org:metadata-1-0/upnp/";
- private const string NS_DLNA = "urn:schemas-dlna-org:metadata-1-0/";
+ private readonly CultureInfo _usCulture = new CultureInfo("en-US");
private readonly DeviceProfile _profile;
private readonly IImageProcessor _imageProcessor;
@@ -100,11 +100,11 @@ namespace Emby.Dlna.Didl
{
// writer.WriteStartDocument();
- writer.WriteStartElement(string.Empty, "DIDL-Lite", NS_DIDL);
+ writer.WriteStartElement(string.Empty, "DIDL-Lite", NsDidl);
- writer.WriteAttributeString("xmlns", "dc", null, NS_DC);
- writer.WriteAttributeString("xmlns", "dlna", null, NS_DLNA);
- writer.WriteAttributeString("xmlns", "upnp", null, NS_UPNP);
+ writer.WriteAttributeString("xmlns", "dc", null, NsDc);
+ writer.WriteAttributeString("xmlns", "dlna", null, NsDlna);
+ writer.WriteAttributeString("xmlns", "upnp", null, NsUpnp);
// didl.SetAttribute("xmlns:sec", NS_SEC);
WriteXmlRootAttributes(_profile, writer);
@@ -147,7 +147,7 @@ namespace Emby.Dlna.Didl
{
var clientId = GetClientId(item, null);
- writer.WriteStartElement(string.Empty, "item", NS_DIDL);
+ writer.WriteStartElement(string.Empty, "item", NsDidl);
writer.WriteAttributeString("restricted", "1");
writer.WriteAttributeString("id", clientId);
@@ -207,7 +207,8 @@ namespace Emby.Dlna.Didl
var targetWidth = streamInfo.TargetWidth;
var targetHeight = streamInfo.TargetHeight;
- var contentFeatureList = new ContentFeatureBuilder(_profile).BuildVideoHeader(streamInfo.Container,
+ var contentFeatureList = new ContentFeatureBuilder(_profile).BuildVideoHeader(
+ streamInfo.Container,
streamInfo.TargetVideoCodec.FirstOrDefault(),
streamInfo.TargetAudioCodec.FirstOrDefault(),
targetWidth,
@@ -279,7 +280,7 @@ namespace Emby.Dlna.Didl
}
else if (string.Equals(subtitleMode, "smi", StringComparison.OrdinalIgnoreCase))
{
- writer.WriteStartElement(string.Empty, "res", NS_DIDL);
+ writer.WriteStartElement(string.Empty, "res", NsDidl);
writer.WriteAttributeString("protocolInfo", "http-get:*:smi/caption:*");
@@ -288,7 +289,7 @@ namespace Emby.Dlna.Didl
}
else
{
- writer.WriteStartElement(string.Empty, "res", NS_DIDL);
+ writer.WriteStartElement(string.Empty, "res", NsDidl);
var protocolInfo = string.Format(
CultureInfo.InvariantCulture,
"http-get:*:text/{0}:*",
@@ -304,7 +305,7 @@ namespace Emby.Dlna.Didl
private void AddVideoResource(XmlWriter writer, Filter filter, string contentFeatures, StreamInfo streamInfo)
{
- writer.WriteStartElement(string.Empty, "res", NS_DIDL);
+ writer.WriteStartElement(string.Empty, "res", NsDidl);
var url = NormalizeDlnaMediaUrl(streamInfo.ToUrl(_serverAddress, _accessToken));
@@ -526,7 +527,7 @@ namespace Emby.Dlna.Didl
private void AddAudioResource(XmlWriter writer, BaseItem audio, string deviceId, Filter filter, StreamInfo streamInfo = null)
{
- writer.WriteStartElement(string.Empty, "res", NS_DIDL);
+ writer.WriteStartElement(string.Empty, "res", NsDidl);
if (streamInfo == null)
{
@@ -583,7 +584,8 @@ namespace Emby.Dlna.Didl
writer.WriteAttributeString("bitrate", targetAudioBitrate.Value.ToString(_usCulture));
}
- var mediaProfile = _profile.GetAudioMediaProfile(streamInfo.Container,
+ var mediaProfile = _profile.GetAudioMediaProfile(
+ streamInfo.Container,
streamInfo.TargetAudioCodec.FirstOrDefault(),
targetChannels,
targetAudioBitrate,
@@ -596,7 +598,8 @@ namespace Emby.Dlna.Didl
? MimeTypes.GetMimeType(filename)
: mediaProfile.MimeType;
- var contentFeatures = new ContentFeatureBuilder(_profile).BuildAudioHeader(streamInfo.Container,
+ var contentFeatures = new ContentFeatureBuilder(_profile).BuildAudioHeader(
+ streamInfo.Container,
streamInfo.TargetAudioCodec.FirstOrDefault(),
targetAudioBitrate,
targetSampleRate,
@@ -627,7 +630,7 @@ namespace Emby.Dlna.Didl
public void WriteFolderElement(XmlWriter writer, BaseItem folder, StubType? stubType, BaseItem context, int childCount, Filter filter, string requestedId = null)
{
- writer.WriteStartElement(string.Empty, "container", NS_DIDL);
+ writer.WriteStartElement(string.Empty, "container", NsDidl);
writer.WriteAttributeString("restricted", "1");
writer.WriteAttributeString("searchable", "1");
@@ -714,7 +717,7 @@ namespace Emby.Dlna.Didl
// MediaMonkey for example won't display content without a title
// if (filter.Contains("dc:title"))
{
- AddValue(writer, "dc", "title", GetDisplayName(item, itemStubType, context), NS_DC);
+ AddValue(writer, "dc", "title", GetDisplayName(item, itemStubType, context), NsDc);
}
WriteObjectClass(writer, item, itemStubType);
@@ -723,7 +726,7 @@ namespace Emby.Dlna.Didl
{
if (item.PremiereDate.HasValue)
{
- AddValue(writer, "dc", "date", item.PremiereDate.Value.ToString("o", CultureInfo.InvariantCulture), NS_DC);
+ AddValue(writer, "dc", "date", item.PremiereDate.Value.ToString("o", CultureInfo.InvariantCulture), NsDc);
}
}
@@ -731,13 +734,13 @@ namespace Emby.Dlna.Didl
{
foreach (var genre in item.Genres)
{
- AddValue(writer, "upnp", "genre", genre, NS_UPNP);
+ AddValue(writer, "upnp", "genre", genre, NsUpnp);
}
}
foreach (var studio in item.Studios)
{
- AddValue(writer, "upnp", "publisher", studio, NS_UPNP);
+ AddValue(writer, "upnp", "publisher", studio, NsUpnp);
}
if (!(item is Folder))
@@ -748,28 +751,29 @@ namespace Emby.Dlna.Didl
if (!string.IsNullOrWhiteSpace(desc))
{
- AddValue(writer, "dc", "description", desc, NS_DC);
+ AddValue(writer, "dc", "description", desc, NsDc);
}
}
+
// if (filter.Contains("upnp:longDescription"))
- //{
+ // {
// if (!string.IsNullOrWhiteSpace(item.Overview))
// {
- // AddValue(writer, "upnp", "longDescription", item.Overview, NS_UPNP);
+ // AddValue(writer, "upnp", "longDescription", item.Overview, NsUpnp);
// }
- //}
+ // }
}
if (!string.IsNullOrEmpty(item.OfficialRating))
{
if (filter.Contains("dc:rating"))
{
- AddValue(writer, "dc", "rating", item.OfficialRating, NS_DC);
+ AddValue(writer, "dc", "rating", item.OfficialRating, NsDc);
}
if (filter.Contains("upnp:rating"))
{
- AddValue(writer, "upnp", "rating", item.OfficialRating, NS_UPNP);
+ AddValue(writer, "upnp", "rating", item.OfficialRating, NsUpnp);
}
}
@@ -781,7 +785,7 @@ namespace Emby.Dlna.Didl
// More types here
// http://oss.linn.co.uk/repos/Public/LibUpnpCil/DidlLite/UpnpAv/Test/TestDidlLite.cs
- writer.WriteStartElement("upnp", "class", NS_UPNP);
+ writer.WriteStartElement("upnp", "class", NsUpnp);
if (item.IsDisplayedAsFolder || stubType.HasValue)
{
@@ -882,7 +886,7 @@ namespace Emby.Dlna.Didl
var type = types.FirstOrDefault(i => string.Equals(i, actor.Type, StringComparison.OrdinalIgnoreCase) || string.Equals(i, actor.Role, StringComparison.OrdinalIgnoreCase))
?? PersonType.Actor;
- AddValue(writer, "upnp", type.ToLowerInvariant(), actor.Name, NS_UPNP);
+ AddValue(writer, "upnp", type.ToLowerInvariant(), actor.Name, NsUpnp);
}
}
@@ -896,8 +900,8 @@ namespace Emby.Dlna.Didl
{
foreach (var artist in hasArtists.Artists)
{
- AddValue(writer, "upnp", "artist", artist, NS_UPNP);
- AddValue(writer, "dc", "creator", artist, NS_DC);
+ AddValue(writer, "upnp", "artist", artist, NsUpnp);
+ AddValue(writer, "dc", "creator", artist, NsDc);
// If it doesn't support album artists (musicvideo), then tag as both
if (hasAlbumArtists == null)
@@ -917,16 +921,16 @@ namespace Emby.Dlna.Didl
if (!string.IsNullOrWhiteSpace(item.Album))
{
- AddValue(writer, "upnp", "album", item.Album, NS_UPNP);
+ AddValue(writer, "upnp", "album", item.Album, NsUpnp);
}
if (item.IndexNumber.HasValue)
{
- AddValue(writer, "upnp", "originalTrackNumber", item.IndexNumber.Value.ToString(_usCulture), NS_UPNP);
+ AddValue(writer, "upnp", "originalTrackNumber", item.IndexNumber.Value.ToString(_usCulture), NsUpnp);
if (item is Episode)
{
- AddValue(writer, "upnp", "episodeNumber", item.IndexNumber.Value.ToString(_usCulture), NS_UPNP);
+ AddValue(writer, "upnp", "episodeNumber", item.IndexNumber.Value.ToString(_usCulture), NsUpnp);
}
}
}
@@ -935,7 +939,7 @@ namespace Emby.Dlna.Didl
{
try
{
- writer.WriteStartElement("upnp", "artist", NS_UPNP);
+ writer.WriteStartElement("upnp", "artist", NsUpnp);
writer.WriteAttributeString("role", "AlbumArtist");
writer.WriteString(name);
@@ -971,14 +975,14 @@ namespace Emby.Dlna.Didl
var albumartUrlInfo = GetImageUrl(imageInfo, _profile.MaxAlbumArtWidth, _profile.MaxAlbumArtHeight, "jpg");
- writer.WriteStartElement("upnp", "albumArtURI", NS_UPNP);
- writer.WriteAttributeString("dlna", "profileID", NS_DLNA, _profile.AlbumArtPn);
- writer.WriteString(albumartUrlInfo.Url);
+ writer.WriteStartElement("upnp", "albumArtURI", NsUpnp);
+ writer.WriteAttributeString("dlna", "profileID", NsDlna, _profile.AlbumArtPn);
+ writer.WriteString(albumartUrlInfo.url);
writer.WriteFullEndElement();
// TOOD: Remove these default values
var iconUrlInfo = GetImageUrl(imageInfo, _profile.MaxIconWidth ?? 48, _profile.MaxIconHeight ?? 48, "jpg");
- writer.WriteElementString("upnp", "icon", NS_UPNP, iconUrlInfo.Url);
+ writer.WriteElementString("upnp", "icon", NsUpnp, iconUrlInfo.url);
if (!_profile.EnableAlbumArtInDidl)
{
@@ -1021,12 +1025,12 @@ namespace Emby.Dlna.Didl
var albumartUrlInfo = GetImageUrl(imageInfo, maxWidth, maxHeight, format);
- writer.WriteStartElement(string.Empty, "res", NS_DIDL);
+ writer.WriteStartElement(string.Empty, "res", NsDidl);
// Images must have a reported size or many clients (Bubble upnp), will only use the first thumbnail
// rather than using a larger one when available
- var width = albumartUrlInfo.Width ?? maxWidth;
- var height = albumartUrlInfo.Height ?? maxHeight;
+ var width = albumartUrlInfo.width ?? maxWidth;
+ var height = albumartUrlInfo.height ?? maxHeight;
var contentFeatures = new ContentFeatureBuilder(_profile)
.BuildImageHeader(format, width, height, imageInfo.IsDirectStream, org_Pn);
@@ -1043,7 +1047,7 @@ namespace Emby.Dlna.Didl
"resolution",
string.Format(CultureInfo.InvariantCulture, "{0}x{1}", width, height));
- writer.WriteString(albumartUrlInfo.Url);
+ writer.WriteString(albumartUrlInfo.url);
writer.WriteFullEndElement();
}
@@ -1139,7 +1143,6 @@ namespace Emby.Dlna.Didl
if (width == 0 || height == 0)
{
- // _imageProcessor.GetImageSize(item, imageInfo);
width = null;
height = null;
}
@@ -1149,18 +1152,6 @@ namespace Emby.Dlna.Didl
height = null;
}
- // try
- //{
- // var size = _imageProcessor.GetImageSize(imageInfo);
-
- // width = size.Width;
- // height = size.Height;
- //}
- // catch
- //{
-
- //}
-
var inputFormat = (Path.GetExtension(imageInfo.Path) ?? string.Empty)
.TrimStart('.')
.Replace("jpeg", "jpg", StringComparison.OrdinalIgnoreCase);
@@ -1177,30 +1168,6 @@ namespace Emby.Dlna.Didl
};
}
- private class ImageDownloadInfo
- {
- internal Guid ItemId;
- internal string ImageTag;
- internal ImageType Type;
-
- internal int? Width;
- internal int? Height;
-
- internal bool IsDirectStream;
-
- internal string Format;
-
- internal ItemImageInfo ItemImageInfo;
- }
-
- private class ImageUrlInfo
- {
- internal string Url;
-
- internal int? Width;
- internal int? Height;
- }
-
public static string GetClientId(BaseItem item, StubType? stubType)
{
return GetClientId(item.Id, stubType);
@@ -1218,7 +1185,7 @@ namespace Emby.Dlna.Didl
return id;
}
- private ImageUrlInfo GetImageUrl(ImageDownloadInfo info, int maxWidth, int maxHeight, string format)
+ private (string url, int? width, int? height) GetImageUrl(ImageDownloadInfo info, int maxWidth, int maxHeight, string format)
{
var url = string.Format(
CultureInfo.InvariantCulture,
@@ -1256,12 +1223,26 @@ namespace Emby.Dlna.Didl
// just lie
info.IsDirectStream = true;
- return new ImageUrlInfo
- {
- Url = url,
- Width = width,
- Height = height
- };
+ return (url, width, height);
+ }
+
+ private class ImageDownloadInfo
+ {
+ internal Guid ItemId { get; set; }
+
+ internal string ImageTag { get; set; }
+
+ internal ImageType Type { get; set; }
+
+ internal int? Width { get; set; }
+
+ internal int? Height { get; set; }
+
+ internal bool IsDirectStream { get; set; }
+
+ internal string Format { get; set; }
+
+ internal ItemImageInfo ItemImageInfo { get; set; }
}
}
}
diff --git a/Emby.Dlna/Didl/Filter.cs b/Emby.Dlna/Didl/Filter.cs
index b730d9db2..b58fdff2c 100644
--- a/Emby.Dlna/Didl/Filter.cs
+++ b/Emby.Dlna/Didl/Filter.cs
@@ -23,9 +23,7 @@ namespace Emby.Dlna.Didl
public bool Contains(string field)
{
- // Don't bother with this. Some clients (media monkey) use the filter and then don't display very well when very little data comes back.
- return true;
- // return _all || ListHelper.ContainsIgnoreCase(_fields, field);
+ return _all || Array.Exists(_fields, x => x.Equals(field, StringComparison.OrdinalIgnoreCase));
}
}
}
diff --git a/Emby.Dlna/Didl/StringWriterWithEncoding.cs b/Emby.Dlna/Didl/StringWriterWithEncoding.cs
index 896fe992b..2b86ea333 100644
--- a/Emby.Dlna/Didl/StringWriterWithEncoding.cs
+++ b/Emby.Dlna/Didl/StringWriterWithEncoding.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable CA1305
using System;
using System.IO;
@@ -29,7 +30,6 @@ namespace Emby.Dlna.Didl
{
}
-
public StringWriterWithEncoding(Encoding encoding)
{
_encoding = encoding;
diff --git a/Emby.Dlna/DlnaConfigurationFactory.cs b/Emby.Dlna/DlnaConfigurationFactory.cs
new file mode 100644
index 000000000..4c6ca869a
--- /dev/null
+++ b/Emby.Dlna/DlnaConfigurationFactory.cs
@@ -0,0 +1,24 @@
+#nullable enable
+#pragma warning disable CS1591
+
+using System.Collections.Generic;
+using Emby.Dlna.Configuration;
+using MediaBrowser.Common.Configuration;
+
+namespace Emby.Dlna
+{
+ public class DlnaConfigurationFactory : IConfigurationFactory
+ {
+ public IEnumerable<ConfigurationStore> GetConfigurations()
+ {
+ return new[]
+ {
+ new ConfigurationStore
+ {
+ Key = "dlna",
+ ConfigurationType = typeof(DlnaOptions)
+ }
+ };
+ }
+ }
+}
diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs
index 269f7ee43..d5629684c 100644
--- a/Emby.Dlna/DlnaManager.cs
+++ b/Emby.Dlna/DlnaManager.cs
@@ -54,11 +54,15 @@ namespace Emby.Dlna
_appHost = appHost;
}
+ private string UserProfilesPath => Path.Combine(_appPaths.ConfigurationDirectoryPath, "dlna", "user");
+
+ private string SystemProfilesPath => Path.Combine(_appPaths.ConfigurationDirectoryPath, "dlna", "system");
+
public async Task InitProfilesAsync()
{
try
{
- await ExtractSystemProfilesAsync();
+ await ExtractSystemProfilesAsync().ConfigureAwait(false);
LoadProfiles();
}
catch (Exception ex)
@@ -240,7 +244,7 @@ namespace Emby.Dlna
}
else
{
- var headerString = string.Join(", ", headers.Select(i => string.Format("{0}={1}", i.Key, i.Value)).ToArray());
+ var headerString = string.Join(", ", headers.Select(i => string.Format(CultureInfo.InvariantCulture, "{0}={1}", i.Key, i.Value)));
_logger.LogDebug("No matching device profile found. {0}", headerString);
}
@@ -280,10 +284,6 @@ namespace Emby.Dlna
return false;
}
- private string UserProfilesPath => Path.Combine(_appPaths.ConfigurationDirectoryPath, "dlna", "user");
-
- private string SystemProfilesPath => Path.Combine(_appPaths.ConfigurationDirectoryPath, "dlna", "system");
-
private IEnumerable<DeviceProfile> GetProfiles(string path, DeviceProfileType type)
{
try
@@ -495,8 +495,8 @@ namespace Emby.Dlna
/// Recreates the object using serialization, to ensure it's not a subclass.
/// If it's a subclass it may not serlialize properly to xml (different root element tag name).
/// </summary>
- /// <param name="profile"></param>
- /// <returns></returns>
+ /// <param name="profile">The device profile.</param>
+ /// <returns>The reserialized device profile.</returns>
private DeviceProfile ReserializeProfile(DeviceProfile profile)
{
if (profile.GetType() == typeof(DeviceProfile))
@@ -509,13 +509,6 @@ namespace Emby.Dlna
return _jsonSerializer.DeserializeFromString<DeviceProfile>(json);
}
- private class InternalProfileInfo
- {
- internal DeviceProfileInfo Info { get; set; }
-
- internal string Path { get; set; }
- }
-
public string GetServerDescriptionXml(IHeaderDictionary headers, string serverUuId, string serverAddress)
{
var profile = GetProfile(headers) ??
@@ -540,7 +533,15 @@ namespace Emby.Dlna
Stream = _assembly.GetManifestResourceStream(resource)
};
}
+
+ private class InternalProfileInfo
+ {
+ internal DeviceProfileInfo Info { get; set; }
+
+ internal string Path { get; set; }
+ }
}
+
/*
class DlnaProfileEntryPoint : IServerEntryPoint
{
diff --git a/Emby.Dlna/Emby.Dlna.csproj b/Emby.Dlna/Emby.Dlna.csproj
index 42a5f95c1..8538f580c 100644
--- a/Emby.Dlna/Emby.Dlna.csproj
+++ b/Emby.Dlna/Emby.Dlna.csproj
@@ -20,7 +20,7 @@
<TargetFramework>netstandard2.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
- <TreatWarningsAsErrors Condition=" '$(Configuration)' == 'Release'" >true</TreatWarningsAsErrors>
+ <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<!-- Code Analyzers-->
diff --git a/Emby.Dlna/EventSubscriptionResponse.cs b/Emby.Dlna/EventSubscriptionResponse.cs
index fd18343e6..1b1bd426c 100644
--- a/Emby.Dlna/EventSubscriptionResponse.cs
+++ b/Emby.Dlna/EventSubscriptionResponse.cs
@@ -15,6 +15,6 @@ namespace Emby.Dlna
public string ContentType { get; set; }
- public Dictionary<string, string> Headers { get; set; }
+ public Dictionary<string, string> Headers { get; }
}
}
diff --git a/Emby.Dlna/Eventing/EventManager.cs b/Emby.Dlna/Eventing/DlnaEventManager.cs
index 7d02f5e96..b66e966df 100644
--- a/Emby.Dlna/Eventing/EventManager.cs
+++ b/Emby.Dlna/Eventing/DlnaEventManager.cs
@@ -14,7 +14,7 @@ using Microsoft.Extensions.Logging;
namespace Emby.Dlna.Eventing
{
- public class EventManager : IEventManager
+ public class DlnaEventManager : IDlnaEventManager
{
private readonly ConcurrentDictionary<string, EventSubscription> _subscriptions =
new ConcurrentDictionary<string, EventSubscription>(StringComparer.OrdinalIgnoreCase);
@@ -22,7 +22,9 @@ namespace Emby.Dlna.Eventing
private readonly ILogger _logger;
private readonly IHttpClient _httpClient;
- public EventManager(ILogger logger, IHttpClient httpClient)
+ private readonly CultureInfo _usCulture = new CultureInfo("en-US");
+
+ public DlnaEventManager(ILogger logger, IHttpClient httpClient)
{
_httpClient = httpClient;
_logger = logger;
@@ -58,7 +60,8 @@ namespace Emby.Dlna.Eventing
var timeout = ParseTimeout(requestedTimeoutString) ?? 300;
var id = "uuid:" + Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
- _logger.LogDebug("Creating event subscription for {0} with timeout of {1} to {2}",
+ _logger.LogDebug(
+ "Creating event subscription for {0} with timeout of {1} to {2}",
notificationType,
timeout,
callbackUrl);
@@ -94,7 +97,7 @@ namespace Emby.Dlna.Eventing
{
_logger.LogDebug("Cancelling event subscription {0}", subscriptionId);
- _subscriptions.TryRemove(subscriptionId, out EventSubscription sub);
+ _subscriptions.TryRemove(subscriptionId, out _);
return new EventSubscriptionResponse
{
@@ -103,7 +106,6 @@ namespace Emby.Dlna.Eventing
};
}
- private readonly CultureInfo _usCulture = new CultureInfo("en-US");
private EventSubscriptionResponse GetEventSubscriptionResponse(string subscriptionId, string requestedTimeoutString, int timeoutSeconds)
{
var response = new EventSubscriptionResponse
diff --git a/Emby.Dlna/IConnectionManager.cs b/Emby.Dlna/IConnectionManager.cs
index 7b4a33a98..9f643a9e6 100644
--- a/Emby.Dlna/IConnectionManager.cs
+++ b/Emby.Dlna/IConnectionManager.cs
@@ -2,7 +2,7 @@
namespace Emby.Dlna
{
- public interface IConnectionManager : IEventManager, IUpnpService
+ public interface IConnectionManager : IDlnaEventManager, IUpnpService
{
}
}
diff --git a/Emby.Dlna/IContentDirectory.cs b/Emby.Dlna/IContentDirectory.cs
index 83ef09c66..10f4d6386 100644
--- a/Emby.Dlna/IContentDirectory.cs
+++ b/Emby.Dlna/IContentDirectory.cs
@@ -2,7 +2,7 @@
namespace Emby.Dlna
{
- public interface IContentDirectory : IEventManager, IUpnpService
+ public interface IContentDirectory : IDlnaEventManager, IUpnpService
{
}
}
diff --git a/Emby.Dlna/IEventManager.cs b/Emby.Dlna/IDlnaEventManager.cs
index 287203389..33cf0896b 100644
--- a/Emby.Dlna/IEventManager.cs
+++ b/Emby.Dlna/IDlnaEventManager.cs
@@ -2,22 +2,32 @@
namespace Emby.Dlna
{
- public interface IEventManager
+ public interface IDlnaEventManager
{
/// <summary>
/// Cancels the event subscription.
/// </summary>
/// <param name="subscriptionId">The subscription identifier.</param>
+ /// <returns>The response.</returns>
EventSubscriptionResponse CancelEventSubscription(string subscriptionId);
/// <summary>
/// Renews the event subscription.
/// </summary>
+ /// <param name="subscriptionId">The subscription identifier.</param>
+ /// <param name="notificationType">The notification type.</param>
+ /// <param name="requestedTimeoutString">The requested timeout as a sting.</param>
+ /// <param name="callbackUrl">The callback url.</param>
+ /// <returns>The response.</returns>
EventSubscriptionResponse RenewEventSubscription(string subscriptionId, string notificationType, string requestedTimeoutString, string callbackUrl);
/// <summary>
/// Creates the event subscription.
/// </summary>
+ /// <param name="notificationType">The notification type.</param>
+ /// <param name="requestedTimeoutString">The requested timeout as a sting.</param>
+ /// <param name="callbackUrl">The callback url.</param>
+ /// <returns>The response.</returns>
EventSubscriptionResponse CreateEventSubscription(string notificationType, string requestedTimeoutString, string callbackUrl);
}
}
diff --git a/Emby.Dlna/IMediaReceiverRegistrar.cs b/Emby.Dlna/IMediaReceiverRegistrar.cs
index b0376b6a9..43e934b53 100644
--- a/Emby.Dlna/IMediaReceiverRegistrar.cs
+++ b/Emby.Dlna/IMediaReceiverRegistrar.cs
@@ -2,7 +2,7 @@
namespace Emby.Dlna
{
- public interface IMediaReceiverRegistrar : IEventManager, IUpnpService
+ public interface IMediaReceiverRegistrar : IDlnaEventManager, IUpnpService
{
}
}
diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs
index a21d4cc11..0ad82022d 100644
--- a/Emby.Dlna/Main/DlnaEntryPoint.cs
+++ b/Emby.Dlna/Main/DlnaEntryPoint.cs
@@ -30,7 +30,7 @@ using OperatingSystem = MediaBrowser.Common.System.OperatingSystem;
namespace Emby.Dlna.Main
{
- public class DlnaEntryPoint : IServerEntryPoint, IRunBeforeStartup
+ public sealed class DlnaEntryPoint : IServerEntryPoint, IRunBeforeStartup
{
private readonly IServerConfigurationManager _config;
private readonly ILogger<DlnaEntryPoint> _logger;
@@ -54,13 +54,7 @@ namespace Emby.Dlna.Main
private SsdpDevicePublisher _publisher;
private ISsdpCommunicationsServer _communicationsServer;
- public IContentDirectory ContentDirectory { get; private set; }
-
- public IConnectionManager ConnectionManager { get; private set; }
-
- public IMediaReceiverRegistrar MediaReceiverRegistrar { get; private set; }
-
- public static DlnaEntryPoint Current;
+ private bool _disposed;
public DlnaEntryPoint(
IServerConfigurationManager config,
@@ -99,14 +93,14 @@ namespace Emby.Dlna.Main
_networkManager = networkManager;
_logger = loggerFactory.CreateLogger<DlnaEntryPoint>();
- ContentDirectory = new ContentDirectory.ContentDirectory(
+ ContentDirectory = new ContentDirectory.ContentDirectoryService(
dlnaManager,
userDataManager,
imageProcessor,
libraryManager,
config,
userManager,
- loggerFactory.CreateLogger<ContentDirectory.ContentDirectory>(),
+ loggerFactory.CreateLogger<ContentDirectory.ContentDirectoryService>(),
httpClient,
localizationManager,
mediaSourceManager,
@@ -114,19 +108,27 @@ namespace Emby.Dlna.Main
mediaEncoder,
tvSeriesManager);
- ConnectionManager = new ConnectionManager.ConnectionManager(
+ ConnectionManager = new ConnectionManager.ConnectionManagerService(
dlnaManager,
config,
- loggerFactory.CreateLogger<ConnectionManager.ConnectionManager>(),
+ loggerFactory.CreateLogger<ConnectionManager.ConnectionManagerService>(),
httpClient);
- MediaReceiverRegistrar = new MediaReceiverRegistrar.MediaReceiverRegistrar(
- loggerFactory.CreateLogger<MediaReceiverRegistrar.MediaReceiverRegistrar>(),
+ MediaReceiverRegistrar = new MediaReceiverRegistrar.MediaReceiverRegistrarService(
+ loggerFactory.CreateLogger<MediaReceiverRegistrar.MediaReceiverRegistrarService>(),
httpClient,
config);
Current = this;
}
+ public static DlnaEntryPoint Current { get; private set; }
+
+ public IContentDirectory ContentDirectory { get; private set; }
+
+ public IConnectionManager ConnectionManager { get; private set; }
+
+ public IMediaReceiverRegistrar MediaReceiverRegistrar { get; private set; }
+
public async Task RunAsync()
{
await ((DlnaManager)_dlnaManager).InitProfilesAsync().ConfigureAwait(false);
@@ -399,8 +401,24 @@ namespace Emby.Dlna.Main
}
}
+ public void DisposeDevicePublisher()
+ {
+ if (_publisher != null)
+ {
+ _logger.LogInformation("Disposing SsdpDevicePublisher");
+ _publisher.Dispose();
+ _publisher = null;
+ }
+ }
+
+ /// <inheritdoc />
public void Dispose()
{
+ if (_disposed)
+ {
+ return;
+ }
+
DisposeDevicePublisher();
DisposePlayToManager();
DisposeDeviceDiscovery();
@@ -416,16 +434,8 @@ namespace Emby.Dlna.Main
ConnectionManager = null;
MediaReceiverRegistrar = null;
Current = null;
- }
- public void DisposeDevicePublisher()
- {
- if (_publisher != null)
- {
- _logger.LogInformation("Disposing SsdpDevicePublisher");
- _publisher.Dispose();
- _publisher = null;
- }
+ _disposed = true;
}
}
}
diff --git a/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrar.cs b/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarService.cs
index 64dfc840a..28de2fef5 100644
--- a/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrar.cs
+++ b/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarService.cs
@@ -8,12 +8,12 @@ using Microsoft.Extensions.Logging;
namespace Emby.Dlna.MediaReceiverRegistrar
{
- public class MediaReceiverRegistrar : BaseService, IMediaReceiverRegistrar
+ public class MediaReceiverRegistrarService : BaseService, IMediaReceiverRegistrar
{
private readonly IServerConfigurationManager _config;
- public MediaReceiverRegistrar(
- ILogger<MediaReceiverRegistrar> logger,
+ public MediaReceiverRegistrarService(
+ ILogger<MediaReceiverRegistrarService> logger,
IHttpClient httpClient,
IServerConfigurationManager config)
: base(logger, httpClient)
diff --git a/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs b/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs
index 849702546..26994925d 100644
--- a/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs
+++ b/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs
@@ -10,7 +10,8 @@ namespace Emby.Dlna.MediaReceiverRegistrar
{
public string GetXml()
{
- return new ServiceXmlBuilder().GetXml(new ServiceActionListBuilder().GetActions(),
+ return new ServiceXmlBuilder().GetXml(
+ new ServiceActionListBuilder().GetActions(),
GetStateVariables());
}
diff --git a/Emby.Dlna/PlayTo/Device.cs b/Emby.Dlna/PlayTo/Device.cs
index 72834c69d..5462e7abc 100644
--- a/Emby.Dlna/PlayTo/Device.cs
+++ b/Emby.Dlna/PlayTo/Device.cs
@@ -19,15 +19,40 @@ namespace Emby.Dlna.PlayTo
{
public class Device : IDisposable
{
+ private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
+
+ private readonly IHttpClient _httpClient;
+
+ private readonly ILogger _logger;
+
+ private readonly object _timerLock = new object();
private Timer _timer;
+ private int _muteVol;
+ private int _volume;
+ private DateTime _lastVolumeRefresh;
+ private bool _volumeRefreshActive;
+ private int _connectFailureCount;
+ private bool _disposed;
+
+ public Device(DeviceInfo deviceProperties, IHttpClient httpClient, ILogger logger)
+ {
+ Properties = deviceProperties;
+ _httpClient = httpClient;
+ _logger = logger;
+ }
+
+ public event EventHandler<PlaybackStartEventArgs> PlaybackStart;
+
+ public event EventHandler<PlaybackProgressEventArgs> PlaybackProgress;
+
+ public event EventHandler<PlaybackStoppedEventArgs> PlaybackStopped;
+
+ public event EventHandler<MediaChangedEventArgs> MediaChanged;
public DeviceInfo Properties { get; set; }
- private int _muteVol;
public bool IsMuted { get; set; }
- private int _volume;
-
public int Volume
{
get
@@ -43,29 +68,21 @@ namespace Emby.Dlna.PlayTo
public TimeSpan Position { get; set; } = TimeSpan.FromSeconds(0);
- public TRANSPORTSTATE TransportState { get; private set; }
-
- public bool IsPlaying => TransportState == TRANSPORTSTATE.PLAYING;
+ public TransportState TransportState { get; private set; }
- public bool IsPaused => TransportState == TRANSPORTSTATE.PAUSED || TransportState == TRANSPORTSTATE.PAUSED_PLAYBACK;
+ public bool IsPlaying => TransportState == TransportState.Playing;
- public bool IsStopped => TransportState == TRANSPORTSTATE.STOPPED;
+ public bool IsPaused => TransportState == TransportState.Paused || TransportState == TransportState.PausedPlayback;
- private readonly IHttpClient _httpClient;
+ public bool IsStopped => TransportState == TransportState.Stopped;
- private readonly ILogger _logger;
+ public Action OnDeviceUnavailable { get; set; }
- private readonly IServerConfigurationManager _config;
+ private TransportCommands AvCommands { get; set; }
- public Action OnDeviceUnavailable { get; set; }
+ private TransportCommands RendererCommands { get; set; }
- public Device(DeviceInfo deviceProperties, IHttpClient httpClient, ILogger logger, IServerConfigurationManager config)
- {
- Properties = deviceProperties;
- _httpClient = httpClient;
- _logger = logger;
- _config = config;
- }
+ public UBaseObject CurrentMediaInfo { get; private set; }
public void Start()
{
@@ -73,8 +90,6 @@ namespace Emby.Dlna.PlayTo
_timer = new Timer(TimerCallback, null, 1000, Timeout.Infinite);
}
- private DateTime _lastVolumeRefresh;
- private bool _volumeRefreshActive;
private Task RefreshVolumeIfNeeded()
{
if (_volumeRefreshActive
@@ -105,7 +120,6 @@ namespace Emby.Dlna.PlayTo
}
}
- private readonly object _timerLock = new object();
private void RestartTimer(bool immediate = false)
{
lock (_timerLock)
@@ -233,6 +247,9 @@ namespace Emby.Dlna.PlayTo
/// <summary>
/// Sets volume on a scale of 0-100.
/// </summary>
+ /// <param name="value">The volume on a scale of 0-100.</param>
+ /// <param name="cancellationToken">The cancellation token to cancel operation.</param>
+ /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public async Task SetVolume(int value, CancellationToken cancellationToken)
{
var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false);
@@ -275,7 +292,7 @@ namespace Emby.Dlna.PlayTo
throw new InvalidOperationException("Unable to find service");
}
- await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, string.Format("{0:hh}:{0:mm}:{0:ss}", value), "REL_TIME"))
+ await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, string.Format(CultureInfo.InvariantCulture, "{0:hh}:{0:mm}:{0:ss}", value), "REL_TIME"))
.ConfigureAwait(false);
RestartTimer(true);
@@ -285,7 +302,7 @@ namespace Emby.Dlna.PlayTo
{
var avCommands = await GetAVProtocolAsync(cancellationToken).ConfigureAwait(false);
- url = url.Replace("&", "&amp;");
+ url = url.Replace("&", "&amp;", StringComparison.Ordinal);
_logger.LogDebug("{0} - SetAvTransport Uri: {1} DlnaHeaders: {2}", Properties.Name, url, header);
@@ -297,8 +314,8 @@ namespace Emby.Dlna.PlayTo
var dictionary = new Dictionary<string, string>
{
- {"CurrentURI", url},
- {"CurrentURIMetaData", CreateDidlMeta(metaData)}
+ { "CurrentURI", url },
+ { "CurrentURIMetaData", CreateDidlMeta(metaData) }
};
var service = GetAvTransportService();
@@ -401,13 +418,11 @@ namespace Emby.Dlna.PlayTo
await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, 1))
.ConfigureAwait(false);
- TransportState = TRANSPORTSTATE.PAUSED;
+ TransportState = TransportState.Paused;
RestartTimer(true);
}
- private int _connectFailureCount;
-
private async void TimerCallback(object sender)
{
if (_disposed)
@@ -436,7 +451,7 @@ namespace Emby.Dlna.PlayTo
if (transportState.HasValue)
{
// If we're not playing anything no need to get additional data
- if (transportState.Value == TRANSPORTSTATE.STOPPED)
+ if (transportState.Value == TransportState.Stopped)
{
UpdateMediaInfo(null, transportState.Value);
}
@@ -465,7 +480,7 @@ namespace Emby.Dlna.PlayTo
}
// If we're not playing anything make sure we don't get data more often than neccessry to keep the Session alive
- if (transportState.Value == TRANSPORTSTATE.STOPPED)
+ if (transportState.Value == TransportState.Stopped)
{
RestartTimerInactive();
}
@@ -539,7 +554,7 @@ namespace Emby.Dlna.PlayTo
return;
}
- var volume = result.Document.Descendants(uPnpNamespaces.RenderingControl + "GetVolumeResponse").Select(i => i.Element("CurrentVolume")).FirstOrDefault(i => i != null);
+ var volume = result.Document.Descendants(UPnpNamespaces.RenderingControl + "GetVolumeResponse").Select(i => i.Element("CurrentVolume")).FirstOrDefault(i => i != null);
var volumeValue = volume?.Value;
if (string.IsNullOrWhiteSpace(volumeValue))
@@ -589,14 +604,14 @@ namespace Emby.Dlna.PlayTo
return;
}
- var valueNode = result.Document.Descendants(uPnpNamespaces.RenderingControl + "GetMuteResponse")
+ var valueNode = result.Document.Descendants(UPnpNamespaces.RenderingControl + "GetMuteResponse")
.Select(i => i.Element("CurrentMute"))
.FirstOrDefault(i => i != null);
IsMuted = string.Equals(valueNode?.Value, "1", StringComparison.OrdinalIgnoreCase);
}
- private async Task<TRANSPORTSTATE?> GetTransportInfo(TransportCommands avCommands, CancellationToken cancellationToken)
+ private async Task<TransportState?> GetTransportInfo(TransportCommands avCommands, CancellationToken cancellationToken)
{
var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetTransportInfo");
if (command == null)
@@ -623,12 +638,12 @@ namespace Emby.Dlna.PlayTo
}
var transportState =
- result.Document.Descendants(uPnpNamespaces.AvTransport + "GetTransportInfoResponse").Select(i => i.Element("CurrentTransportState")).FirstOrDefault(i => i != null);
+ result.Document.Descendants(UPnpNamespaces.AvTransport + "GetTransportInfoResponse").Select(i => i.Element("CurrentTransportState")).FirstOrDefault(i => i != null);
var transportStateValue = transportState?.Value;
if (transportStateValue != null
- && Enum.TryParse(transportStateValue, true, out TRANSPORTSTATE state))
+ && Enum.TryParse(transportStateValue, true, out TransportState state))
{
return state;
}
@@ -636,7 +651,7 @@ namespace Emby.Dlna.PlayTo
return null;
}
- private async Task<uBaseObject> GetMediaInfo(TransportCommands avCommands, CancellationToken cancellationToken)
+ private async Task<UBaseObject> GetMediaInfo(TransportCommands avCommands, CancellationToken cancellationToken)
{
var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetMediaInfo");
if (command == null)
@@ -671,7 +686,7 @@ namespace Emby.Dlna.PlayTo
return null;
}
- var e = track.Element(uPnpNamespaces.items) ?? track;
+ var e = track.Element(UPnpNamespaces.Items) ?? track;
var elementString = (string)e;
@@ -687,13 +702,13 @@ namespace Emby.Dlna.PlayTo
return null;
}
- e = track.Element(uPnpNamespaces.items) ?? track;
+ e = track.Element(UPnpNamespaces.Items) ?? track;
elementString = (string)e;
if (!string.IsNullOrWhiteSpace(elementString))
{
- return new uBaseObject
+ return new UBaseObject
{
Url = elementString
};
@@ -702,7 +717,7 @@ namespace Emby.Dlna.PlayTo
return null;
}
- private async Task<(bool, uBaseObject)> GetPositionInfo(TransportCommands avCommands, CancellationToken cancellationToken)
+ private async Task<(bool, UBaseObject)> GetPositionInfo(TransportCommands avCommands, CancellationToken cancellationToken)
{
var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetPositionInfo");
if (command == null)
@@ -731,11 +746,11 @@ namespace Emby.Dlna.PlayTo
return (false, null);
}
- var trackUriElem = result.Document.Descendants(uPnpNamespaces.AvTransport + "GetPositionInfoResponse").Select(i => i.Element("TrackURI")).FirstOrDefault(i => i != null);
- var trackUri = trackUriElem == null ? null : trackUriElem.Value;
+ var trackUriElem = result.Document.Descendants(UPnpNamespaces.AvTransport + "GetPositionInfoResponse").Select(i => i.Element("TrackURI")).FirstOrDefault(i => i != null);
+ var trackUri = trackUriElem?.Value;
- var durationElem = result.Document.Descendants(uPnpNamespaces.AvTransport + "GetPositionInfoResponse").Select(i => i.Element("TrackDuration")).FirstOrDefault(i => i != null);
- var duration = durationElem == null ? null : durationElem.Value;
+ var durationElem = result.Document.Descendants(UPnpNamespaces.AvTransport + "GetPositionInfoResponse").Select(i => i.Element("TrackDuration")).FirstOrDefault(i => i != null);
+ var duration = durationElem?.Value;
if (!string.IsNullOrWhiteSpace(duration)
&& !string.Equals(duration, "NOT_IMPLEMENTED", StringComparison.OrdinalIgnoreCase))
@@ -747,8 +762,8 @@ namespace Emby.Dlna.PlayTo
Duration = null;
}
- var positionElem = result.Document.Descendants(uPnpNamespaces.AvTransport + "GetPositionInfoResponse").Select(i => i.Element("RelTime")).FirstOrDefault(i => i != null);
- var position = positionElem == null ? null : positionElem.Value;
+ var positionElem = result.Document.Descendants(UPnpNamespaces.AvTransport + "GetPositionInfoResponse").Select(i => i.Element("RelTime")).FirstOrDefault(i => i != null);
+ var position = positionElem?.Value;
if (!string.IsNullOrWhiteSpace(position) && !string.Equals(position, "NOT_IMPLEMENTED", StringComparison.OrdinalIgnoreCase))
{
@@ -787,7 +802,7 @@ namespace Emby.Dlna.PlayTo
return (true, null);
}
- var e = uPnpResponse.Element(uPnpNamespaces.items);
+ var e = uPnpResponse.Element(UPnpNamespaces.Items);
var uTrack = CreateUBaseObject(e, trackUri);
@@ -819,7 +834,7 @@ namespace Emby.Dlna.PlayTo
// some devices send back invalid xml
try
{
- return XElement.Parse(xml.Replace("&", "&amp;"));
+ return XElement.Parse(xml.Replace("&", "&amp;", StringComparison.Ordinal));
}
catch (XmlException)
{
@@ -828,27 +843,27 @@ namespace Emby.Dlna.PlayTo
return null;
}
- private static uBaseObject CreateUBaseObject(XElement container, string trackUri)
+ private static UBaseObject CreateUBaseObject(XElement container, string trackUri)
{
if (container == null)
{
throw new ArgumentNullException(nameof(container));
}
- var url = container.GetValue(uPnpNamespaces.Res);
+ var url = container.GetValue(UPnpNamespaces.Res);
if (string.IsNullOrWhiteSpace(url))
{
url = trackUri;
}
- return new uBaseObject
+ return new UBaseObject
{
- Id = container.GetAttributeValue(uPnpNamespaces.Id),
- ParentId = container.GetAttributeValue(uPnpNamespaces.ParentId),
- Title = container.GetValue(uPnpNamespaces.title),
- IconUrl = container.GetValue(uPnpNamespaces.Artwork),
- SecondText = "",
+ Id = container.GetAttributeValue(UPnpNamespaces.Id),
+ ParentId = container.GetAttributeValue(UPnpNamespaces.ParentId),
+ Title = container.GetValue(UPnpNamespaces.Title),
+ IconUrl = container.GetValue(UPnpNamespaces.Artwork),
+ SecondText = string.Empty,
Url = url,
ProtocolInfo = GetProtocolInfo(container),
MetaData = container.ToString()
@@ -862,11 +877,11 @@ namespace Emby.Dlna.PlayTo
throw new ArgumentNullException(nameof(container));
}
- var resElement = container.Element(uPnpNamespaces.Res);
+ var resElement = container.Element(UPnpNamespaces.Res);
if (resElement != null)
{
- var info = resElement.Attribute(uPnpNamespaces.ProtocolInfo);
+ var info = resElement.Attribute(UPnpNamespaces.ProtocolInfo);
if (info != null && !string.IsNullOrWhiteSpace(info.Value))
{
@@ -941,12 +956,12 @@ namespace Emby.Dlna.PlayTo
return url;
}
- if (!url.Contains("/"))
+ if (!url.Contains('/', StringComparison.Ordinal))
{
url = "/dmr/" + url;
}
- if (!url.StartsWith("/"))
+ if (!url.StartsWith("/", StringComparison.Ordinal))
{
url = "/" + url;
}
@@ -954,11 +969,7 @@ namespace Emby.Dlna.PlayTo
return baseUrl + url;
}
- private TransportCommands AvCommands { get; set; }
-
- private TransportCommands RendererCommands { get; set; }
-
- public static async Task<Device> CreateuPnpDeviceAsync(Uri url, IHttpClient httpClient, IServerConfigurationManager config, ILogger logger, CancellationToken cancellationToken)
+ public static async Task<Device> CreateuPnpDeviceAsync(Uri url, IHttpClient httpClient, ILogger logger, CancellationToken cancellationToken)
{
var ssdpHttpClient = new SsdpHttpClient(httpClient);
@@ -966,13 +977,13 @@ namespace Emby.Dlna.PlayTo
var friendlyNames = new List<string>();
- var name = document.Descendants(uPnpNamespaces.ud.GetName("friendlyName")).FirstOrDefault();
+ var name = document.Descendants(UPnpNamespaces.Ud.GetName("friendlyName")).FirstOrDefault();
if (name != null && !string.IsNullOrWhiteSpace(name.Value))
{
friendlyNames.Add(name.Value);
}
- var room = document.Descendants(uPnpNamespaces.ud.GetName("roomName")).FirstOrDefault();
+ var room = document.Descendants(UPnpNamespaces.Ud.GetName("roomName")).FirstOrDefault();
if (room != null && !string.IsNullOrWhiteSpace(room.Value))
{
friendlyNames.Add(room.Value);
@@ -981,77 +992,77 @@ namespace Emby.Dlna.PlayTo
var deviceProperties = new DeviceInfo()
{
Name = string.Join(" ", friendlyNames),
- BaseUrl = string.Format("http://{0}:{1}", url.Host, url.Port)
+ BaseUrl = string.Format(CultureInfo.InvariantCulture, "http://{0}:{1}", url.Host, url.Port)
};
- var model = document.Descendants(uPnpNamespaces.ud.GetName("modelName")).FirstOrDefault();
+ var model = document.Descendants(UPnpNamespaces.Ud.GetName("modelName")).FirstOrDefault();
if (model != null)
{
deviceProperties.ModelName = model.Value;
}
- var modelNumber = document.Descendants(uPnpNamespaces.ud.GetName("modelNumber")).FirstOrDefault();
+ var modelNumber = document.Descendants(UPnpNamespaces.Ud.GetName("modelNumber")).FirstOrDefault();
if (modelNumber != null)
{
deviceProperties.ModelNumber = modelNumber.Value;
}
- var uuid = document.Descendants(uPnpNamespaces.ud.GetName("UDN")).FirstOrDefault();
+ var uuid = document.Descendants(UPnpNamespaces.Ud.GetName("UDN")).FirstOrDefault();
if (uuid != null)
{
deviceProperties.UUID = uuid.Value;
}
- var manufacturer = document.Descendants(uPnpNamespaces.ud.GetName("manufacturer")).FirstOrDefault();
+ var manufacturer = document.Descendants(UPnpNamespaces.Ud.GetName("manufacturer")).FirstOrDefault();
if (manufacturer != null)
{
deviceProperties.Manufacturer = manufacturer.Value;
}
- var manufacturerUrl = document.Descendants(uPnpNamespaces.ud.GetName("manufacturerURL")).FirstOrDefault();
+ var manufacturerUrl = document.Descendants(UPnpNamespaces.Ud.GetName("manufacturerURL")).FirstOrDefault();
if (manufacturerUrl != null)
{
deviceProperties.ManufacturerUrl = manufacturerUrl.Value;
}
- var presentationUrl = document.Descendants(uPnpNamespaces.ud.GetName("presentationURL")).FirstOrDefault();
+ var presentationUrl = document.Descendants(UPnpNamespaces.Ud.GetName("presentationURL")).FirstOrDefault();
if (presentationUrl != null)
{
deviceProperties.PresentationUrl = presentationUrl.Value;
}
- var modelUrl = document.Descendants(uPnpNamespaces.ud.GetName("modelURL")).FirstOrDefault();
+ var modelUrl = document.Descendants(UPnpNamespaces.Ud.GetName("modelURL")).FirstOrDefault();
if (modelUrl != null)
{
deviceProperties.ModelUrl = modelUrl.Value;
}
- var serialNumber = document.Descendants(uPnpNamespaces.ud.GetName("serialNumber")).FirstOrDefault();
+ var serialNumber = document.Descendants(UPnpNamespaces.Ud.GetName("serialNumber")).FirstOrDefault();
if (serialNumber != null)
{
deviceProperties.SerialNumber = serialNumber.Value;
}
- var modelDescription = document.Descendants(uPnpNamespaces.ud.GetName("modelDescription")).FirstOrDefault();
+ var modelDescription = document.Descendants(UPnpNamespaces.Ud.GetName("modelDescription")).FirstOrDefault();
if (modelDescription != null)
{
deviceProperties.ModelDescription = modelDescription.Value;
}
- var icon = document.Descendants(uPnpNamespaces.ud.GetName("icon")).FirstOrDefault();
+ var icon = document.Descendants(UPnpNamespaces.Ud.GetName("icon")).FirstOrDefault();
if (icon != null)
{
deviceProperties.Icon = CreateIcon(icon);
}
- foreach (var services in document.Descendants(uPnpNamespaces.ud.GetName("serviceList")))
+ foreach (var services in document.Descendants(UPnpNamespaces.Ud.GetName("serviceList")))
{
if (services == null)
{
continue;
}
- var servicesList = services.Descendants(uPnpNamespaces.ud.GetName("service"));
+ var servicesList = services.Descendants(UPnpNamespaces.Ud.GetName("service"));
if (servicesList == null)
{
continue;
@@ -1068,10 +1079,9 @@ namespace Emby.Dlna.PlayTo
}
}
- return new Device(deviceProperties, httpClient, logger, config);
+ return new Device(deviceProperties, httpClient, logger);
}
- private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
private static DeviceIcon CreateIcon(XElement element)
{
if (element == null)
@@ -1079,11 +1089,11 @@ namespace Emby.Dlna.PlayTo
throw new ArgumentNullException(nameof(element));
}
- var mimeType = element.GetDescendantValue(uPnpNamespaces.ud.GetName("mimetype"));
- var width = element.GetDescendantValue(uPnpNamespaces.ud.GetName("width"));
- var height = element.GetDescendantValue(uPnpNamespaces.ud.GetName("height"));
- var depth = element.GetDescendantValue(uPnpNamespaces.ud.GetName("depth"));
- var url = element.GetDescendantValue(uPnpNamespaces.ud.GetName("url"));
+ var mimeType = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("mimetype"));
+ var width = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("width"));
+ var height = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("height"));
+ var depth = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("depth"));
+ var url = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("url"));
var widthValue = int.Parse(width, NumberStyles.Integer, UsCulture);
var heightValue = int.Parse(height, NumberStyles.Integer, UsCulture);
@@ -1100,11 +1110,11 @@ namespace Emby.Dlna.PlayTo
private static DeviceService Create(XElement element)
{
- var type = element.GetDescendantValue(uPnpNamespaces.ud.GetName("serviceType"));
- var id = element.GetDescendantValue(uPnpNamespaces.ud.GetName("serviceId"));
- var scpdUrl = element.GetDescendantValue(uPnpNamespaces.ud.GetName("SCPDURL"));
- var controlURL = element.GetDescendantValue(uPnpNamespaces.ud.GetName("controlURL"));
- var eventSubURL = element.GetDescendantValue(uPnpNamespaces.ud.GetName("eventSubURL"));
+ var type = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("serviceType"));
+ var id = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("serviceId"));
+ var scpdUrl = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("SCPDURL"));
+ var controlURL = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("controlURL"));
+ var eventSubURL = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("eventSubURL"));
return new DeviceService
{
@@ -1116,14 +1126,7 @@ namespace Emby.Dlna.PlayTo
};
}
- public event EventHandler<PlaybackStartEventArgs> PlaybackStart;
- public event EventHandler<PlaybackProgressEventArgs> PlaybackProgress;
- public event EventHandler<PlaybackStoppedEventArgs> PlaybackStopped;
- public event EventHandler<MediaChangedEventArgs> MediaChanged;
-
- public uBaseObject CurrentMediaInfo { get; private set; }
-
- private void UpdateMediaInfo(uBaseObject mediaInfo, TRANSPORTSTATE state)
+ private void UpdateMediaInfo(UBaseObject mediaInfo, TransportState state)
{
TransportState = state;
@@ -1132,7 +1135,7 @@ namespace Emby.Dlna.PlayTo
if (previousMediaInfo == null && mediaInfo != null)
{
- if (state != TRANSPORTSTATE.STOPPED)
+ if (state != TransportState.Stopped)
{
OnPlaybackStart(mediaInfo);
}
@@ -1151,7 +1154,7 @@ namespace Emby.Dlna.PlayTo
}
}
- private void OnPlaybackStart(uBaseObject mediaInfo)
+ private void OnPlaybackStart(UBaseObject mediaInfo)
{
if (string.IsNullOrWhiteSpace(mediaInfo.Url))
{
@@ -1164,7 +1167,7 @@ namespace Emby.Dlna.PlayTo
});
}
- private void OnPlaybackProgress(uBaseObject mediaInfo)
+ private void OnPlaybackProgress(UBaseObject mediaInfo)
{
if (string.IsNullOrWhiteSpace(mediaInfo.Url))
{
@@ -1177,7 +1180,7 @@ namespace Emby.Dlna.PlayTo
});
}
- private void OnPlaybackStop(uBaseObject mediaInfo)
+ private void OnPlaybackStop(UBaseObject mediaInfo)
{
PlaybackStopped?.Invoke(this, new PlaybackStoppedEventArgs
{
@@ -1185,7 +1188,7 @@ namespace Emby.Dlna.PlayTo
});
}
- private void OnMediaChanged(uBaseObject old, uBaseObject newMedia)
+ private void OnMediaChanged(UBaseObject old, UBaseObject newMedia)
{
MediaChanged?.Invoke(this, new MediaChangedEventArgs
{
@@ -1194,14 +1197,17 @@ namespace Emby.Dlna.PlayTo
});
}
- bool _disposed;
-
+ /// <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)
@@ -1220,9 +1226,10 @@ namespace Emby.Dlna.PlayTo
_disposed = true;
}
+ /// <inheritdoc />
public override string ToString()
{
- return string.Format("{0} - {1}", Properties.Name, Properties.BaseUrl);
+ return string.Format(CultureInfo.InvariantCulture, "{0} - {1}", Properties.Name, Properties.BaseUrl);
}
}
}
diff --git a/Emby.Dlna/PlayTo/DeviceInfo.cs b/Emby.Dlna/PlayTo/DeviceInfo.cs
index f3aaaebc4..d3daab9e0 100644
--- a/Emby.Dlna/PlayTo/DeviceInfo.cs
+++ b/Emby.Dlna/PlayTo/DeviceInfo.cs
@@ -8,6 +8,9 @@ namespace Emby.Dlna.PlayTo
{
public class DeviceInfo
{
+ private readonly List<DeviceService> _services = new List<DeviceService>();
+ private string _baseUrl = string.Empty;
+
public DeviceInfo()
{
Name = "Generic Device";
@@ -33,7 +36,6 @@ namespace Emby.Dlna.PlayTo
public string PresentationUrl { get; set; }
- private string _baseUrl = string.Empty;
public string BaseUrl
{
get => _baseUrl;
@@ -42,7 +44,6 @@ namespace Emby.Dlna.PlayTo
public DeviceIcon Icon { get; set; }
- private readonly List<DeviceService> _services = new List<DeviceService>();
public List<DeviceService> Services => _services;
public DeviceIdentification ToDeviceIdentification()
diff --git a/Emby.Dlna/PlayTo/MediaChangedEventArgs.cs b/Emby.Dlna/PlayTo/MediaChangedEventArgs.cs
new file mode 100644
index 000000000..dabd079af
--- /dev/null
+++ b/Emby.Dlna/PlayTo/MediaChangedEventArgs.cs
@@ -0,0 +1,13 @@
+#pragma warning disable CS1591
+
+using System;
+
+namespace Emby.Dlna.PlayTo
+{
+ public class MediaChangedEventArgs : EventArgs
+ {
+ public UBaseObject OldMediaInfo { get; set; }
+
+ public UBaseObject NewMediaInfo { get; set; }
+ }
+}
diff --git a/Emby.Dlna/PlayTo/PlayToController.cs b/Emby.Dlna/PlayTo/PlayToController.cs
index 92a93d434..328759c5b 100644
--- a/Emby.Dlna/PlayTo/PlayToController.cs
+++ b/Emby.Dlna/PlayTo/PlayToController.cs
@@ -8,6 +8,7 @@ using System.Threading;
using System.Threading.Tasks;
using Emby.Dlna.Didl;
using Jellyfin.Data.Entities;
+using Jellyfin.Data.Events;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing;
@@ -18,7 +19,6 @@ using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Session;
using Microsoft.AspNetCore.WebUtilities;
@@ -31,7 +31,6 @@ namespace Emby.Dlna.PlayTo
{
private static readonly CultureInfo _usCulture = CultureInfo.ReadOnly(new CultureInfo("en-US"));
- private Device _device;
private readonly SessionInfo _session;
private readonly ISessionManager _sessionManager;
private readonly ILibraryManager _libraryManager;
@@ -50,6 +49,7 @@ namespace Emby.Dlna.PlayTo
private readonly string _accessToken;
private readonly List<PlaylistItem> _playlist = new List<PlaylistItem>();
+ private Device _device;
private int _currentPlaylistIndex;
private bool _disposed;
@@ -372,8 +372,13 @@ namespace Emby.Dlna.PlayTo
if (!command.ControllingUserId.Equals(Guid.Empty))
{
- _sessionManager.LogSessionActivity(_session.Client, _session.ApplicationVersion, _session.DeviceId,
- _session.DeviceName, _session.RemoteEndPoint, user);
+ _sessionManager.LogSessionActivity(
+ _session.Client,
+ _session.ApplicationVersion,
+ _session.DeviceId,
+ _session.DeviceName,
+ _session.RemoteEndPoint,
+ user);
}
return PlayItems(playlist, cancellationToken);
@@ -498,42 +503,44 @@ namespace Emby.Dlna.PlayTo
if (streamInfo.MediaType == DlnaProfileType.Audio)
{
return new ContentFeatureBuilder(profile)
- .BuildAudioHeader(streamInfo.Container,
- streamInfo.TargetAudioCodec.FirstOrDefault(),
- streamInfo.TargetAudioBitrate,
- streamInfo.TargetAudioSampleRate,
- streamInfo.TargetAudioChannels,
- streamInfo.TargetAudioBitDepth,
- streamInfo.IsDirectStream,
- streamInfo.RunTimeTicks ?? 0,
- streamInfo.TranscodeSeekInfo);
+ .BuildAudioHeader(
+ streamInfo.Container,
+ streamInfo.TargetAudioCodec.FirstOrDefault(),
+ streamInfo.TargetAudioBitrate,
+ streamInfo.TargetAudioSampleRate,
+ streamInfo.TargetAudioChannels,
+ streamInfo.TargetAudioBitDepth,
+ streamInfo.IsDirectStream,
+ streamInfo.RunTimeTicks ?? 0,
+ streamInfo.TranscodeSeekInfo);
}
if (streamInfo.MediaType == DlnaProfileType.Video)
{
var list = new ContentFeatureBuilder(profile)
- .BuildVideoHeader(streamInfo.Container,
- streamInfo.TargetVideoCodec.FirstOrDefault(),
- streamInfo.TargetAudioCodec.FirstOrDefault(),
- streamInfo.TargetWidth,
- streamInfo.TargetHeight,
- streamInfo.TargetVideoBitDepth,
- streamInfo.TargetVideoBitrate,
- streamInfo.TargetTimestamp,
- streamInfo.IsDirectStream,
- streamInfo.RunTimeTicks ?? 0,
- streamInfo.TargetVideoProfile,
- streamInfo.TargetVideoLevel,
- streamInfo.TargetFramerate ?? 0,
- streamInfo.TargetPacketLength,
- streamInfo.TranscodeSeekInfo,
- streamInfo.IsTargetAnamorphic,
- streamInfo.IsTargetInterlaced,
- streamInfo.TargetRefFrames,
- streamInfo.TargetVideoStreamCount,
- streamInfo.TargetAudioStreamCount,
- streamInfo.TargetVideoCodecTag,
- streamInfo.IsTargetAVC);
+ .BuildVideoHeader(
+ streamInfo.Container,
+ streamInfo.TargetVideoCodec.FirstOrDefault(),
+ streamInfo.TargetAudioCodec.FirstOrDefault(),
+ streamInfo.TargetWidth,
+ streamInfo.TargetHeight,
+ streamInfo.TargetVideoBitDepth,
+ streamInfo.TargetVideoBitrate,
+ streamInfo.TargetTimestamp,
+ streamInfo.IsDirectStream,
+ streamInfo.RunTimeTicks ?? 0,
+ streamInfo.TargetVideoProfile,
+ streamInfo.TargetVideoLevel,
+ streamInfo.TargetFramerate ?? 0,
+ streamInfo.TargetPacketLength,
+ streamInfo.TranscodeSeekInfo,
+ streamInfo.IsTargetAnamorphic,
+ streamInfo.IsTargetInterlaced,
+ streamInfo.TargetRefFrames,
+ streamInfo.TargetVideoStreamCount,
+ streamInfo.TargetAudioStreamCount,
+ streamInfo.TargetVideoCodecTag,
+ streamInfo.IsTargetAVC);
return list.Count == 0 ? null : list[0];
}
@@ -633,6 +640,10 @@ namespace Emby.Dlna.PlayTo
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)
@@ -673,48 +684,41 @@ namespace Emby.Dlna.PlayTo
case GeneralCommandType.ToggleMute:
return _device.ToggleMute(cancellationToken);
case GeneralCommandType.SetAudioStreamIndex:
+ if (command.Arguments.TryGetValue("Index", out string index))
{
- if (command.Arguments.TryGetValue("Index", out string arg))
+ if (int.TryParse(index, NumberStyles.Integer, _usCulture, out var val))
{
- if (int.TryParse(arg, NumberStyles.Integer, _usCulture, out var val))
- {
- return SetAudioStreamIndex(val);
- }
-
- throw new ArgumentException("Unsupported SetAudioStreamIndex value supplied.");
+ return SetAudioStreamIndex(val);
}
- throw new ArgumentException("SetAudioStreamIndex argument cannot be null");
+ throw new ArgumentException("Unsupported SetAudioStreamIndex value supplied.");
}
+
+ throw new ArgumentException("SetAudioStreamIndex argument cannot be null");
case GeneralCommandType.SetSubtitleStreamIndex:
+ if (command.Arguments.TryGetValue("Index", out index))
{
- if (command.Arguments.TryGetValue("Index", out string arg))
+ if (int.TryParse(index, NumberStyles.Integer, _usCulture, out var val))
{
- if (int.TryParse(arg, NumberStyles.Integer, _usCulture, out var val))
- {
- return SetSubtitleStreamIndex(val);
- }
-
- throw new ArgumentException("Unsupported SetSubtitleStreamIndex value supplied.");
+ return SetSubtitleStreamIndex(val);
}
- throw new ArgumentException("SetSubtitleStreamIndex argument cannot be null");
+ throw new ArgumentException("Unsupported SetSubtitleStreamIndex value supplied.");
}
+
+ throw new ArgumentException("SetSubtitleStreamIndex argument cannot be null");
case GeneralCommandType.SetVolume:
+ if (command.Arguments.TryGetValue("Volume", out string vol))
{
- if (command.Arguments.TryGetValue("Volume", out string arg))
+ if (int.TryParse(vol, NumberStyles.Integer, _usCulture, out var volume))
{
- if (int.TryParse(arg, NumberStyles.Integer, _usCulture, out var volume))
- {
- return _device.SetVolume(volume, cancellationToken);
- }
-
- throw new ArgumentException("Unsupported volume value supplied.");
+ return _device.SetVolume(volume, cancellationToken);
}
- throw new ArgumentException("Volume argument cannot be null");
+ throw new ArgumentException("Unsupported volume value supplied.");
}
+ throw new ArgumentException("Volume argument cannot be null");
default:
return Task.CompletedTask;
}
@@ -778,7 +782,7 @@ namespace Emby.Dlna.PlayTo
const int maxWait = 15000000;
const int interval = 500;
var currentWait = 0;
- while (_device.TransportState != TRANSPORTSTATE.PLAYING && currentWait < maxWait)
+ while (_device.TransportState != TransportState.Playing && currentWait < maxWait)
{
await Task.Delay(interval).ConfigureAwait(false);
currentWait += interval;
@@ -787,8 +791,67 @@ namespace Emby.Dlna.PlayTo
await _device.Seek(TimeSpan.FromTicks(positionTicks), cancellationToken).ConfigureAwait(false);
}
+ private static int? GetIntValue(IReadOnlyDictionary<string, string> values, string name)
+ {
+ var value = values.GetValueOrDefault(name);
+
+ if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
+ {
+ return result;
+ }
+
+ return null;
+ }
+
+ private static long GetLongValue(IReadOnlyDictionary<string, string> values, string name)
+ {
+ var value = values.GetValueOrDefault(name);
+
+ if (long.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
+ {
+ return result;
+ }
+
+ return 0;
+ }
+
+ /// <inheritdoc />
+ public Task SendMessage<T>(string name, Guid messageId, T data, CancellationToken cancellationToken)
+ {
+ if (_disposed)
+ {
+ throw new ObjectDisposedException(GetType().Name);
+ }
+
+ if (_device == null)
+ {
+ return Task.CompletedTask;
+ }
+
+ if (string.Equals(name, "Play", StringComparison.OrdinalIgnoreCase))
+ {
+ return SendPlayCommand(data as PlayRequest, cancellationToken);
+ }
+
+ if (string.Equals(name, "PlayState", StringComparison.OrdinalIgnoreCase))
+ {
+ return SendPlaystateCommand(data as PlaystateRequest, cancellationToken);
+ }
+
+ if (string.Equals(name, "GeneralCommand", StringComparison.OrdinalIgnoreCase))
+ {
+ return SendGeneralCommand(data as GeneralCommand, cancellationToken);
+ }
+
+ // Not supported or needed right now
+ return Task.CompletedTask;
+ }
+
private class StreamParams
{
+ private MediaSourceInfo mediaSource;
+ private IMediaSourceManager _mediaSourceManager;
+
public Guid ItemId { get; set; }
public bool IsDirectStream { get; set; }
@@ -809,15 +872,11 @@ namespace Emby.Dlna.PlayTo
public BaseItem Item { get; set; }
- private MediaSourceInfo MediaSource;
-
- private IMediaSourceManager _mediaSourceManager;
-
public async Task<MediaSourceInfo> GetMediaSource(CancellationToken cancellationToken)
{
- if (MediaSource != null)
+ if (mediaSource != null)
{
- return MediaSource;
+ return mediaSource;
}
var hasMediaSources = Item as IHasMediaSources;
@@ -827,9 +886,9 @@ namespace Emby.Dlna.PlayTo
return null;
}
- MediaSource = await _mediaSourceManager.GetMediaSource(Item, MediaSourceId, LiveStreamId, false, cancellationToken).ConfigureAwait(false);
+ mediaSource = await _mediaSourceManager.GetMediaSource(Item, MediaSourceId, LiveStreamId, false, cancellationToken).ConfigureAwait(false);
- return MediaSource;
+ return mediaSource;
}
private static Guid GetItemId(string url)
@@ -901,61 +960,5 @@ namespace Emby.Dlna.PlayTo
return request;
}
}
-
- private static int? GetIntValue(IReadOnlyDictionary<string, string> values, string name)
- {
- var value = values.GetValueOrDefault(name);
-
- if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
- {
- return result;
- }
-
- return null;
- }
-
- private static long GetLongValue(IReadOnlyDictionary<string, string> values, string name)
- {
- var value = values.GetValueOrDefault(name);
-
- if (long.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
- {
- return result;
- }
-
- return 0;
- }
-
- /// <inheritdoc />
- public Task SendMessage<T>(string name, Guid messageId, T data, CancellationToken cancellationToken)
- {
- if (_disposed)
- {
- throw new ObjectDisposedException(GetType().Name);
- }
-
- if (_device == null)
- {
- return Task.CompletedTask;
- }
-
- if (string.Equals(name, "Play", StringComparison.OrdinalIgnoreCase))
- {
- return SendPlayCommand(data as PlayRequest, cancellationToken);
- }
-
- if (string.Equals(name, "PlayState", StringComparison.OrdinalIgnoreCase))
- {
- return SendPlaystateCommand(data as PlaystateRequest, cancellationToken);
- }
-
- if (string.Equals(name, "GeneralCommand", StringComparison.OrdinalIgnoreCase))
- {
- return SendGeneralCommand(data as GeneralCommand, cancellationToken);
- }
-
- // Not supported or needed right now
- return Task.CompletedTask;
- }
}
}
diff --git a/Emby.Dlna/PlayTo/PlayToManager.cs b/Emby.Dlna/PlayTo/PlayToManager.cs
index 512589e4d..ff801f263 100644
--- a/Emby.Dlna/PlayTo/PlayToManager.cs
+++ b/Emby.Dlna/PlayTo/PlayToManager.cs
@@ -6,6 +6,7 @@ using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Events;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
@@ -16,7 +17,6 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Session;
using Microsoft.Extensions.Logging;
@@ -92,7 +92,7 @@ namespace Emby.Dlna.PlayTo
// It has to report that it's a media renderer
if (usn.IndexOf("MediaRenderer:", StringComparison.OrdinalIgnoreCase) == -1 &&
- nt.IndexOf("MediaRenderer:", StringComparison.OrdinalIgnoreCase) == -1)
+ nt.IndexOf("MediaRenderer:", StringComparison.OrdinalIgnoreCase) == -1)
{
// _logger.LogDebug("Upnp device {0} does not contain a MediaRenderer device (0).", location);
return;
@@ -174,7 +174,7 @@ namespace Emby.Dlna.PlayTo
if (controller == null)
{
- var device = await Device.CreateuPnpDeviceAsync(uri, _httpClient, _config, _logger, cancellationToken).ConfigureAwait(false);
+ var device = await Device.CreateuPnpDeviceAsync(uri, _httpClient, _logger, cancellationToken).ConfigureAwait(false);
string deviceName = device.Properties.Name;
@@ -192,20 +192,20 @@ namespace Emby.Dlna.PlayTo
controller = new PlayToController(
sessionInfo,
- _sessionManager,
- _libraryManager,
- _logger,
- _dlnaManager,
- _userManager,
- _imageProcessor,
- serverAddress,
- null,
- _deviceDiscovery,
- _userDataManager,
- _localization,
- _mediaSourceManager,
- _config,
- _mediaEncoder);
+ _sessionManager,
+ _libraryManager,
+ _logger,
+ _dlnaManager,
+ _userManager,
+ _imageProcessor,
+ serverAddress,
+ null,
+ _deviceDiscovery,
+ _userDataManager,
+ _localization,
+ _mediaSourceManager,
+ _config,
+ _mediaEncoder);
sessionInfo.AddController(controller);
@@ -218,17 +218,17 @@ namespace Emby.Dlna.PlayTo
{
PlayableMediaTypes = profile.GetSupportedMediaTypes(),
- SupportedCommands = new string[]
+ SupportedCommands = new[]
{
- GeneralCommandType.VolumeDown.ToString(),
- GeneralCommandType.VolumeUp.ToString(),
- GeneralCommandType.Mute.ToString(),
- GeneralCommandType.Unmute.ToString(),
- GeneralCommandType.ToggleMute.ToString(),
- GeneralCommandType.SetVolume.ToString(),
- GeneralCommandType.SetAudioStreamIndex.ToString(),
- GeneralCommandType.SetSubtitleStreamIndex.ToString(),
- GeneralCommandType.PlayMediaSource.ToString()
+ GeneralCommandType.VolumeDown.ToString(),
+ GeneralCommandType.VolumeUp.ToString(),
+ GeneralCommandType.Mute.ToString(),
+ GeneralCommandType.Unmute.ToString(),
+ GeneralCommandType.ToggleMute.ToString(),
+ GeneralCommandType.SetVolume.ToString(),
+ GeneralCommandType.SetAudioStreamIndex.ToString(),
+ GeneralCommandType.SetSubtitleStreamIndex.ToString(),
+ GeneralCommandType.PlayMediaSource.ToString()
},
SupportsMediaControl = true
@@ -247,8 +247,9 @@ namespace Emby.Dlna.PlayTo
{
_disposeCancellationTokenSource.Cancel();
}
- catch
+ catch (Exception ex)
{
+ _logger.LogDebug(ex, "Error while disposing PlayToManager");
}
_sessionLock.Dispose();
diff --git a/Emby.Dlna/PlayTo/PlaybackProgressEventArgs.cs b/Emby.Dlna/PlayTo/PlaybackProgressEventArgs.cs
index 795618df2..d14617c8a 100644
--- a/Emby.Dlna/PlayTo/PlaybackProgressEventArgs.cs
+++ b/Emby.Dlna/PlayTo/PlaybackProgressEventArgs.cs
@@ -6,6 +6,6 @@ namespace Emby.Dlna.PlayTo
{
public class PlaybackProgressEventArgs : EventArgs
{
- public uBaseObject MediaInfo { get; set; }
+ public UBaseObject MediaInfo { get; set; }
}
}
diff --git a/Emby.Dlna/PlayTo/PlaybackStartEventArgs.cs b/Emby.Dlna/PlayTo/PlaybackStartEventArgs.cs
index 27883ca32..3f8d55263 100644
--- a/Emby.Dlna/PlayTo/PlaybackStartEventArgs.cs
+++ b/Emby.Dlna/PlayTo/PlaybackStartEventArgs.cs
@@ -6,6 +6,6 @@ namespace Emby.Dlna.PlayTo
{
public class PlaybackStartEventArgs : EventArgs
{
- public uBaseObject MediaInfo { get; set; }
+ public UBaseObject MediaInfo { get; set; }
}
}
diff --git a/Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs b/Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs
index fa42b80e8..deeb47918 100644
--- a/Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs
+++ b/Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs
@@ -6,13 +6,6 @@ namespace Emby.Dlna.PlayTo
{
public class PlaybackStoppedEventArgs : EventArgs
{
- public uBaseObject MediaInfo { get; set; }
- }
-
- public class MediaChangedEventArgs : EventArgs
- {
- public uBaseObject OldMediaInfo { get; set; }
-
- public uBaseObject NewMediaInfo { get; set; }
+ public UBaseObject MediaInfo { get; set; }
}
}
diff --git a/Emby.Dlna/PlayTo/TRANSPORTSTATE.cs b/Emby.Dlna/PlayTo/TRANSPORTSTATE.cs
deleted file mode 100644
index 7daefeca8..000000000
--- a/Emby.Dlna/PlayTo/TRANSPORTSTATE.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-#pragma warning disable CS1591
-
-namespace Emby.Dlna.PlayTo
-{
- public enum TRANSPORTSTATE
- {
- STOPPED,
- PLAYING,
- TRANSITIONING,
- PAUSED_PLAYBACK,
- PAUSED
- }
-}
diff --git a/Emby.Dlna/PlayTo/TransportCommands.cs b/Emby.Dlna/PlayTo/TransportCommands.cs
index c0ce3ab6e..fda17a8b4 100644
--- a/Emby.Dlna/PlayTo/TransportCommands.cs
+++ b/Emby.Dlna/PlayTo/TransportCommands.cs
@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
+using System.Globalization;
using System.Linq;
using System.Xml.Linq;
using Emby.Dlna.Common;
@@ -11,36 +12,30 @@ namespace Emby.Dlna.PlayTo
{
public class TransportCommands
{
+ private const string CommandBase = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" + "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" + "<SOAP-ENV:Body>" + "<m:{0} xmlns:m=\"{1}\">" + "{2}" + "</m:{0}>" + "</SOAP-ENV:Body></SOAP-ENV:Envelope>";
private List<StateVariable> _stateVariables = new List<StateVariable>();
- public List<StateVariable> StateVariables
- {
- get => _stateVariables;
- set => _stateVariables = value;
- }
-
private List<ServiceAction> _serviceActions = new List<ServiceAction>();
- public List<ServiceAction> ServiceActions
- {
- get => _serviceActions;
- set => _serviceActions = value;
- }
+
+ public List<StateVariable> StateVariables => _stateVariables;
+
+ public List<ServiceAction> ServiceActions => _serviceActions;
public static TransportCommands Create(XDocument document)
{
var command = new TransportCommands();
- var actionList = document.Descendants(uPnpNamespaces.svc + "actionList");
+ var actionList = document.Descendants(UPnpNamespaces.Svc + "actionList");
- foreach (var container in actionList.Descendants(uPnpNamespaces.svc + "action"))
+ foreach (var container in actionList.Descendants(UPnpNamespaces.Svc + "action"))
{
command.ServiceActions.Add(ServiceActionFromXml(container));
}
- var stateValues = document.Descendants(uPnpNamespaces.ServiceStateTable).FirstOrDefault();
+ var stateValues = document.Descendants(UPnpNamespaces.ServiceStateTable).FirstOrDefault();
if (stateValues != null)
{
- foreach (var container in stateValues.Elements(uPnpNamespaces.svc + "stateVariable"))
+ foreach (var container in stateValues.Elements(UPnpNamespaces.Svc + "stateVariable"))
{
command.StateVariables.Add(FromXml(container));
}
@@ -51,19 +46,19 @@ namespace Emby.Dlna.PlayTo
private static ServiceAction ServiceActionFromXml(XElement container)
{
- var argumentList = new List<Argument>();
+ var serviceAction = new ServiceAction
+ {
+ Name = container.GetValue(UPnpNamespaces.Svc + "name"),
+ };
- foreach (var arg in container.Descendants(uPnpNamespaces.svc + "argument"))
+ var argumentList = serviceAction.ArgumentList;
+
+ foreach (var arg in container.Descendants(UPnpNamespaces.Svc + "argument"))
{
argumentList.Add(ArgumentFromXml(arg));
}
- return new ServiceAction
- {
- Name = container.GetValue(uPnpNamespaces.svc + "name"),
-
- ArgumentList = argumentList
- };
+ return serviceAction;
}
private static Argument ArgumentFromXml(XElement container)
@@ -75,29 +70,29 @@ namespace Emby.Dlna.PlayTo
return new Argument
{
- Name = container.GetValue(uPnpNamespaces.svc + "name"),
- Direction = container.GetValue(uPnpNamespaces.svc + "direction"),
- RelatedStateVariable = container.GetValue(uPnpNamespaces.svc + "relatedStateVariable")
+ Name = container.GetValue(UPnpNamespaces.Svc + "name"),
+ Direction = container.GetValue(UPnpNamespaces.Svc + "direction"),
+ RelatedStateVariable = container.GetValue(UPnpNamespaces.Svc + "relatedStateVariable")
};
}
private static StateVariable FromXml(XElement container)
{
var allowedValues = new List<string>();
- var element = container.Descendants(uPnpNamespaces.svc + "allowedValueList")
+ var element = container.Descendants(UPnpNamespaces.Svc + "allowedValueList")
.FirstOrDefault();
if (element != null)
{
- var values = element.Descendants(uPnpNamespaces.svc + "allowedValue");
+ var values = element.Descendants(UPnpNamespaces.Svc + "allowedValue");
allowedValues.AddRange(values.Select(child => child.Value));
}
return new StateVariable
{
- Name = container.GetValue(uPnpNamespaces.svc + "name"),
- DataType = container.GetValue(uPnpNamespaces.svc + "dataType"),
+ Name = container.GetValue(UPnpNamespaces.Svc + "name"),
+ DataType = container.GetValue(UPnpNamespaces.Svc + "dataType"),
AllowedValues = allowedValues.ToArray()
};
}
@@ -123,7 +118,7 @@ namespace Emby.Dlna.PlayTo
}
}
- return string.Format(CommandBase, action.Name, xmlNamespace, stateString);
+ return string.Format(CultureInfo.InvariantCulture, CommandBase, action.Name, xmlNamespace, stateString);
}
public string BuildPost(ServiceAction action, string xmlNamesapce, object value, string commandParameter = "")
@@ -147,7 +142,7 @@ namespace Emby.Dlna.PlayTo
}
}
- return string.Format(CommandBase, action.Name, xmlNamesapce, stateString);
+ return string.Format(CultureInfo.InvariantCulture, CommandBase, action.Name, xmlNamesapce, stateString);
}
public string BuildPost(ServiceAction action, string xmlNamesapce, object value, Dictionary<string, string> dictionary)
@@ -170,7 +165,7 @@ namespace Emby.Dlna.PlayTo
}
}
- return string.Format(CommandBase, action.Name, xmlNamesapce, stateString);
+ return string.Format(CultureInfo.InvariantCulture, CommandBase, action.Name, xmlNamesapce, stateString);
}
private string BuildArgumentXml(Argument argument, string value, string commandParameter = "")
@@ -180,15 +175,12 @@ namespace Emby.Dlna.PlayTo
if (state != null)
{
var sendValue = state.AllowedValues.FirstOrDefault(a => string.Equals(a, commandParameter, StringComparison.OrdinalIgnoreCase)) ??
- state.AllowedValues.FirstOrDefault() ??
- value;
+ (state.AllowedValues.Count > 0 ? state.AllowedValues[0] : value);
- return string.Format("<{0} xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"{1}\">{2}</{0}>", argument.Name, state.DataType ?? "string", sendValue);
+ return string.Format(CultureInfo.InvariantCulture, "<{0} xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"{1}\">{2}</{0}>", argument.Name, state.DataType ?? "string", sendValue);
}
- return string.Format("<{0}>{1}</{0}>", argument.Name, value);
+ return string.Format(CultureInfo.InvariantCulture, "<{0}>{1}</{0}>", argument.Name, value);
}
-
- private const string CommandBase = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" + "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" + "<SOAP-ENV:Body>" + "<m:{0} xmlns:m=\"{1}\">" + "{2}" + "</m:{0}>" + "</SOAP-ENV:Body></SOAP-ENV:Envelope>";
}
}
diff --git a/Emby.Dlna/PlayTo/TransportState.cs b/Emby.Dlna/PlayTo/TransportState.cs
new file mode 100644
index 000000000..7068a5d24
--- /dev/null
+++ b/Emby.Dlna/PlayTo/TransportState.cs
@@ -0,0 +1,14 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1602
+
+namespace Emby.Dlna.PlayTo
+{
+ public enum TransportState
+ {
+ Stopped,
+ Playing,
+ Transitioning,
+ PausedPlayback,
+ Paused
+ }
+}
diff --git a/Emby.Dlna/PlayTo/UpnpContainer.cs b/Emby.Dlna/PlayTo/UpnpContainer.cs
index e2d7a10f0..05f27603f 100644
--- a/Emby.Dlna/PlayTo/UpnpContainer.cs
+++ b/Emby.Dlna/PlayTo/UpnpContainer.cs
@@ -6,22 +6,22 @@ using Emby.Dlna.Ssdp;
namespace Emby.Dlna.PlayTo
{
- public class UpnpContainer : uBaseObject
+ public class UpnpContainer : UBaseObject
{
- public static uBaseObject Create(XElement container)
+ public static UBaseObject Create(XElement container)
{
if (container == null)
{
throw new ArgumentNullException(nameof(container));
}
- return new uBaseObject
+ return new UBaseObject
{
- Id = container.GetAttributeValue(uPnpNamespaces.Id),
- ParentId = container.GetAttributeValue(uPnpNamespaces.ParentId),
- Title = container.GetValue(uPnpNamespaces.title),
- IconUrl = container.GetValue(uPnpNamespaces.Artwork),
- UpnpClass = container.GetValue(uPnpNamespaces.uClass)
+ Id = container.GetAttributeValue(UPnpNamespaces.Id),
+ ParentId = container.GetAttributeValue(UPnpNamespaces.ParentId),
+ Title = container.GetValue(UPnpNamespaces.Title),
+ IconUrl = container.GetValue(UPnpNamespaces.Artwork),
+ UpnpClass = container.GetValue(UPnpNamespaces.Class)
};
}
}
diff --git a/Emby.Dlna/PlayTo/uBaseObject.cs b/Emby.Dlna/PlayTo/uBaseObject.cs
index 05c19299f..0d9478e42 100644
--- a/Emby.Dlna/PlayTo/uBaseObject.cs
+++ b/Emby.Dlna/PlayTo/uBaseObject.cs
@@ -1,10 +1,11 @@
#pragma warning disable CS1591
using System;
+using System.Collections.Generic;
namespace Emby.Dlna.PlayTo
{
- public class uBaseObject
+ public class UBaseObject
{
public string Id { get; set; }
@@ -20,20 +21,10 @@ namespace Emby.Dlna.PlayTo
public string Url { get; set; }
- public string[] ProtocolInfo { get; set; }
+ public IReadOnlyList<string> ProtocolInfo { get; set; }
public string UpnpClass { get; set; }
- public bool Equals(uBaseObject obj)
- {
- if (obj == null)
- {
- throw new ArgumentNullException(nameof(obj));
- }
-
- return string.Equals(Id, obj.Id);
- }
-
public string MediaType
{
get
@@ -58,5 +49,15 @@ namespace Emby.Dlna.PlayTo
return null;
}
}
+
+ public bool Equals(UBaseObject obj)
+ {
+ if (obj == null)
+ {
+ throw new ArgumentNullException(nameof(obj));
+ }
+
+ return string.Equals(Id, obj.Id, StringComparison.Ordinal);
+ }
}
}
diff --git a/Emby.Dlna/PlayTo/uPnpNamespaces.cs b/Emby.Dlna/PlayTo/uPnpNamespaces.cs
index dc65cdf43..5042d4493 100644
--- a/Emby.Dlna/PlayTo/uPnpNamespaces.cs
+++ b/Emby.Dlna/PlayTo/uPnpNamespaces.cs
@@ -4,38 +4,64 @@ using System.Xml.Linq;
namespace Emby.Dlna.PlayTo
{
- public class uPnpNamespaces
+ public static class UPnpNamespaces
{
- public static XNamespace dc = "http://purl.org/dc/elements/1.1/";
- public static XNamespace ns = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/";
- public static XNamespace svc = "urn:schemas-upnp-org:service-1-0";
- public static XNamespace ud = "urn:schemas-upnp-org:device-1-0";
- public static XNamespace upnp = "urn:schemas-upnp-org:metadata-1-0/upnp/";
- public static XNamespace RenderingControl = "urn:schemas-upnp-org:service:RenderingControl:1";
- public static XNamespace AvTransport = "urn:schemas-upnp-org:service:AVTransport:1";
- public static XNamespace ContentDirectory = "urn:schemas-upnp-org:service:ContentDirectory:1";
-
- public static XName containers = ns + "container";
- public static XName items = ns + "item";
- public static XName title = dc + "title";
- public static XName creator = dc + "creator";
- public static XName artist = upnp + "artist";
- public static XName Id = "id";
- public static XName ParentId = "parentID";
- public static XName uClass = upnp + "class";
- public static XName Artwork = upnp + "albumArtURI";
- public static XName Description = dc + "description";
- public static XName LongDescription = upnp + "longDescription";
- public static XName Album = upnp + "album";
- public static XName Author = upnp + "author";
- public static XName Director = upnp + "director";
- public static XName PlayCount = upnp + "playbackCount";
- public static XName Tracknumber = upnp + "originalTrackNumber";
- public static XName Res = ns + "res";
- public static XName Duration = "duration";
- public static XName ProtocolInfo = "protocolInfo";
-
- public static XName ServiceStateTable = svc + "serviceStateTable";
- public static XName StateVariable = svc + "stateVariable";
+ public static XNamespace Dc { get; } = "http://purl.org/dc/elements/1.1/";
+
+ public static XNamespace Ns { get; } = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/";
+
+ public static XNamespace Svc { get; } = "urn:schemas-upnp-org:service-1-0";
+
+ public static XNamespace Ud { get; } = "urn:schemas-upnp-org:device-1-0";
+
+ public static XNamespace UPnp { get; } = "urn:schemas-upnp-org:metadata-1-0/upnp/";
+
+ public static XNamespace RenderingControl { get; } = "urn:schemas-upnp-org:service:RenderingControl:1";
+
+ public static XNamespace AvTransport { get; } = "urn:schemas-upnp-org:service:AVTransport:1";
+
+ public static XNamespace ContentDirectory { get; } = "urn:schemas-upnp-org:service:ContentDirectory:1";
+
+ public static XName Containers { get; } = Ns + "container";
+
+ public static XName Items { get; } = Ns + "item";
+
+ public static XName Title { get; } = Dc + "title";
+
+ public static XName Creator { get; } = Dc + "creator";
+
+ public static XName Artist { get; } = UPnp + "artist";
+
+ public static XName Id { get; } = "id";
+
+ public static XName ParentId { get; } = "parentID";
+
+ public static XName Class { get; } = UPnp + "class";
+
+ public static XName Artwork { get; } = UPnp + "albumArtURI";
+
+ public static XName Description { get; } = Dc + "description";
+
+ public static XName LongDescription { get; } = UPnp + "longDescription";
+
+ public static XName Album { get; } = UPnp + "album";
+
+ public static XName Author { get; } = UPnp + "author";
+
+ public static XName Director { get; } = UPnp + "director";
+
+ public static XName PlayCount { get; } = UPnp + "playbackCount";
+
+ public static XName Tracknumber { get; } = UPnp + "originalTrackNumber";
+
+ public static XName Res { get; } = Ns + "res";
+
+ public static XName Duration { get; } = "duration";
+
+ public static XName ProtocolInfo { get; } = "protocolInfo";
+
+ public static XName ServiceStateTable { get; } = Svc + "serviceStateTable";
+
+ public static XName StateVariable { get; } = Svc + "stateVariable";
}
}
diff --git a/Emby.Dlna/Profiles/DefaultProfile.cs b/Emby.Dlna/Profiles/DefaultProfile.cs
index 90a23a4a2..d4af72b62 100644
--- a/Emby.Dlna/Profiles/DefaultProfile.cs
+++ b/Emby.Dlna/Profiles/DefaultProfile.cs
@@ -64,14 +64,14 @@ namespace Emby.Dlna.Profiles
new DirectPlayProfile
{
// play all
- Container = "",
+ Container = string.Empty,
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
// play all
- Container = "",
+ Container = string.Empty,
Type = DlnaProfileType.Audio
}
};
diff --git a/Emby.Dlna/Profiles/DishHopperJoeyProfile.cs b/Emby.Dlna/Profiles/DishHopperJoeyProfile.cs
index 942e36930..2a7524a6a 100644
--- a/Emby.Dlna/Profiles/DishHopperJoeyProfile.cs
+++ b/Emby.Dlna/Profiles/DishHopperJoeyProfile.cs
@@ -24,7 +24,7 @@ namespace Emby.Dlna.Profiles
{
Match = HeaderMatchType.Substring,
Name = "User-Agent",
- Value ="Zip_"
+ Value = "Zip_"
}
}
};
@@ -81,7 +81,7 @@ namespace Emby.Dlna.Profiles
{
Type = CodecType.Video,
Codec = "h264",
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -124,7 +124,7 @@ namespace Emby.Dlna.Profiles
new CodecProfile
{
Type = CodecType.Video,
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -161,7 +161,7 @@ namespace Emby.Dlna.Profiles
{
Type = CodecType.VideoAudio,
Codec = "ac3,he-aac",
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -177,7 +177,7 @@ namespace Emby.Dlna.Profiles
{
Type = CodecType.VideoAudio,
Codec = "aac",
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -192,7 +192,7 @@ namespace Emby.Dlna.Profiles
new CodecProfile
{
Type = CodecType.VideoAudio,
- Conditions = new []
+ Conditions = new[]
{
// The device does not have any audio switching capabilities
new ProfileCondition
diff --git a/Emby.Dlna/Profiles/LgTvProfile.cs b/Emby.Dlna/Profiles/LgTvProfile.cs
index 02301764c..fbb368d3e 100644
--- a/Emby.Dlna/Profiles/LgTvProfile.cs
+++ b/Emby.Dlna/Profiles/LgTvProfile.cs
@@ -84,7 +84,7 @@ namespace Emby.Dlna.Profiles
{
Type = DlnaProfileType.Photo,
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -191,7 +191,7 @@ namespace Emby.Dlna.Profiles
}
};
- ResponseProfiles = new ResponseProfile[]
+ ResponseProfiles = new[]
{
new ResponseProfile
{
diff --git a/Emby.Dlna/Profiles/LinksysDMA2100Profile.cs b/Emby.Dlna/Profiles/LinksysDMA2100Profile.cs
index 1b1423520..1a510dfec 100644
--- a/Emby.Dlna/Profiles/LinksysDMA2100Profile.cs
+++ b/Emby.Dlna/Profiles/LinksysDMA2100Profile.cs
@@ -32,7 +32,7 @@ namespace Emby.Dlna.Profiles
}
};
- ResponseProfiles = new ResponseProfile[]
+ ResponseProfiles = new[]
{
new ResponseProfile
{
diff --git a/Emby.Dlna/Profiles/PanasonicVieraProfile.cs b/Emby.Dlna/Profiles/PanasonicVieraProfile.cs
index 44c35e142..0d536acf3 100644
--- a/Emby.Dlna/Profiles/PanasonicVieraProfile.cs
+++ b/Emby.Dlna/Profiles/PanasonicVieraProfile.cs
@@ -138,7 +138,7 @@ namespace Emby.Dlna.Profiles
{
Type = DlnaProfileType.Photo,
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
diff --git a/Emby.Dlna/Profiles/PopcornHourProfile.cs b/Emby.Dlna/Profiles/PopcornHourProfile.cs
index 9e9f6966f..7fbf8c164 100644
--- a/Emby.Dlna/Profiles/PopcornHourProfile.cs
+++ b/Emby.Dlna/Profiles/PopcornHourProfile.cs
@@ -93,8 +93,8 @@ namespace Emby.Dlna.Profiles
new CodecProfile
{
Type = CodecType.Video,
- Codec="h264",
- Conditions = new []
+ Codec = "h264",
+ Conditions = new[]
{
new ProfileCondition(ProfileConditionType.EqualsAny, ProfileConditionValue.VideoProfile, "baseline|constrained baseline"),
new ProfileCondition
@@ -122,7 +122,7 @@ namespace Emby.Dlna.Profiles
new CodecProfile
{
Type = CodecType.Video,
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -150,7 +150,7 @@ namespace Emby.Dlna.Profiles
{
Type = CodecType.VideoAudio,
Codec = "aac",
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -166,7 +166,7 @@ namespace Emby.Dlna.Profiles
{
Type = CodecType.Audio,
Codec = "aac",
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -182,7 +182,7 @@ namespace Emby.Dlna.Profiles
{
Type = CodecType.Audio,
Codec = "mp3",
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -202,7 +202,7 @@ namespace Emby.Dlna.Profiles
}
};
- ResponseProfiles = new ResponseProfile[]
+ ResponseProfiles = new[]
{
new ResponseProfile
{
diff --git a/Emby.Dlna/Profiles/SamsungSmartTvProfile.cs b/Emby.Dlna/Profiles/SamsungSmartTvProfile.cs
index 4ff2ab9be..ddbebba2a 100644
--- a/Emby.Dlna/Profiles/SamsungSmartTvProfile.cs
+++ b/Emby.Dlna/Profiles/SamsungSmartTvProfile.cs
@@ -139,7 +139,7 @@ namespace Emby.Dlna.Profiles
{
Type = DlnaProfileType.Photo,
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
diff --git a/Emby.Dlna/Profiles/SonyBlurayPlayer2013.cs b/Emby.Dlna/Profiles/SonyBlurayPlayer2013.cs
index 238fe9f6b..765c12504 100644
--- a/Emby.Dlna/Profiles/SonyBlurayPlayer2013.cs
+++ b/Emby.Dlna/Profiles/SonyBlurayPlayer2013.cs
@@ -150,7 +150,7 @@ namespace Emby.Dlna.Profiles
{
Type = CodecType.Video,
Codec = "h264",
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -178,7 +178,7 @@ namespace Emby.Dlna.Profiles
{
Type = CodecType.VideoAudio,
Codec = "ac3",
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -197,7 +197,7 @@ namespace Emby.Dlna.Profiles
{
Type = DlnaProfileType.Photo,
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
diff --git a/Emby.Dlna/Profiles/SonyBlurayPlayer2014.cs b/Emby.Dlna/Profiles/SonyBlurayPlayer2014.cs
index 812a48151..390c8a3e4 100644
--- a/Emby.Dlna/Profiles/SonyBlurayPlayer2014.cs
+++ b/Emby.Dlna/Profiles/SonyBlurayPlayer2014.cs
@@ -150,7 +150,7 @@ namespace Emby.Dlna.Profiles
{
Type = CodecType.Video,
Codec = "h264",
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -178,7 +178,7 @@ namespace Emby.Dlna.Profiles
{
Type = CodecType.VideoAudio,
Codec = "ac3",
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -197,7 +197,7 @@ namespace Emby.Dlna.Profiles
{
Type = DlnaProfileType.Photo,
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
diff --git a/Emby.Dlna/Profiles/SonyBlurayPlayer2015.cs b/Emby.Dlna/Profiles/SonyBlurayPlayer2015.cs
index 6bfff322e..25adc4d02 100644
--- a/Emby.Dlna/Profiles/SonyBlurayPlayer2015.cs
+++ b/Emby.Dlna/Profiles/SonyBlurayPlayer2015.cs
@@ -138,7 +138,7 @@ namespace Emby.Dlna.Profiles
{
Type = CodecType.Video,
Codec = "h264",
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -166,7 +166,7 @@ namespace Emby.Dlna.Profiles
{
Type = CodecType.VideoAudio,
Codec = "ac3",
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -185,7 +185,7 @@ namespace Emby.Dlna.Profiles
{
Type = DlnaProfileType.Photo,
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
diff --git a/Emby.Dlna/Profiles/SonyBlurayPlayer2016.cs b/Emby.Dlna/Profiles/SonyBlurayPlayer2016.cs
index ec2529574..0a39a5f40 100644
--- a/Emby.Dlna/Profiles/SonyBlurayPlayer2016.cs
+++ b/Emby.Dlna/Profiles/SonyBlurayPlayer2016.cs
@@ -138,7 +138,7 @@ namespace Emby.Dlna.Profiles
{
Type = CodecType.Video,
Codec = "h264",
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -166,7 +166,7 @@ namespace Emby.Dlna.Profiles
{
Type = CodecType.VideoAudio,
Codec = "ac3",
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -185,7 +185,7 @@ namespace Emby.Dlna.Profiles
{
Type = DlnaProfileType.Photo,
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
diff --git a/Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs b/Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs
index ecdd2e7a4..05c8ab1c1 100644
--- a/Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs
+++ b/Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs
@@ -114,7 +114,7 @@ namespace Emby.Dlna.Profiles
{
Type = CodecType.Video,
Codec = "h264",
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -156,7 +156,7 @@ namespace Emby.Dlna.Profiles
{
Type = CodecType.VideoAudio,
Codec = "ac3",
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -172,7 +172,7 @@ namespace Emby.Dlna.Profiles
{
Type = CodecType.VideoAudio,
Codec = "aac",
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -191,7 +191,7 @@ namespace Emby.Dlna.Profiles
{
Type = DlnaProfileType.Photo,
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -217,7 +217,7 @@ namespace Emby.Dlna.Profiles
VideoCodec = "h264,mpeg4,vc1",
AudioCodec = "ac3,aac,mp3",
MimeType = "video/vnd.dlna.mpeg-tts",
- OrgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",
+ OrgPn = "MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",
Type = DlnaProfileType.Video
},
diff --git a/Emby.Dlna/Profiles/SonyBravia2010Profile.cs b/Emby.Dlna/Profiles/SonyBravia2010Profile.cs
index 68365ba4a..8ab4acd1b 100644
--- a/Emby.Dlna/Profiles/SonyBravia2010Profile.cs
+++ b/Emby.Dlna/Profiles/SonyBravia2010Profile.cs
@@ -102,13 +102,13 @@ namespace Emby.Dlna.Profiles
new ResponseProfile
{
Container = "ts,mpegts",
- VideoCodec="h264",
- AudioCodec="ac3,aac,mp3",
+ VideoCodec = "h264",
+ AudioCodec = "ac3,aac,mp3",
MimeType = "video/vnd.dlna.mpeg-tts",
- OrgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T",
+ OrgPn = "AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T",
Type = DlnaProfileType.Video,
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -128,13 +128,13 @@ namespace Emby.Dlna.Profiles
new ResponseProfile
{
Container = "ts,mpegts",
- VideoCodec="h264",
- AudioCodec="ac3,aac,mp3",
+ VideoCodec = "h264",
+ AudioCodec = "ac3,aac,mp3",
MimeType = "video/mpeg",
- OrgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO",
+ OrgPn = "AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO",
Type = DlnaProfileType.Video,
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -148,28 +148,28 @@ namespace Emby.Dlna.Profiles
new ResponseProfile
{
Container = "ts,mpegts",
- VideoCodec="h264",
- AudioCodec="ac3,aac,mp3",
+ VideoCodec = "h264",
+ AudioCodec = "ac3,aac,mp3",
MimeType = "video/vnd.dlna.mpeg-tts",
- OrgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU",
+ OrgPn = "AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU",
Type = DlnaProfileType.Video
},
new ResponseProfile
{
Container = "ts,mpegts",
- VideoCodec="mpeg2video",
+ VideoCodec = "mpeg2video",
MimeType = "video/vnd.dlna.mpeg-tts",
- OrgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",
+ OrgPn = "MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",
Type = DlnaProfileType.Video
},
new ResponseProfile
{
Container = "mpeg",
- VideoCodec="mpeg1video,mpeg2video",
+ VideoCodec = "mpeg1video,mpeg2video",
MimeType = "video/mpeg",
- OrgPn="MPEG_PS_NTSC,MPEG_PS_PAL",
+ OrgPn = "MPEG_PS_NTSC,MPEG_PS_PAL",
Type = DlnaProfileType.Video
}
};
@@ -180,7 +180,7 @@ namespace Emby.Dlna.Profiles
{
Type = DlnaProfileType.Photo,
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -204,7 +204,7 @@ namespace Emby.Dlna.Profiles
{
Type = CodecType.Video,
Codec = "h264",
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -243,7 +243,7 @@ namespace Emby.Dlna.Profiles
{
Type = CodecType.Video,
Codec = "mpeg2video",
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -275,7 +275,7 @@ namespace Emby.Dlna.Profiles
new CodecProfile
{
Type = CodecType.Video,
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -303,7 +303,7 @@ namespace Emby.Dlna.Profiles
Type = CodecType.VideoAudio,
Codec = "ac3",
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -319,7 +319,7 @@ namespace Emby.Dlna.Profiles
Type = CodecType.VideoAudio,
Codec = "aac",
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -341,7 +341,7 @@ namespace Emby.Dlna.Profiles
Type = CodecType.VideoAudio,
Codec = "mp3,mp2",
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
diff --git a/Emby.Dlna/Profiles/SonyBravia2011Profile.cs b/Emby.Dlna/Profiles/SonyBravia2011Profile.cs
index b34af04a5..42d253394 100644
--- a/Emby.Dlna/Profiles/SonyBravia2011Profile.cs
+++ b/Emby.Dlna/Profiles/SonyBravia2011Profile.cs
@@ -120,7 +120,7 @@ namespace Emby.Dlna.Profiles
{
Type = DlnaProfileType.Photo,
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -143,13 +143,13 @@ namespace Emby.Dlna.Profiles
new ResponseProfile
{
Container = "ts,mpegts",
- VideoCodec="h264",
- AudioCodec="ac3,aac,mp3",
+ VideoCodec = "h264",
+ AudioCodec = "ac3,aac,mp3",
MimeType = "video/vnd.dlna.mpeg-tts",
- OrgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T",
+ OrgPn = "AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T",
Type = DlnaProfileType.Video,
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -169,13 +169,13 @@ namespace Emby.Dlna.Profiles
new ResponseProfile
{
Container = "ts,mpegts",
- VideoCodec="h264",
- AudioCodec="ac3,aac,mp3",
+ VideoCodec = "h264",
+ AudioCodec = "ac3,aac,mp3",
MimeType = "video/mpeg",
- OrgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO",
+ OrgPn = "AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO",
Type = DlnaProfileType.Video,
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -189,28 +189,28 @@ namespace Emby.Dlna.Profiles
new ResponseProfile
{
Container = "ts,mpegts",
- VideoCodec="h264",
- AudioCodec="ac3,aac,mp3",
+ VideoCodec = "h264",
+ AudioCodec = "ac3,aac,mp3",
MimeType = "video/vnd.dlna.mpeg-tts",
- OrgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU",
+ OrgPn = "AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU",
Type = DlnaProfileType.Video
},
new ResponseProfile
{
Container = "ts,mpegts",
- VideoCodec="mpeg2video",
+ VideoCodec = "mpeg2video",
MimeType = "video/vnd.dlna.mpeg-tts",
- OrgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",
+ OrgPn = "MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",
Type = DlnaProfileType.Video
},
new ResponseProfile
{
Container = "mpeg",
- VideoCodec="mpeg1video,mpeg2video",
+ VideoCodec = "mpeg1video,mpeg2video",
MimeType = "video/mpeg",
- OrgPn="MPEG_PS_NTSC,MPEG_PS_PAL",
+ OrgPn = "MPEG_PS_NTSC,MPEG_PS_PAL",
Type = DlnaProfileType.Video
},
new ResponseProfile
@@ -227,7 +227,7 @@ namespace Emby.Dlna.Profiles
{
Type = CodecType.Video,
Codec = "h264",
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -266,7 +266,7 @@ namespace Emby.Dlna.Profiles
{
Type = CodecType.Video,
Codec = "mpeg2video",
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -298,7 +298,7 @@ namespace Emby.Dlna.Profiles
new CodecProfile
{
Type = CodecType.Video,
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -326,7 +326,7 @@ namespace Emby.Dlna.Profiles
Type = CodecType.VideoAudio,
Codec = "ac3",
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -364,7 +364,7 @@ namespace Emby.Dlna.Profiles
Type = CodecType.VideoAudio,
Codec = "mp3,mp2",
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
diff --git a/Emby.Dlna/Profiles/SonyBravia2012Profile.cs b/Emby.Dlna/Profiles/SonyBravia2012Profile.cs
index 0e75d0cb5..0598e8342 100644
--- a/Emby.Dlna/Profiles/SonyBravia2012Profile.cs
+++ b/Emby.Dlna/Profiles/SonyBravia2012Profile.cs
@@ -131,13 +131,13 @@ namespace Emby.Dlna.Profiles
new ResponseProfile
{
Container = "ts,mpegts",
- VideoCodec="h264",
- AudioCodec="ac3,aac,mp3",
+ VideoCodec = "h264",
+ AudioCodec = "ac3,aac,mp3",
MimeType = "video/vnd.dlna.mpeg-tts",
- OrgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T",
+ OrgPn = "AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T",
Type = DlnaProfileType.Video,
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -157,13 +157,13 @@ namespace Emby.Dlna.Profiles
new ResponseProfile
{
Container = "ts,mpegts",
- VideoCodec="h264",
- AudioCodec="ac3,aac,mp3",
+ VideoCodec = "h264",
+ AudioCodec = "ac3,aac,mp3",
MimeType = "video/mpeg",
- OrgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO",
+ OrgPn = "AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO",
Type = DlnaProfileType.Video,
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -177,28 +177,28 @@ namespace Emby.Dlna.Profiles
new ResponseProfile
{
Container = "ts,mpegts",
- VideoCodec="h264",
- AudioCodec="ac3,aac,mp3",
+ VideoCodec = "h264",
+ AudioCodec = "ac3,aac,mp3",
MimeType = "video/vnd.dlna.mpeg-tts",
- OrgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU",
+ OrgPn = "AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU",
Type = DlnaProfileType.Video
},
new ResponseProfile
{
Container = "ts,mpegts",
- VideoCodec="mpeg2video",
+ VideoCodec = "mpeg2video",
MimeType = "video/vnd.dlna.mpeg-tts",
- OrgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",
+ OrgPn = "MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",
Type = DlnaProfileType.Video
},
new ResponseProfile
{
Container = "mpeg",
- VideoCodec="mpeg1video,mpeg2video",
+ VideoCodec = "mpeg1video,mpeg2video",
MimeType = "video/mpeg",
- OrgPn="MPEG_PS_NTSC,MPEG_PS_PAL",
+ OrgPn = "MPEG_PS_NTSC,MPEG_PS_PAL",
Type = DlnaProfileType.Video
},
new ResponseProfile
@@ -215,7 +215,7 @@ namespace Emby.Dlna.Profiles
{
Type = DlnaProfileType.Photo,
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -282,7 +282,7 @@ namespace Emby.Dlna.Profiles
Type = CodecType.VideoAudio,
Codec = "mp3,mp2",
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
diff --git a/Emby.Dlna/Profiles/SonyBravia2013Profile.cs b/Emby.Dlna/Profiles/SonyBravia2013Profile.cs
index 3300863c9..3d90a1e72 100644
--- a/Emby.Dlna/Profiles/SonyBravia2013Profile.cs
+++ b/Emby.Dlna/Profiles/SonyBravia2013Profile.cs
@@ -164,7 +164,7 @@ namespace Emby.Dlna.Profiles
{
Type = DlnaProfileType.Photo,
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -187,13 +187,13 @@ namespace Emby.Dlna.Profiles
new ResponseProfile
{
Container = "ts,mpegts",
- VideoCodec="h264",
- AudioCodec="ac3,aac,mp3",
+ VideoCodec = "h264",
+ AudioCodec = "ac3,aac,mp3",
MimeType = "video/vnd.dlna.mpeg-tts",
- OrgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T",
+ OrgPn = "AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T",
Type = DlnaProfileType.Video,
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -213,13 +213,13 @@ namespace Emby.Dlna.Profiles
new ResponseProfile
{
Container = "ts,mpegts",
- VideoCodec="h264",
- AudioCodec="ac3,aac,mp3",
+ VideoCodec = "h264",
+ AudioCodec = "ac3,aac,mp3",
MimeType = "video/mpeg",
- OrgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO",
+ OrgPn = "AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO",
Type = DlnaProfileType.Video,
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -233,28 +233,28 @@ namespace Emby.Dlna.Profiles
new ResponseProfile
{
Container = "ts,mpegts",
- VideoCodec="h264",
- AudioCodec="ac3,aac,mp3",
+ VideoCodec = "h264",
+ AudioCodec = "ac3,aac,mp3",
MimeType = "video/vnd.dlna.mpeg-tts",
- OrgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU",
+ OrgPn = "AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU",
Type = DlnaProfileType.Video
},
new ResponseProfile
{
Container = "ts,mpegts",
- VideoCodec="mpeg2video",
+ VideoCodec = "mpeg2video",
MimeType = "video/vnd.dlna.mpeg-tts",
- OrgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",
+ OrgPn = "MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",
Type = DlnaProfileType.Video
},
new ResponseProfile
{
Container = "mpeg",
- VideoCodec="mpeg1video,mpeg2video",
+ VideoCodec = "mpeg1video,mpeg2video",
MimeType = "video/mpeg",
- OrgPn="MPEG_PS_NTSC,MPEG_PS_PAL",
+ OrgPn = "MPEG_PS_NTSC,MPEG_PS_PAL",
Type = DlnaProfileType.Video
},
new ResponseProfile
@@ -265,14 +265,13 @@ namespace Emby.Dlna.Profiles
}
};
-
CodecProfiles = new[]
{
new CodecProfile
{
Type = CodecType.Video,
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -300,7 +299,7 @@ namespace Emby.Dlna.Profiles
Type = CodecType.VideoAudio,
Codec = "mp3,mp2",
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
diff --git a/Emby.Dlna/Profiles/SonyBravia2014Profile.cs b/Emby.Dlna/Profiles/SonyBravia2014Profile.cs
index 4e833441c..9188f73ef 100644
--- a/Emby.Dlna/Profiles/SonyBravia2014Profile.cs
+++ b/Emby.Dlna/Profiles/SonyBravia2014Profile.cs
@@ -164,7 +164,7 @@ namespace Emby.Dlna.Profiles
{
Type = DlnaProfileType.Photo,
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -187,13 +187,13 @@ namespace Emby.Dlna.Profiles
new ResponseProfile
{
Container = "ts,mpegts",
- VideoCodec="h264",
- AudioCodec="ac3,aac,mp3",
+ VideoCodec = "h264",
+ AudioCodec = "ac3,aac,mp3",
MimeType = "video/vnd.dlna.mpeg-tts",
- OrgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T",
+ OrgPn = "AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T",
Type = DlnaProfileType.Video,
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -213,13 +213,13 @@ namespace Emby.Dlna.Profiles
new ResponseProfile
{
Container = "ts,mpegts",
- VideoCodec="h264",
- AudioCodec="ac3,aac,mp3",
+ VideoCodec = "h264",
+ AudioCodec = "ac3,aac,mp3",
MimeType = "video/mpeg",
- OrgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO",
+ OrgPn = "AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO",
Type = DlnaProfileType.Video,
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -233,28 +233,28 @@ namespace Emby.Dlna.Profiles
new ResponseProfile
{
Container = "ts,mpegts",
- VideoCodec="h264",
- AudioCodec="ac3,aac,mp3",
+ VideoCodec = "h264",
+ AudioCodec = "ac3,aac,mp3",
MimeType = "video/vnd.dlna.mpeg-tts",
- OrgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU",
+ OrgPn = "AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU",
Type = DlnaProfileType.Video
},
new ResponseProfile
{
Container = "ts,mpegts",
- VideoCodec="mpeg2video",
+ VideoCodec = "mpeg2video",
MimeType = "video/vnd.dlna.mpeg-tts",
- OrgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",
+ OrgPn = "MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",
Type = DlnaProfileType.Video
},
new ResponseProfile
{
Container = "mpeg",
- VideoCodec="mpeg1video,mpeg2video",
+ VideoCodec = "mpeg1video,mpeg2video",
MimeType = "video/mpeg",
- OrgPn="MPEG_PS_NTSC,MPEG_PS_PAL",
+ OrgPn = "MPEG_PS_NTSC,MPEG_PS_PAL",
Type = DlnaProfileType.Video
},
new ResponseProfile
@@ -265,14 +265,13 @@ namespace Emby.Dlna.Profiles
}
};
-
CodecProfiles = new[]
{
new CodecProfile
{
Type = CodecType.Video,
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -300,7 +299,7 @@ namespace Emby.Dlna.Profiles
Type = CodecType.VideoAudio,
Codec = "mp3,mp2",
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
diff --git a/Emby.Dlna/Profiles/SonyPs3Profile.cs b/Emby.Dlna/Profiles/SonyPs3Profile.cs
index 7f72356bd..d56b1df50 100644
--- a/Emby.Dlna/Profiles/SonyPs3Profile.cs
+++ b/Emby.Dlna/Profiles/SonyPs3Profile.cs
@@ -108,7 +108,7 @@ namespace Emby.Dlna.Profiles
{
Type = DlnaProfileType.Photo,
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -133,7 +133,7 @@ namespace Emby.Dlna.Profiles
Type = CodecType.Video,
Codec = "h264",
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -176,7 +176,7 @@ namespace Emby.Dlna.Profiles
Type = CodecType.VideoAudio,
Codec = "ac3",
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -201,7 +201,7 @@ namespace Emby.Dlna.Profiles
Type = CodecType.VideoAudio,
Codec = "wmapro",
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -217,7 +217,7 @@ namespace Emby.Dlna.Profiles
Type = CodecType.VideoAudio,
Codec = "aac",
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -235,7 +235,7 @@ namespace Emby.Dlna.Profiles
new ResponseProfile
{
Container = "mp4,mov",
- AudioCodec="aac",
+ AudioCodec = "aac",
MimeType = "video/mp4",
Type = DlnaProfileType.Video
},
@@ -244,7 +244,7 @@ namespace Emby.Dlna.Profiles
{
Container = "avi",
MimeType = "video/divx",
- OrgPn="AVI",
+ OrgPn = "AVI",
Type = DlnaProfileType.Video
},
diff --git a/Emby.Dlna/Profiles/SonyPs4Profile.cs b/Emby.Dlna/Profiles/SonyPs4Profile.cs
index 411bfe2b0..db56094e2 100644
--- a/Emby.Dlna/Profiles/SonyPs4Profile.cs
+++ b/Emby.Dlna/Profiles/SonyPs4Profile.cs
@@ -110,7 +110,7 @@ namespace Emby.Dlna.Profiles
{
Type = DlnaProfileType.Photo,
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -135,7 +135,7 @@ namespace Emby.Dlna.Profiles
Type = CodecType.Video,
Codec = "h264",
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -178,7 +178,7 @@ namespace Emby.Dlna.Profiles
Type = CodecType.VideoAudio,
Codec = "ac3",
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -203,7 +203,7 @@ namespace Emby.Dlna.Profiles
Type = CodecType.VideoAudio,
Codec = "wmapro",
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -219,7 +219,7 @@ namespace Emby.Dlna.Profiles
Type = CodecType.VideoAudio,
Codec = "aac",
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -237,7 +237,7 @@ namespace Emby.Dlna.Profiles
new ResponseProfile
{
Container = "mp4,mov",
- AudioCodec="aac",
+ AudioCodec = "aac",
MimeType = "video/mp4",
Type = DlnaProfileType.Video
},
@@ -246,7 +246,7 @@ namespace Emby.Dlna.Profiles
{
Container = "avi",
MimeType = "video/divx",
- OrgPn="AVI",
+ OrgPn = "AVI",
Type = DlnaProfileType.Video
},
diff --git a/Emby.Dlna/Profiles/WdtvLiveProfile.cs b/Emby.Dlna/Profiles/WdtvLiveProfile.cs
index 2de9a8cd9..937ca0f42 100644
--- a/Emby.Dlna/Profiles/WdtvLiveProfile.cs
+++ b/Emby.Dlna/Profiles/WdtvLiveProfile.cs
@@ -20,7 +20,7 @@ namespace Emby.Dlna.Profiles
Headers = new[]
{
- new HttpHeaderInfo {Name = "User-Agent", Value = "alphanetworks", Match = HeaderMatchType.Substring},
+ new HttpHeaderInfo { Name = "User-Agent", Value = "alphanetworks", Match = HeaderMatchType.Substring },
new HttpHeaderInfo
{
Name = "User-Agent",
@@ -168,7 +168,7 @@ namespace Emby.Dlna.Profiles
{
Type = DlnaProfileType.Photo,
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -193,7 +193,7 @@ namespace Emby.Dlna.Profiles
Type = CodecType.Video,
Codec = "h264",
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -221,7 +221,7 @@ namespace Emby.Dlna.Profiles
Type = CodecType.VideoAudio,
Codec = "aac",
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
diff --git a/Emby.Dlna/Profiles/XboxOneProfile.cs b/Emby.Dlna/Profiles/XboxOneProfile.cs
index 2cbe4e6ac..84d8184a2 100644
--- a/Emby.Dlna/Profiles/XboxOneProfile.cs
+++ b/Emby.Dlna/Profiles/XboxOneProfile.cs
@@ -119,7 +119,7 @@ namespace Emby.Dlna.Profiles
Type = DlnaProfileType.Video,
Container = "mp4,mov",
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -138,7 +138,7 @@ namespace Emby.Dlna.Profiles
{
Type = CodecType.Video,
Codec = "mpeg4",
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -187,7 +187,7 @@ namespace Emby.Dlna.Profiles
{
Type = CodecType.Video,
Codec = "h264",
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -236,7 +236,7 @@ namespace Emby.Dlna.Profiles
{
Type = CodecType.Video,
Codec = "wmv2,wmv3,vc1",
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -284,7 +284,7 @@ namespace Emby.Dlna.Profiles
new CodecProfile
{
Type = CodecType.Video,
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -307,7 +307,7 @@ namespace Emby.Dlna.Profiles
{
Type = CodecType.VideoAudio,
Codec = "ac3,wmav2,wmapro",
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
@@ -323,7 +323,7 @@ namespace Emby.Dlna.Profiles
{
Type = CodecType.VideoAudio,
Codec = "aac",
- Conditions = new []
+ Conditions = new[]
{
new ProfileCondition
{
diff --git a/Emby.Dlna/Service/BaseControlHandler.cs b/Emby.Dlna/Service/BaseControlHandler.cs
index 699d325ea..d160e3339 100644
--- a/Emby.Dlna/Service/BaseControlHandler.cs
+++ b/Emby.Dlna/Service/BaseControlHandler.cs
@@ -15,11 +15,7 @@ namespace Emby.Dlna.Service
{
public abstract class BaseControlHandler
{
- private const string NS_SOAPENV = "http://schemas.xmlsoap.org/soap/envelope/";
-
- protected IServerConfigurationManager Config { get; }
-
- protected ILogger Logger { get; }
+ private const string NsSoapEnv = "http://schemas.xmlsoap.org/soap/envelope/";
protected BaseControlHandler(IServerConfigurationManager config, ILogger logger)
{
@@ -27,6 +23,10 @@ namespace Emby.Dlna.Service
Logger = logger;
}
+ protected IServerConfigurationManager Config { get; }
+
+ protected ILogger Logger { get; }
+
public async Task<ControlResponse> ProcessControlRequestAsync(ControlRequest request)
{
try
@@ -80,10 +80,10 @@ namespace Emby.Dlna.Service
{
writer.WriteStartDocument(true);
- writer.WriteStartElement("SOAP-ENV", "Envelope", NS_SOAPENV);
- writer.WriteAttributeString(string.Empty, "encodingStyle", NS_SOAPENV, "http://schemas.xmlsoap.org/soap/encoding/");
+ writer.WriteStartElement("SOAP-ENV", "Envelope", NsSoapEnv);
+ writer.WriteAttributeString(string.Empty, "encodingStyle", NsSoapEnv, "http://schemas.xmlsoap.org/soap/encoding/");
- writer.WriteStartElement("SOAP-ENV", "Body", NS_SOAPENV);
+ writer.WriteStartElement("SOAP-ENV", "Body", NsSoapEnv);
writer.WriteStartElement("u", requestInfo.LocalName + "Response", requestInfo.NamespaceURI);
WriteResult(requestInfo.LocalName, requestInfo.Headers, writer);
@@ -210,15 +210,6 @@ namespace Emby.Dlna.Service
}
}
- private class ControlRequestInfo
- {
- public string LocalName { get; set; }
-
- public string NamespaceURI { get; set; }
-
- public Dictionary<string, string> Headers { get; } = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- }
-
protected abstract void WriteResult(string methodName, IDictionary<string, string> methodParams, XmlWriter xmlWriter);
private void LogRequest(ControlRequest request)
@@ -240,5 +231,14 @@ namespace Emby.Dlna.Service
Logger.LogDebug("Control response. Headers: {@Headers}\n{Xml}", response.Headers, response.Xml);
}
+
+ private class ControlRequestInfo
+ {
+ public string LocalName { get; set; }
+
+ public string NamespaceURI { get; set; }
+
+ public Dictionary<string, string> Headers { get; } = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+ }
}
}
diff --git a/Emby.Dlna/Service/BaseService.cs b/Emby.Dlna/Service/BaseService.cs
index 8794ec26a..40d069e7c 100644
--- a/Emby.Dlna/Service/BaseService.cs
+++ b/Emby.Dlna/Service/BaseService.cs
@@ -6,20 +6,22 @@ using Microsoft.Extensions.Logging;
namespace Emby.Dlna.Service
{
- public class BaseService : IEventManager
+ public class BaseService : IDlnaEventManager
{
- protected IEventManager EventManager;
- protected IHttpClient HttpClient;
- protected ILogger Logger;
-
protected BaseService(ILogger<BaseService> logger, IHttpClient httpClient)
{
Logger = logger;
HttpClient = httpClient;
- EventManager = new EventManager(logger, HttpClient);
+ EventManager = new DlnaEventManager(logger, HttpClient);
}
+ protected IDlnaEventManager EventManager { get; }
+
+ protected IHttpClient HttpClient { get; }
+
+ protected ILogger Logger { get; }
+
public EventSubscriptionResponse CancelEventSubscription(string subscriptionId)
{
return EventManager.CancelEventSubscription(subscriptionId);
diff --git a/Emby.Dlna/Service/ControlErrorHandler.cs b/Emby.Dlna/Service/ControlErrorHandler.cs
index 047e9f014..f2b5dd9ca 100644
--- a/Emby.Dlna/Service/ControlErrorHandler.cs
+++ b/Emby.Dlna/Service/ControlErrorHandler.cs
@@ -10,7 +10,7 @@ namespace Emby.Dlna.Service
{
public static class ControlErrorHandler
{
- private const string NS_SOAPENV = "http://schemas.xmlsoap.org/soap/envelope/";
+ private const string NsSoapEnv = "http://schemas.xmlsoap.org/soap/envelope/";
public static ControlResponse GetResponse(Exception ex)
{
@@ -26,11 +26,11 @@ namespace Emby.Dlna.Service
{
writer.WriteStartDocument(true);
- writer.WriteStartElement("SOAP-ENV", "Envelope", NS_SOAPENV);
- writer.WriteAttributeString(string.Empty, "encodingStyle", NS_SOAPENV, "http://schemas.xmlsoap.org/soap/encoding/");
+ writer.WriteStartElement("SOAP-ENV", "Envelope", NsSoapEnv);
+ writer.WriteAttributeString(string.Empty, "encodingStyle", NsSoapEnv, "http://schemas.xmlsoap.org/soap/encoding/");
- writer.WriteStartElement("SOAP-ENV", "Body", NS_SOAPENV);
- writer.WriteStartElement("SOAP-ENV", "Fault", NS_SOAPENV);
+ writer.WriteStartElement("SOAP-ENV", "Body", NsSoapEnv);
+ writer.WriteStartElement("SOAP-ENV", "Fault", NsSoapEnv);
writer.WriteElementString("faultcode", "500");
writer.WriteElementString("faultstring", ex.Message);
diff --git a/Emby.Dlna/Service/ServiceXmlBuilder.cs b/Emby.Dlna/Service/ServiceXmlBuilder.cs
index 6c7d6f846..1e56d09b2 100644
--- a/Emby.Dlna/Service/ServiceXmlBuilder.cs
+++ b/Emby.Dlna/Service/ServiceXmlBuilder.cs
@@ -87,7 +87,7 @@ namespace Emby.Dlna.Service
.Append(SecurityElement.Escape(item.DataType ?? string.Empty))
.Append("</dataType>");
- if (item.AllowedValues.Length > 0)
+ if (item.AllowedValues.Count > 0)
{
builder.Append("<allowedValueList>");
foreach (var allowedValue in item.AllowedValues)
diff --git a/Emby.Dlna/Ssdp/DeviceDiscovery.cs b/Emby.Dlna/Ssdp/DeviceDiscovery.cs
index 7daac96d1..8c7d961f3 100644
--- a/Emby.Dlna/Ssdp/DeviceDiscovery.cs
+++ b/Emby.Dlna/Ssdp/DeviceDiscovery.cs
@@ -3,9 +3,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using Jellyfin.Data.Events;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Events;
using Rssdp;
using Rssdp.Infrastructure;
@@ -17,9 +17,17 @@ namespace Emby.Dlna.Ssdp
private readonly IServerConfigurationManager _config;
+ private SsdpDeviceLocator _deviceLocator;
+ private ISsdpCommunicationsServer _commsServer;
+
private int _listenerCount;
private bool _disposed;
+ public DeviceDiscovery(IServerConfigurationManager config)
+ {
+ _config = config;
+ }
+
private event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceDiscoveredInternal;
/// <inheritdoc />
@@ -49,15 +57,6 @@ namespace Emby.Dlna.Ssdp
/// <inheritdoc />
public event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceLeft;
- private SsdpDeviceLocator _deviceLocator;
-
- private ISsdpCommunicationsServer _commsServer;
-
- public DeviceDiscovery(IServerConfigurationManager config)
- {
- _config = config;
- }
-
// Call this method from somewhere in your code to start the search.
public void Start(ISsdpCommunicationsServer communicationsServer)
{
diff --git a/Emby.Dlna/Ssdp/Extensions.cs b/Emby.Dlna/Ssdp/SsdpExtensions.cs
index 613d332b2..e7a52f168 100644
--- a/Emby.Dlna/Ssdp/Extensions.cs
+++ b/Emby.Dlna/Ssdp/SsdpExtensions.cs
@@ -5,7 +5,7 @@ using System.Xml.Linq;
namespace Emby.Dlna.Ssdp
{
- public static class Extensions
+ public static class SsdpExtensions
{
public static string GetValue(this XElement container, XName name)
{
diff --git a/Emby.Naming/Emby.Naming.csproj b/Emby.Naming/Emby.Naming.csproj
index c017e76c7..5e2c6e3e3 100644
--- a/Emby.Naming/Emby.Naming.csproj
+++ b/Emby.Naming/Emby.Naming.csproj
@@ -23,8 +23,9 @@
<PropertyGroup>
<Authors>Jellyfin Contributors</Authors>
<PackageId>Jellyfin.Naming</PackageId>
- <PackageLicenseUrl>https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt</PackageLicenseUrl>
+ <VersionPrefix>10.7.0</VersionPrefix>
<RepositoryUrl>https://github.com/jellyfin/jellyfin</RepositoryUrl>
+ <PackageLicenseExpression>GPL-3.0-only</PackageLicenseExpression>
</PropertyGroup>
<!-- Code Analyzers-->
diff --git a/Emby.Notifications/NotificationEntryPoint.cs b/Emby.Notifications/NotificationEntryPoint.cs
index b923fd26c..ded22d26c 100644
--- a/Emby.Notifications/NotificationEntryPoint.cs
+++ b/Emby.Notifications/NotificationEntryPoint.cs
@@ -4,6 +4,7 @@ using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Events;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Entities;
@@ -13,7 +14,6 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Notifications;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Model.Activity;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Notifications;
using Microsoft.Extensions.Logging;
diff --git a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
deleted file mode 100644
index 84bec9201..000000000
--- a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
+++ /dev/null
@@ -1,590 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using Jellyfin.Data.Entities;
-using MediaBrowser.Common.Plugins;
-using MediaBrowser.Common.Updates;
-using MediaBrowser.Controller.Authentication;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Plugins;
-using MediaBrowser.Controller.Session;
-using MediaBrowser.Controller.Subtitles;
-using MediaBrowser.Model.Activity;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.Globalization;
-using MediaBrowser.Model.Notifications;
-using MediaBrowser.Model.Tasks;
-using MediaBrowser.Model.Updates;
-using Microsoft.Extensions.Logging;
-
-namespace Emby.Server.Implementations.Activity
-{
- /// <summary>
- /// Entry point for the activity logger.
- /// </summary>
- public sealed class ActivityLogEntryPoint : IServerEntryPoint
- {
- private readonly ILogger<ActivityLogEntryPoint> _logger;
- private readonly IInstallationManager _installationManager;
- private readonly ISessionManager _sessionManager;
- private readonly ITaskManager _taskManager;
- private readonly IActivityManager _activityManager;
- private readonly ILocalizationManager _localization;
- private readonly ISubtitleManager _subManager;
- private readonly IUserManager _userManager;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ActivityLogEntryPoint"/> class.
- /// </summary>
- /// <param name="logger">The logger.</param>
- /// <param name="sessionManager">The session manager.</param>
- /// <param name="taskManager">The task manager.</param>
- /// <param name="activityManager">The activity manager.</param>
- /// <param name="localization">The localization manager.</param>
- /// <param name="installationManager">The installation manager.</param>
- /// <param name="subManager">The subtitle manager.</param>
- /// <param name="userManager">The user manager.</param>
- public ActivityLogEntryPoint(
- ILogger<ActivityLogEntryPoint> logger,
- ISessionManager sessionManager,
- ITaskManager taskManager,
- IActivityManager activityManager,
- ILocalizationManager localization,
- IInstallationManager installationManager,
- ISubtitleManager subManager,
- IUserManager userManager)
- {
- _logger = logger;
- _sessionManager = sessionManager;
- _taskManager = taskManager;
- _activityManager = activityManager;
- _localization = localization;
- _installationManager = installationManager;
- _subManager = subManager;
- _userManager = userManager;
- }
-
- /// <inheritdoc />
- public Task RunAsync()
- {
- _taskManager.TaskCompleted += OnTaskCompleted;
-
- _installationManager.PluginInstalled += OnPluginInstalled;
- _installationManager.PluginUninstalled += OnPluginUninstalled;
- _installationManager.PluginUpdated += OnPluginUpdated;
- _installationManager.PackageInstallationFailed += OnPackageInstallationFailed;
-
- _sessionManager.SessionStarted += OnSessionStarted;
- _sessionManager.AuthenticationFailed += OnAuthenticationFailed;
- _sessionManager.AuthenticationSucceeded += OnAuthenticationSucceeded;
- _sessionManager.SessionEnded += OnSessionEnded;
- _sessionManager.PlaybackStart += OnPlaybackStart;
- _sessionManager.PlaybackStopped += OnPlaybackStopped;
-
- _subManager.SubtitleDownloadFailure += OnSubtitleDownloadFailure;
-
- _userManager.OnUserCreated += OnUserCreated;
- _userManager.OnUserPasswordChanged += OnUserPasswordChanged;
- _userManager.OnUserDeleted += OnUserDeleted;
- _userManager.OnUserLockedOut += OnUserLockedOut;
-
- return Task.CompletedTask;
- }
-
- private async void OnUserLockedOut(object sender, GenericEventArgs<User> e)
- {
- await CreateLogEntry(new ActivityLog(
- string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("UserLockedOutWithName"),
- e.Argument.Username),
- NotificationType.UserLockedOut.ToString(),
- e.Argument.Id)
- {
- LogSeverity = LogLevel.Error
- }).ConfigureAwait(false);
- }
-
- private async void OnSubtitleDownloadFailure(object sender, SubtitleDownloadFailureEventArgs e)
- {
- await CreateLogEntry(new ActivityLog(
- string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("SubtitleDownloadFailureFromForItem"),
- e.Provider,
- Notifications.NotificationEntryPoint.GetItemName(e.Item)),
- "SubtitleDownloadFailure",
- Guid.Empty)
- {
- ItemId = e.Item.Id.ToString("N", CultureInfo.InvariantCulture),
- ShortOverview = e.Exception.Message
- }).ConfigureAwait(false);
- }
-
- private async void OnPlaybackStopped(object sender, PlaybackStopEventArgs e)
- {
- var item = e.MediaInfo;
-
- if (item == null)
- {
- _logger.LogWarning("PlaybackStopped reported with null media info.");
- return;
- }
-
- if (e.Item != null && e.Item.IsThemeMedia)
- {
- // Don't report theme song or local trailer playback
- return;
- }
-
- if (e.Users.Count == 0)
- {
- return;
- }
-
- var user = e.Users[0];
-
- await CreateLogEntry(new ActivityLog(
- string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("UserStoppedPlayingItemWithValues"),
- user.Username,
- GetItemName(item),
- e.DeviceName),
- GetPlaybackStoppedNotificationType(item.MediaType),
- user.Id))
- .ConfigureAwait(false);
- }
-
- private async void OnPlaybackStart(object sender, PlaybackProgressEventArgs e)
- {
- var item = e.MediaInfo;
-
- if (item == null)
- {
- _logger.LogWarning("PlaybackStart reported with null media info.");
- return;
- }
-
- if (e.Item != null && e.Item.IsThemeMedia)
- {
- // Don't report theme song or local trailer playback
- return;
- }
-
- if (e.Users.Count == 0)
- {
- return;
- }
-
- var user = e.Users.First();
-
- await CreateLogEntry(new ActivityLog(
- string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("UserStartedPlayingItemWithValues"),
- user.Username,
- GetItemName(item),
- e.DeviceName),
- GetPlaybackNotificationType(item.MediaType),
- user.Id))
- .ConfigureAwait(false);
- }
-
- private static string GetItemName(BaseItemDto item)
- {
- var name = item.Name;
-
- if (!string.IsNullOrEmpty(item.SeriesName))
- {
- name = item.SeriesName + " - " + name;
- }
-
- if (item.Artists != null && item.Artists.Count > 0)
- {
- name = item.Artists[0] + " - " + name;
- }
-
- return name;
- }
-
- private static string GetPlaybackNotificationType(string mediaType)
- {
- if (string.Equals(mediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase))
- {
- return NotificationType.AudioPlayback.ToString();
- }
-
- if (string.Equals(mediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
- {
- return NotificationType.VideoPlayback.ToString();
- }
-
- return null;
- }
-
- private static string GetPlaybackStoppedNotificationType(string mediaType)
- {
- if (string.Equals(mediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase))
- {
- return NotificationType.AudioPlaybackStopped.ToString();
- }
-
- if (string.Equals(mediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
- {
- return NotificationType.VideoPlaybackStopped.ToString();
- }
-
- return null;
- }
-
- private async void OnSessionEnded(object sender, SessionEventArgs e)
- {
- var session = e.SessionInfo;
-
- if (string.IsNullOrEmpty(session.UserName))
- {
- return;
- }
-
- await CreateLogEntry(new ActivityLog(
- string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("UserOfflineFromDevice"),
- session.UserName,
- session.DeviceName),
- "SessionEnded",
- session.UserId)
- {
- ShortOverview = string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("LabelIpAddressValue"),
- session.RemoteEndPoint),
- }).ConfigureAwait(false);
- }
-
- private async void OnAuthenticationSucceeded(object sender, GenericEventArgs<AuthenticationResult> e)
- {
- var user = e.Argument.User;
-
- await CreateLogEntry(new ActivityLog(
- string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("AuthenticationSucceededWithUserName"),
- user.Name),
- "AuthenticationSucceeded",
- user.Id)
- {
- ShortOverview = string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("LabelIpAddressValue"),
- e.Argument.SessionInfo.RemoteEndPoint),
- }).ConfigureAwait(false);
- }
-
- private async void OnAuthenticationFailed(object sender, GenericEventArgs<AuthenticationRequest> e)
- {
- await CreateLogEntry(new ActivityLog(
- string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("FailedLoginAttemptWithUserName"),
- e.Argument.Username),
- "AuthenticationFailed",
- Guid.Empty)
- {
- LogSeverity = LogLevel.Error,
- ShortOverview = string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("LabelIpAddressValue"),
- e.Argument.RemoteEndPoint),
- }).ConfigureAwait(false);
- }
-
- private async void OnUserDeleted(object sender, GenericEventArgs<User> e)
- {
- await CreateLogEntry(new ActivityLog(
- string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("UserDeletedWithName"),
- e.Argument.Username),
- "UserDeleted",
- Guid.Empty))
- .ConfigureAwait(false);
- }
-
- private async void OnUserPasswordChanged(object sender, GenericEventArgs<User> e)
- {
- await CreateLogEntry(new ActivityLog(
- string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("UserPasswordChangedWithName"),
- e.Argument.Username),
- "UserPasswordChanged",
- e.Argument.Id))
- .ConfigureAwait(false);
- }
-
- private async void OnUserCreated(object sender, GenericEventArgs<User> e)
- {
- await CreateLogEntry(new ActivityLog(
- string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("UserCreatedWithName"),
- e.Argument.Username),
- "UserCreated",
- e.Argument.Id))
- .ConfigureAwait(false);
- }
-
- private async void OnSessionStarted(object sender, SessionEventArgs e)
- {
- var session = e.SessionInfo;
-
- if (string.IsNullOrEmpty(session.UserName))
- {
- return;
- }
-
- await CreateLogEntry(new ActivityLog(
- string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("UserOnlineFromDevice"),
- session.UserName,
- session.DeviceName),
- "SessionStarted",
- session.UserId)
- {
- ShortOverview = string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("LabelIpAddressValue"),
- session.RemoteEndPoint)
- }).ConfigureAwait(false);
- }
-
- private async void OnPluginUpdated(object sender, InstallationInfo e)
- {
- await CreateLogEntry(new ActivityLog(
- string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("PluginUpdatedWithName"),
- e.Name),
- NotificationType.PluginUpdateInstalled.ToString(),
- Guid.Empty)
- {
- ShortOverview = string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("VersionNumber"),
- e.Version),
- Overview = e.Changelog
- }).ConfigureAwait(false);
- }
-
- private async void OnPluginUninstalled(object sender, IPlugin e)
- {
- await CreateLogEntry(new ActivityLog(
- string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("PluginUninstalledWithName"),
- e.Name),
- NotificationType.PluginUninstalled.ToString(),
- Guid.Empty))
- .ConfigureAwait(false);
- }
-
- private async void OnPluginInstalled(object sender, InstallationInfo e)
- {
- await CreateLogEntry(new ActivityLog(
- string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("PluginInstalledWithName"),
- e.Name),
- NotificationType.PluginInstalled.ToString(),
- Guid.Empty)
- {
- ShortOverview = string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("VersionNumber"),
- e.Version)
- }).ConfigureAwait(false);
- }
-
- private async void OnPackageInstallationFailed(object sender, InstallationFailedEventArgs e)
- {
- var installationInfo = e.InstallationInfo;
-
- await CreateLogEntry(new ActivityLog(
- string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("NameInstallFailed"),
- installationInfo.Name),
- NotificationType.InstallationFailed.ToString(),
- Guid.Empty)
- {
- ShortOverview = string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("VersionNumber"),
- installationInfo.Version),
- Overview = e.Exception.Message
- }).ConfigureAwait(false);
- }
-
- private async void OnTaskCompleted(object sender, TaskCompletionEventArgs e)
- {
- var result = e.Result;
- var task = e.Task;
-
- if (task.ScheduledTask is IConfigurableScheduledTask activityTask
- && !activityTask.IsLogged)
- {
- return;
- }
-
- var time = result.EndTimeUtc - result.StartTimeUtc;
- var runningTime = string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("LabelRunningTimeValue"),
- ToUserFriendlyString(time));
-
- if (result.Status == TaskCompletionStatus.Failed)
- {
- var vals = new List<string>();
-
- if (!string.IsNullOrEmpty(e.Result.ErrorMessage))
- {
- vals.Add(e.Result.ErrorMessage);
- }
-
- if (!string.IsNullOrEmpty(e.Result.LongErrorMessage))
- {
- vals.Add(e.Result.LongErrorMessage);
- }
-
- await CreateLogEntry(new ActivityLog(
- string.Format(CultureInfo.InvariantCulture, _localization.GetLocalizedString("ScheduledTaskFailedWithName"), task.Name),
- NotificationType.TaskFailed.ToString(),
- Guid.Empty)
- {
- LogSeverity = LogLevel.Error,
- Overview = string.Join(Environment.NewLine, vals),
- ShortOverview = runningTime
- }).ConfigureAwait(false);
- }
- }
-
- private async Task CreateLogEntry(ActivityLog entry)
- => await _activityManager.CreateAsync(entry).ConfigureAwait(false);
-
- /// <inheritdoc />
- public void Dispose()
- {
- _taskManager.TaskCompleted -= OnTaskCompleted;
-
- _installationManager.PluginInstalled -= OnPluginInstalled;
- _installationManager.PluginUninstalled -= OnPluginUninstalled;
- _installationManager.PluginUpdated -= OnPluginUpdated;
- _installationManager.PackageInstallationFailed -= OnPackageInstallationFailed;
-
- _sessionManager.SessionStarted -= OnSessionStarted;
- _sessionManager.AuthenticationFailed -= OnAuthenticationFailed;
- _sessionManager.AuthenticationSucceeded -= OnAuthenticationSucceeded;
- _sessionManager.SessionEnded -= OnSessionEnded;
-
- _sessionManager.PlaybackStart -= OnPlaybackStart;
- _sessionManager.PlaybackStopped -= OnPlaybackStopped;
-
- _subManager.SubtitleDownloadFailure -= OnSubtitleDownloadFailure;
-
- _userManager.OnUserCreated -= OnUserCreated;
- _userManager.OnUserPasswordChanged -= OnUserPasswordChanged;
- _userManager.OnUserDeleted -= OnUserDeleted;
- _userManager.OnUserLockedOut -= OnUserLockedOut;
- }
-
- /// <summary>
- /// Constructs a user-friendly string for this TimeSpan instance.
- /// </summary>
- private static string ToUserFriendlyString(TimeSpan span)
- {
- const int DaysInYear = 365;
- const int DaysInMonth = 30;
-
- // Get each non-zero value from TimeSpan component
- var values = new List<string>();
-
- // Number of years
- int days = span.Days;
- if (days >= DaysInYear)
- {
- int years = days / DaysInYear;
- values.Add(CreateValueString(years, "year"));
- days %= DaysInYear;
- }
-
- // Number of months
- if (days >= DaysInMonth)
- {
- int months = days / DaysInMonth;
- values.Add(CreateValueString(months, "month"));
- days = days % DaysInMonth;
- }
-
- // Number of days
- if (days >= 1)
- {
- values.Add(CreateValueString(days, "day"));
- }
-
- // Number of hours
- if (span.Hours >= 1)
- {
- values.Add(CreateValueString(span.Hours, "hour"));
- }
-
- // Number of minutes
- if (span.Minutes >= 1)
- {
- values.Add(CreateValueString(span.Minutes, "minute"));
- }
-
- // Number of seconds (include when 0 if no other components included)
- if (span.Seconds >= 1 || values.Count == 0)
- {
- values.Add(CreateValueString(span.Seconds, "second"));
- }
-
- // Combine values into string
- var builder = new StringBuilder();
- for (int i = 0; i < values.Count; i++)
- {
- if (builder.Length > 0)
- {
- builder.Append(i == values.Count - 1 ? " and " : ", ");
- }
-
- builder.Append(values[i]);
- }
-
- // Return result
- return builder.ToString();
- }
-
- /// <summary>
- /// Constructs a string description of a time-span value.
- /// </summary>
- /// <param name="value">The value of this item.</param>
- /// <param name="description">The name of this item (singular form).</param>
- private static string CreateValueString(int value, string description)
- {
- return string.Format(
- CultureInfo.InvariantCulture,
- "{0:#,##0} {1}",
- value,
- value == 1 ? description : string.Format(CultureInfo.InvariantCulture, "{0}s", description));
- }
- }
-}
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index 99530bfbd..a2d6e2c9e 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -53,7 +53,6 @@ using MediaBrowser.Common.Net;
using MediaBrowser.Common.Plugins;
using MediaBrowser.Common.Updates;
using MediaBrowser.Controller;
-using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Chapters;
using MediaBrowser.Controller.Collections;
@@ -98,6 +97,7 @@ using MediaBrowser.Providers.Plugins.TheTvdb;
using MediaBrowser.Providers.Subtitles;
using MediaBrowser.XbmcMetadata.Providers;
using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Prometheus.DotNetRuntime;
@@ -173,6 +173,8 @@ namespace Emby.Server.Implementations
/// </summary>
protected ILogger<ApplicationHost> Logger { get; }
+ protected IServiceCollection ServiceCollection { get; }
+
private IPlugin[] _plugins;
/// <summary>
@@ -238,9 +240,11 @@ namespace Emby.Server.Implementations
ILoggerFactory loggerFactory,
IStartupOptions options,
IFileSystem fileSystem,
- INetworkManager networkManager)
+ INetworkManager networkManager,
+ IServiceCollection serviceCollection)
{
_xmlSerializer = new MyXmlSerializer();
+ ServiceCollection = serviceCollection;
_networkManager = networkManager;
networkManager.LocalSubnetsFn = GetConfiguredLocalSubnets;
@@ -464,7 +468,7 @@ namespace Emby.Server.Implementations
}
/// <inheritdoc/>
- public void Init(IServiceCollection serviceCollection)
+ public void Init()
{
HttpPort = ServerConfigurationManager.Configuration.HttpServerPortNumber;
HttpsPort = ServerConfigurationManager.Configuration.HttpsPortNumber;
@@ -493,7 +497,7 @@ namespace Emby.Server.Implementations
DiscoverTypes();
- RegisterServices(serviceCollection);
+ RegisterServices();
}
public Task ExecuteHttpHandlerAsync(HttpContext context, Func<Task> next)
@@ -502,139 +506,139 @@ namespace Emby.Server.Implementations
/// <summary>
/// Registers services/resources with the service collection that will be available via DI.
/// </summary>
- protected virtual void RegisterServices(IServiceCollection serviceCollection)
+ protected virtual void RegisterServices()
{
- serviceCollection.AddSingleton(_startupOptions);
+ ServiceCollection.AddSingleton(_startupOptions);
- serviceCollection.AddMemoryCache();
+ ServiceCollection.AddMemoryCache();
- serviceCollection.AddSingleton(ConfigurationManager);
- serviceCollection.AddSingleton<IApplicationHost>(this);
+ ServiceCollection.AddSingleton(ConfigurationManager);
+ ServiceCollection.AddSingleton<IApplicationHost>(this);
- serviceCollection.AddSingleton<IApplicationPaths>(ApplicationPaths);
+ ServiceCollection.AddSingleton<IApplicationPaths>(ApplicationPaths);
- serviceCollection.AddSingleton<IJsonSerializer, JsonSerializer>();
+ ServiceCollection.AddSingleton<IJsonSerializer, JsonSerializer>();
- serviceCollection.AddSingleton(_fileSystemManager);
- serviceCollection.AddSingleton<TvdbClientManager>();
+ ServiceCollection.AddSingleton(_fileSystemManager);
+ ServiceCollection.AddSingleton<TvdbClientManager>();
- serviceCollection.AddSingleton<IHttpClient, HttpClientManager.HttpClientManager>();
+ ServiceCollection.AddSingleton<IHttpClient, HttpClientManager.HttpClientManager>();
- serviceCollection.AddSingleton(_networkManager);
+ ServiceCollection.AddSingleton(_networkManager);
- serviceCollection.AddSingleton<IIsoManager, IsoManager>();
+ ServiceCollection.AddSingleton<IIsoManager, IsoManager>();
- serviceCollection.AddSingleton<ITaskManager, TaskManager>();
+ ServiceCollection.AddSingleton<ITaskManager, TaskManager>();
- serviceCollection.AddSingleton(_xmlSerializer);
+ ServiceCollection.AddSingleton(_xmlSerializer);
- serviceCollection.AddSingleton<IStreamHelper, StreamHelper>();
+ ServiceCollection.AddSingleton<IStreamHelper, StreamHelper>();
- serviceCollection.AddSingleton<ICryptoProvider, CryptographyProvider>();
+ ServiceCollection.AddSingleton<ICryptoProvider, CryptographyProvider>();
- serviceCollection.AddSingleton<ISocketFactory, SocketFactory>();
+ ServiceCollection.AddSingleton<ISocketFactory, SocketFactory>();
- serviceCollection.AddSingleton<IInstallationManager, InstallationManager>();
+ ServiceCollection.AddSingleton<IInstallationManager, InstallationManager>();
- serviceCollection.AddSingleton<IZipClient, ZipClient>();
+ ServiceCollection.AddSingleton<IZipClient, ZipClient>();
- serviceCollection.AddSingleton<IHttpResultFactory, HttpResultFactory>();
+ ServiceCollection.AddSingleton<IHttpResultFactory, HttpResultFactory>();
- serviceCollection.AddSingleton<IServerApplicationHost>(this);
- serviceCollection.AddSingleton<IServerApplicationPaths>(ApplicationPaths);
+ ServiceCollection.AddSingleton<IServerApplicationHost>(this);
+ ServiceCollection.AddSingleton<IServerApplicationPaths>(ApplicationPaths);
- serviceCollection.AddSingleton(ServerConfigurationManager);
+ ServiceCollection.AddSingleton(ServerConfigurationManager);
- serviceCollection.AddSingleton<ILocalizationManager, LocalizationManager>();
+ ServiceCollection.AddSingleton<ILocalizationManager, LocalizationManager>();
- serviceCollection.AddSingleton<IBlurayExaminer, BdInfoExaminer>();
+ ServiceCollection.AddSingleton<IBlurayExaminer, BdInfoExaminer>();
- serviceCollection.AddSingleton<IUserDataRepository, SqliteUserDataRepository>();
- serviceCollection.AddSingleton<IUserDataManager, UserDataManager>();
+ ServiceCollection.AddSingleton<IUserDataRepository, SqliteUserDataRepository>();
+ ServiceCollection.AddSingleton<IUserDataManager, UserDataManager>();
- serviceCollection.AddSingleton<IItemRepository, SqliteItemRepository>();
+ ServiceCollection.AddSingleton<IItemRepository, SqliteItemRepository>();
- serviceCollection.AddSingleton<IAuthenticationRepository, AuthenticationRepository>();
+ ServiceCollection.AddSingleton<IAuthenticationRepository, AuthenticationRepository>();
// TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required
- serviceCollection.AddTransient(provider => new Lazy<IDtoService>(provider.GetRequiredService<IDtoService>));
+ ServiceCollection.AddTransient(provider => new Lazy<IDtoService>(provider.GetRequiredService<IDtoService>));
// TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required
- serviceCollection.AddTransient(provider => new Lazy<EncodingHelper>(provider.GetRequiredService<EncodingHelper>));
- serviceCollection.AddSingleton<IMediaEncoder, MediaBrowser.MediaEncoding.Encoder.MediaEncoder>();
+ ServiceCollection.AddTransient(provider => new Lazy<EncodingHelper>(provider.GetRequiredService<EncodingHelper>));
+ ServiceCollection.AddSingleton<IMediaEncoder, MediaBrowser.MediaEncoding.Encoder.MediaEncoder>();
// TODO: Refactor to eliminate the circular dependencies here so that Lazy<T> isn't required
- serviceCollection.AddTransient(provider => new Lazy<ILibraryMonitor>(provider.GetRequiredService<ILibraryMonitor>));
- serviceCollection.AddTransient(provider => new Lazy<IProviderManager>(provider.GetRequiredService<IProviderManager>));
- serviceCollection.AddTransient(provider => new Lazy<IUserViewManager>(provider.GetRequiredService<IUserViewManager>));
- serviceCollection.AddSingleton<ILibraryManager, LibraryManager>();
+ ServiceCollection.AddTransient(provider => new Lazy<ILibraryMonitor>(provider.GetRequiredService<ILibraryMonitor>));
+ ServiceCollection.AddTransient(provider => new Lazy<IProviderManager>(provider.GetRequiredService<IProviderManager>));
+ ServiceCollection.AddTransient(provider => new Lazy<IUserViewManager>(provider.GetRequiredService<IUserViewManager>));
+ ServiceCollection.AddSingleton<ILibraryManager, LibraryManager>();
- serviceCollection.AddSingleton<IMusicManager, MusicManager>();
+ ServiceCollection.AddSingleton<IMusicManager, MusicManager>();
- serviceCollection.AddSingleton<ILibraryMonitor, LibraryMonitor>();
+ ServiceCollection.AddSingleton<ILibraryMonitor, LibraryMonitor>();
- serviceCollection.AddSingleton<ISearchEngine, SearchEngine>();
+ ServiceCollection.AddSingleton<ISearchEngine, SearchEngine>();
- serviceCollection.AddSingleton<ServiceController>();
- serviceCollection.AddSingleton<IHttpServer, HttpListenerHost>();
+ ServiceCollection.AddSingleton<ServiceController>();
+ ServiceCollection.AddSingleton<IHttpServer, HttpListenerHost>();
- serviceCollection.AddSingleton<IImageProcessor, ImageProcessor>();
+ ServiceCollection.AddSingleton<IImageProcessor, ImageProcessor>();
- serviceCollection.AddSingleton<ITVSeriesManager, TVSeriesManager>();
+ ServiceCollection.AddSingleton<ITVSeriesManager, TVSeriesManager>();
- serviceCollection.AddSingleton<IDeviceManager, DeviceManager>();
+ ServiceCollection.AddSingleton<IDeviceManager, DeviceManager>();
- serviceCollection.AddSingleton<IMediaSourceManager, MediaSourceManager>();
+ ServiceCollection.AddSingleton<IMediaSourceManager, MediaSourceManager>();
- serviceCollection.AddSingleton<ISubtitleManager, SubtitleManager>();
+ ServiceCollection.AddSingleton<ISubtitleManager, SubtitleManager>();
- serviceCollection.AddSingleton<IProviderManager, ProviderManager>();
+ ServiceCollection.AddSingleton<IProviderManager, ProviderManager>();
// TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required
- serviceCollection.AddTransient(provider => new Lazy<ILiveTvManager>(provider.GetRequiredService<ILiveTvManager>));
- serviceCollection.AddSingleton<IDtoService, DtoService>();
+ ServiceCollection.AddTransient(provider => new Lazy<ILiveTvManager>(provider.GetRequiredService<ILiveTvManager>));
+ ServiceCollection.AddSingleton<IDtoService, DtoService>();
- serviceCollection.AddSingleton<IChannelManager, ChannelManager>();
+ ServiceCollection.AddSingleton<IChannelManager, ChannelManager>();
- serviceCollection.AddSingleton<ISessionManager, SessionManager>();
+ ServiceCollection.AddSingleton<ISessionManager, SessionManager>();
- serviceCollection.AddSingleton<IDlnaManager, DlnaManager>();
+ ServiceCollection.AddSingleton<IDlnaManager, DlnaManager>();
- serviceCollection.AddSingleton<ICollectionManager, CollectionManager>();
+ ServiceCollection.AddSingleton<ICollectionManager, CollectionManager>();
- serviceCollection.AddSingleton<IPlaylistManager, PlaylistManager>();
+ ServiceCollection.AddSingleton<IPlaylistManager, PlaylistManager>();
- serviceCollection.AddSingleton<ISyncPlayManager, SyncPlayManager>();
+ ServiceCollection.AddSingleton<ISyncPlayManager, SyncPlayManager>();
- serviceCollection.AddSingleton<LiveTvDtoService>();
- serviceCollection.AddSingleton<ILiveTvManager, LiveTvManager>();
+ ServiceCollection.AddSingleton<LiveTvDtoService>();
+ ServiceCollection.AddSingleton<ILiveTvManager, LiveTvManager>();
- serviceCollection.AddSingleton<IUserViewManager, UserViewManager>();
+ ServiceCollection.AddSingleton<IUserViewManager, UserViewManager>();
- serviceCollection.AddSingleton<INotificationManager, NotificationManager>();
+ ServiceCollection.AddSingleton<INotificationManager, NotificationManager>();
- serviceCollection.AddSingleton<IDeviceDiscovery, DeviceDiscovery>();
+ ServiceCollection.AddSingleton<IDeviceDiscovery, DeviceDiscovery>();
- serviceCollection.AddSingleton<IChapterManager, ChapterManager>();
+ ServiceCollection.AddSingleton<IChapterManager, ChapterManager>();
- serviceCollection.AddSingleton<IEncodingManager, MediaEncoder.EncodingManager>();
+ ServiceCollection.AddSingleton<IEncodingManager, MediaEncoder.EncodingManager>();
- serviceCollection.AddSingleton<IAuthorizationContext, AuthorizationContext>();
- serviceCollection.AddSingleton<ISessionContext, SessionContext>();
+ ServiceCollection.AddSingleton<IAuthorizationContext, AuthorizationContext>();
+ ServiceCollection.AddSingleton<ISessionContext, SessionContext>();
- serviceCollection.AddSingleton<IAuthService, AuthService>();
+ ServiceCollection.AddSingleton<IAuthService, AuthService>();
- serviceCollection.AddSingleton<ISubtitleEncoder, MediaBrowser.MediaEncoding.Subtitles.SubtitleEncoder>();
+ ServiceCollection.AddSingleton<ISubtitleEncoder, MediaBrowser.MediaEncoding.Subtitles.SubtitleEncoder>();
- serviceCollection.AddSingleton<IResourceFileManager, ResourceFileManager>();
- serviceCollection.AddSingleton<EncodingHelper>();
+ ServiceCollection.AddSingleton<IResourceFileManager, ResourceFileManager>();
+ ServiceCollection.AddSingleton<EncodingHelper>();
- serviceCollection.AddSingleton<IAttachmentExtractor, MediaBrowser.MediaEncoding.Attachments.AttachmentExtractor>();
+ ServiceCollection.AddSingleton<IAttachmentExtractor, MediaBrowser.MediaEncoding.Attachments.AttachmentExtractor>();
- serviceCollection.AddSingleton<TranscodingJobHelper>();
- serviceCollection.AddScoped<MediaInfoHelper>();
- serviceCollection.AddScoped<AudioHelper>();
- serviceCollection.AddScoped<DynamicHlsHelper>();
+ ServiceCollection.AddSingleton<TranscodingJobHelper>();
+ ServiceCollection.AddScoped<MediaInfoHelper>();
+ ServiceCollection.AddScoped<AudioHelper>();
+ ServiceCollection.AddScoped<DynamicHlsHelper>();
}
/// <summary>
@@ -834,6 +838,8 @@ namespace Emby.Server.Implementations
{
hasPluginConfiguration.SetStartupInfo(s => Directory.CreateDirectory(s));
}
+
+ plugin.RegisterServices(ServiceCollection);
}
catch (Exception ex)
{
@@ -1388,6 +1394,20 @@ namespace Emby.Server.Implementations
_plugins = list.ToArray();
}
+ public IEnumerable<Assembly> GetApiPluginAssemblies()
+ {
+ var assemblies = _allConcreteTypes
+ .Where(i => typeof(ControllerBase).IsAssignableFrom(i))
+ .Select(i => i.Assembly)
+ .Distinct();
+
+ foreach (var assembly in assemblies)
+ {
+ Logger.LogDebug("Found API endpoints in plugin {name}", assembly.FullName);
+ yield return assembly;
+ }
+ }
+
public virtual void LaunchUrl(string url)
{
if (!CanLaunchWebBrowser)
diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs
index d8ab1f1a1..26fc1bee4 100644
--- a/Emby.Server.Implementations/Channels/ChannelManager.cs
+++ b/Emby.Server.Implementations/Channels/ChannelManager.cs
@@ -746,12 +746,21 @@ namespace Emby.Server.Implementations.Channels
// null if came from cache
if (itemsResult != null)
{
- var internalItems = itemsResult.Items
- .Select(i => GetChannelItemEntity(i, channelProvider, channel.Id, parentItem, cancellationToken))
- .ToArray();
+ var items = itemsResult.Items;
+ var itemsLen = items.Count;
+ var internalItems = new Guid[itemsLen];
+ for (int i = 0; i < itemsLen; i++)
+ {
+ internalItems[i] = (await GetChannelItemEntityAsync(
+ items[i],
+ channelProvider,
+ channel.Id,
+ parentItem,
+ cancellationToken).ConfigureAwait(false)).Id;
+ }
var existingIds = _libraryManager.GetItemIds(query);
- var deadIds = existingIds.Except(internalItems.Select(i => i.Id))
+ var deadIds = existingIds.Except(internalItems)
.ToArray();
foreach (var deadId in deadIds)
@@ -963,7 +972,7 @@ namespace Emby.Server.Implementations.Channels
return item;
}
- private BaseItem GetChannelItemEntity(ChannelItemInfo info, IChannel channelProvider, Guid internalChannelId, BaseItem parentFolder, CancellationToken cancellationToken)
+ private async Task<BaseItem> GetChannelItemEntityAsync(ChannelItemInfo info, IChannel channelProvider, Guid internalChannelId, BaseItem parentFolder, CancellationToken cancellationToken)
{
var parentFolderId = parentFolder.Id;
@@ -1165,7 +1174,7 @@ namespace Emby.Server.Implementations.Channels
}
else if (forceUpdate)
{
- item.UpdateToRepository(ItemUpdateType.None, cancellationToken);
+ await item.UpdateToRepositoryAsync(ItemUpdateType.None, cancellationToken).ConfigureAwait(false);
}
if ((isNew || forceUpdate) && info.Type == ChannelItemType.Media)
diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs
index ac2edc1e2..3011a37e3 100644
--- a/Emby.Server.Implementations/Collections/CollectionManager.cs
+++ b/Emby.Server.Implementations/Collections/CollectionManager.cs
@@ -132,7 +132,7 @@ namespace Emby.Server.Implementations.Collections
}
/// <inheritdoc />
- public BoxSet CreateCollection(CollectionCreationOptions options)
+ public async Task<BoxSet> CreateCollectionAsync(CollectionCreationOptions options)
{
var name = options.Name;
@@ -141,7 +141,7 @@ namespace Emby.Server.Implementations.Collections
// This could cause it to get re-resolved as a plain folder
var folderName = _fileSystem.GetValidFilename(name) + " [boxset]";
- var parentFolder = GetCollectionsFolder(true).GetAwaiter().GetResult();
+ var parentFolder = await GetCollectionsFolder(true).ConfigureAwait(false);
if (parentFolder == null)
{
@@ -169,12 +169,16 @@ namespace Emby.Server.Implementations.Collections
if (options.ItemIdList.Length > 0)
{
- AddToCollection(collection.Id, options.ItemIdList, false, new MetadataRefreshOptions(new DirectoryService(_fileSystem))
- {
- // The initial adding of items is going to create a local metadata file
- // This will cause internet metadata to be skipped as a result
- MetadataRefreshMode = MetadataRefreshMode.FullRefresh
- });
+ await AddToCollectionAsync(
+ collection.Id,
+ options.ItemIdList.Select(x => new Guid(x)),
+ false,
+ new MetadataRefreshOptions(new DirectoryService(_fileSystem))
+ {
+ // The initial adding of items is going to create a local metadata file
+ // This will cause internet metadata to be skipped as a result
+ MetadataRefreshMode = MetadataRefreshMode.FullRefresh
+ }).ConfigureAwait(false);
}
else
{
@@ -197,18 +201,10 @@ namespace Emby.Server.Implementations.Collections
}
/// <inheritdoc />
- public void AddToCollection(Guid collectionId, IEnumerable<string> ids)
- {
- AddToCollection(collectionId, ids, true, new MetadataRefreshOptions(new DirectoryService(_fileSystem)));
- }
+ public Task AddToCollectionAsync(Guid collectionId, IEnumerable<Guid> ids)
+ => AddToCollectionAsync(collectionId, ids, true, new MetadataRefreshOptions(new DirectoryService(_fileSystem)));
- /// <inheritdoc />
- public void AddToCollection(Guid collectionId, IEnumerable<Guid> ids)
- {
- AddToCollection(collectionId, ids.Select(i => i.ToString("N", CultureInfo.InvariantCulture)), true, new MetadataRefreshOptions(new DirectoryService(_fileSystem)));
- }
-
- private void AddToCollection(Guid collectionId, IEnumerable<string> ids, bool fireEvent, MetadataRefreshOptions refreshOptions)
+ private async Task AddToCollectionAsync(Guid collectionId, IEnumerable<Guid> ids, bool fireEvent, MetadataRefreshOptions refreshOptions)
{
var collection = _libraryManager.GetItemById(collectionId) as BoxSet;
if (collection == null)
@@ -224,15 +220,14 @@ namespace Emby.Server.Implementations.Collections
foreach (var id in ids)
{
- var guidId = new Guid(id);
- var item = _libraryManager.GetItemById(guidId);
+ var item = _libraryManager.GetItemById(id);
if (item == null)
{
throw new ArgumentException("No item exists with the supplied Id");
}
- if (!currentLinkedChildrenIds.Contains(guidId))
+ if (!currentLinkedChildrenIds.Contains(id))
{
itemList.Add(item);
@@ -249,7 +244,7 @@ namespace Emby.Server.Implementations.Collections
collection.UpdateRatingToItems(linkedChildrenList);
- collection.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
+ await collection.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
refreshOptions.ForceSave = true;
_providerManager.QueueRefresh(collection.Id, refreshOptions, RefreshPriority.High);
@@ -266,13 +261,7 @@ namespace Emby.Server.Implementations.Collections
}
/// <inheritdoc />
- public void RemoveFromCollection(Guid collectionId, IEnumerable<string> itemIds)
- {
- RemoveFromCollection(collectionId, itemIds.Select(i => new Guid(i)));
- }
-
- /// <inheritdoc />
- public void RemoveFromCollection(Guid collectionId, IEnumerable<Guid> itemIds)
+ public async Task RemoveFromCollectionAsync(Guid collectionId, IEnumerable<Guid> itemIds)
{
var collection = _libraryManager.GetItemById(collectionId) as BoxSet;
@@ -309,7 +298,7 @@ namespace Emby.Server.Implementations.Collections
collection.LinkedChildren = collection.LinkedChildren.Except(list).ToArray();
}
- collection.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
+ await collection.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
_providerManager.QueueRefresh(
collection.Id,
new MetadataRefreshOptions(new DirectoryService(_fileSystem))
diff --git a/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs b/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs
index a15295fca..f05a30a89 100644
--- a/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs
+++ b/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs
@@ -2,11 +2,11 @@ using System;
using System.Globalization;
using System.IO;
using Emby.Server.Implementations.AppBase;
+using Jellyfin.Data.Events;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
using Microsoft.Extensions.Logging;
diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
index d11e5e62e..5bf740cfc 100644
--- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
@@ -4308,7 +4308,7 @@ namespace Emby.Server.Implementations.Data
whereClauses.Add("ProductionYear=@Years");
if (statement != null)
{
- statement.TryBind("@Years", query.Years[0].ToString());
+ statement.TryBind("@Years", query.Years[0].ToString(CultureInfo.InvariantCulture));
}
}
else if (query.Years.Length > 1)
@@ -4560,13 +4560,13 @@ namespace Emby.Server.Implementations.Data
if (query.AncestorIds.Length > 1)
{
var inClause = string.Join(",", query.AncestorIds.Select(i => "'" + i.ToString("N", CultureInfo.InvariantCulture) + "'"));
- whereClauses.Add(string.Format("Guid in (select itemId from AncestorIds where AncestorIdText in ({0}))", inClause));
+ whereClauses.Add(string.Format(CultureInfo.InvariantCulture, "Guid in (select itemId from AncestorIds where AncestorIdText in ({0}))", inClause));
}
if (!string.IsNullOrWhiteSpace(query.AncestorWithPresentationUniqueKey))
{
var inClause = "select guid from TypedBaseItems where PresentationUniqueKey=@AncestorWithPresentationUniqueKey";
- whereClauses.Add(string.Format("Guid in (select itemId from AncestorIds where AncestorId in ({0}))", inClause));
+ whereClauses.Add(string.Format(CultureInfo.InvariantCulture, "Guid in (select itemId from AncestorIds where AncestorId in ({0}))", inClause));
if (statement != null)
{
statement.TryBind("@AncestorWithPresentationUniqueKey", query.AncestorWithPresentationUniqueKey);
@@ -5170,7 +5170,10 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
insertText.Append(',');
}
- insertText.AppendFormat("(@ItemId, @AncestorId{0}, @AncestorIdText{0})", i.ToString(CultureInfo.InvariantCulture));
+ insertText.AppendFormat(
+ CultureInfo.InvariantCulture,
+ "(@ItemId, @AncestorId{0}, @AncestorIdText{0})",
+ i.ToString(CultureInfo.InvariantCulture));
}
using (var statement = PrepareStatement(db, insertText.ToString()))
diff --git a/Emby.Server.Implementations/Devices/DeviceManager.cs b/Emby.Server.Implementations/Devices/DeviceManager.cs
index cc4b407f5..f98c694c4 100644
--- a/Emby.Server.Implementations/Devices/DeviceManager.cs
+++ b/Emby.Server.Implementations/Devices/DeviceManager.cs
@@ -7,13 +7,13 @@ using System.IO;
using System.Linq;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
+using Jellyfin.Data.Events;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Security;
using MediaBrowser.Model.Devices;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Session;
diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
index 1adef68aa..56fc57327 100644
--- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj
+++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
@@ -22,7 +22,7 @@
</ItemGroup>
<ItemGroup>
- <PackageReference Include="IPNetwork2" Version="2.5.211" />
+ <PackageReference Include="IPNetwork2" Version="2.5.224" />
<PackageReference Include="Jellyfin.XmlTv" Version="10.6.2" />
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.2.7" />
<PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="2.2.0" />
@@ -37,11 +37,11 @@
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.6" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.6" />
<PackageReference Include="Mono.Nat" Version="2.0.2" />
- <PackageReference Include="prometheus-net.DotNetRuntime" Version="3.3.1" />
+ <PackageReference Include="prometheus-net.DotNetRuntime" Version="3.4.0" />
<PackageReference Include="ServiceStack.Text.Core" Version="5.9.2" />
<PackageReference Include="sharpcompress" Version="0.26.0" />
<PackageReference Include="SQLitePCL.pretty.netstandard" Version="2.1.0" />
- <PackageReference Include="DotNet.Glob" Version="3.0.9" />
+ <PackageReference Include="DotNet.Glob" Version="3.1.0" />
</ItemGroup>
<ItemGroup>
diff --git a/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs b/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
index 9fce49425..2e8cc76d2 100644
--- a/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
+++ b/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
@@ -7,11 +7,11 @@ using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Events;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Events;
using Microsoft.Extensions.Logging;
using Mono.Nat;
diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
index 1deef7f72..c9d21d963 100644
--- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
@@ -7,6 +7,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
+using Jellyfin.Data.Events;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
@@ -15,7 +16,6 @@ using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Events;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.EntryPoints
diff --git a/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs b/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs
index 632735910..44d2580d6 100644
--- a/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs
@@ -5,6 +5,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Enums;
+using Jellyfin.Data.Events;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Plugins;
@@ -43,22 +44,22 @@ namespace Emby.Server.Implementations.EntryPoints
return Task.CompletedTask;
}
- private async void OnLiveTvManagerSeriesTimerCreated(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e)
+ private async void OnLiveTvManagerSeriesTimerCreated(object sender, GenericEventArgs<TimerEventInfo> e)
{
await SendMessage("SeriesTimerCreated", e.Argument).ConfigureAwait(false);
}
- private async void OnLiveTvManagerTimerCreated(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e)
+ private async void OnLiveTvManagerTimerCreated(object sender, GenericEventArgs<TimerEventInfo> e)
{
await SendMessage("TimerCreated", e.Argument).ConfigureAwait(false);
}
- private async void OnLiveTvManagerSeriesTimerCancelled(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e)
+ private async void OnLiveTvManagerSeriesTimerCancelled(object sender, GenericEventArgs<TimerEventInfo> e)
{
await SendMessage("SeriesTimerCancelled", e.Argument).ConfigureAwait(false);
}
- private async void OnLiveTvManagerTimerCancelled(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e)
+ private async void OnLiveTvManagerTimerCancelled(object sender, GenericEventArgs<TimerEventInfo> e)
{
await SendMessage("TimerCancelled", e.Argument).ConfigureAwait(false);
}
diff --git a/Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs b/Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs
deleted file mode 100644
index 826d4d8dc..000000000
--- a/Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs
+++ /dev/null
@@ -1,210 +0,0 @@
-using System;
-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.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
-{
- /// <summary>
- /// Class WebSocketEvents.
- /// </summary>
- public class ServerEventNotifier : IServerEntryPoint
- {
- /// <summary>
- /// The user manager.
- /// </summary>
- private readonly IUserManager _userManager;
-
- /// <summary>
- /// The installation manager.
- /// </summary>
- private readonly IInstallationManager _installationManager;
-
- /// <summary>
- /// The kernel.
- /// </summary>
- private readonly IServerApplicationHost _appHost;
-
- /// <summary>
- /// The task manager.
- /// </summary>
- private readonly ITaskManager _taskManager;
-
- private readonly ISessionManager _sessionManager;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ServerEventNotifier"/> class.
- /// </summary>
- /// <param name="appHost">The application host.</param>
- /// <param name="userManager">The user manager.</param>
- /// <param name="installationManager">The installation manager.</param>
- /// <param name="taskManager">The task manager.</param>
- /// <param name="sessionManager">The session manager.</param>
- public ServerEventNotifier(
- IServerApplicationHost appHost,
- IUserManager userManager,
- IInstallationManager installationManager,
- ITaskManager taskManager,
- ISessionManager sessionManager)
- {
- _userManager = userManager;
- _installationManager = installationManager;
- _appHost = appHost;
- _taskManager = taskManager;
- _sessionManager = sessionManager;
- }
-
- /// <inheritdoc />
- public Task RunAsync()
- {
- _userManager.OnUserDeleted += OnUserDeleted;
- _userManager.OnUserUpdated += OnUserUpdated;
-
- _appHost.HasPendingRestartChanged += OnHasPendingRestartChanged;
-
- _installationManager.PluginUninstalled += OnPluginUninstalled;
- _installationManager.PackageInstalling += OnPackageInstalling;
- _installationManager.PackageInstallationCancelled += OnPackageInstallationCancelled;
- _installationManager.PackageInstallationCompleted += OnPackageInstallationCompleted;
- _installationManager.PackageInstallationFailed += OnPackageInstallationFailed;
-
- _taskManager.TaskCompleted += OnTaskCompleted;
-
- return Task.CompletedTask;
- }
-
- private async void OnPackageInstalling(object sender, InstallationInfo e)
- {
- await SendMessageToAdminSessions("PackageInstalling", e).ConfigureAwait(false);
- }
-
- private async void OnPackageInstallationCancelled(object sender, InstallationInfo e)
- {
- await SendMessageToAdminSessions("PackageInstallationCancelled", e).ConfigureAwait(false);
- }
-
- private async void OnPackageInstallationCompleted(object sender, InstallationInfo e)
- {
- await SendMessageToAdminSessions("PackageInstallationCompleted", e).ConfigureAwait(false);
- }
-
- private async void OnPackageInstallationFailed(object sender, InstallationFailedEventArgs e)
- {
- await SendMessageToAdminSessions("PackageInstallationFailed", e.InstallationInfo).ConfigureAwait(false);
- }
-
- private async void OnTaskCompleted(object sender, TaskCompletionEventArgs e)
- {
- await SendMessageToAdminSessions("ScheduledTaskEnded", e.Result).ConfigureAwait(false);
- }
-
- /// <summary>
- /// Installations the manager_ plugin uninstalled.
- /// </summary>
- /// <param name="sender">The sender.</param>
- /// <param name="e">The e.</param>
- private async void OnPluginUninstalled(object sender, IPlugin e)
- {
- await SendMessageToAdminSessions("PluginUninstalled", e).ConfigureAwait(false);
- }
-
- /// <summary>
- /// Handles the HasPendingRestartChanged event of the kernel control.
- /// </summary>
- /// <param name="sender">The source of the event.</param>
- /// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
- private async void OnHasPendingRestartChanged(object sender, EventArgs e)
- {
- await _sessionManager.SendRestartRequiredNotification(CancellationToken.None).ConfigureAwait(false);
- }
-
- /// <summary>
- /// Users the manager_ user updated.
- /// </summary>
- /// <param name="sender">The sender.</param>
- /// <param name="e">The e.</param>
- private async void OnUserUpdated(object sender, GenericEventArgs<User> e)
- {
- var dto = _userManager.GetUserDto(e.Argument);
-
- await SendMessageToUserSession(e.Argument, "UserUpdated", dto).ConfigureAwait(false);
- }
-
- /// <summary>
- /// Users the manager_ user deleted.
- /// </summary>
- /// <param name="sender">The sender.</param>
- /// <param name="e">The e.</param>
- private async void OnUserDeleted(object sender, GenericEventArgs<User> e)
- {
- await SendMessageToUserSession(e.Argument, "UserDeleted", e.Argument.Id.ToString("N", CultureInfo.InvariantCulture)).ConfigureAwait(false);
- }
-
- private async Task SendMessageToAdminSessions<T>(string name, T data)
- {
- try
- {
- await _sessionManager.SendMessageToAdminSessions(name, data, CancellationToken.None).ConfigureAwait(false);
- }
- catch (Exception)
- {
- }
- }
-
- private async Task SendMessageToUserSession<T>(User user, string name, T data)
- {
- try
- {
- await _sessionManager.SendMessageToUserSessions(
- new List<Guid> { user.Id },
- name,
- data,
- CancellationToken.None).ConfigureAwait(false);
- }
- catch (Exception)
- {
- }
- }
-
- /// <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)
- {
- _userManager.OnUserDeleted -= OnUserDeleted;
- _userManager.OnUserUpdated -= OnUserUpdated;
-
- _installationManager.PluginUninstalled -= OnPluginUninstalled;
- _installationManager.PackageInstalling -= OnPackageInstalling;
- _installationManager.PackageInstallationCancelled -= OnPackageInstallationCancelled;
- _installationManager.PackageInstallationCompleted -= OnPackageInstallationCompleted;
- _installationManager.PackageInstallationFailed -= OnPackageInstallationFailed;
-
- _appHost.HasPendingRestartChanged -= OnHasPendingRestartChanged;
-
- _taskManager.TaskCompleted -= OnTaskCompleted;
- }
- }
- }
-}
diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
index dafdd5b7b..fe39bb4b2 100644
--- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
+++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
@@ -12,13 +12,13 @@ using System.Threading;
using System.Threading.Tasks;
using Emby.Server.Implementations.Services;
using Emby.Server.Implementations.SocketSharp;
+using Jellyfin.Data.Events;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Net;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Services;
diff --git a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs
index d738047e0..7eae4e764 100644
--- a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs
+++ b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs
@@ -179,7 +179,7 @@ namespace Emby.Server.Implementations.HttpServer
return;
}
- WebSocketMessage<object> stub;
+ WebSocketMessage<object>? stub;
try
{
@@ -209,6 +209,12 @@ namespace Emby.Server.Implementations.HttpServer
return;
}
+ if (stub == null)
+ {
+ _logger.LogError("Error processing web socket message");
+ return;
+ }
+
// Tell the PipeReader how much of the buffer we have consumed
reader.AdvanceTo(buffer.End);
diff --git a/Emby.Server.Implementations/IO/LibraryMonitor.cs b/Emby.Server.Implementations/IO/LibraryMonitor.cs
index a32b03aaa..9290dfcd0 100644
--- a/Emby.Server.Implementations/IO/LibraryMonitor.cs
+++ b/Emby.Server.Implementations/IO/LibraryMonitor.cs
@@ -6,12 +6,11 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
+using Emby.Server.Implementations.Library;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Plugins;
using MediaBrowser.Model.IO;
-using Emby.Server.Implementations.Library;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.IO
@@ -38,6 +37,8 @@ namespace Emby.Server.Implementations.IO
/// </summary>
private readonly ConcurrentDictionary<string, string> _tempIgnoredPaths = new ConcurrentDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+ private bool _disposed = false;
+
/// <summary>
/// Add the path to our temporary ignore list. Use when writing to a path within our listening scope.
/// </summary>
@@ -492,8 +493,6 @@ namespace Emby.Server.Implementations.IO
}
}
- private bool _disposed = false;
-
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
@@ -522,24 +521,4 @@ namespace Emby.Server.Implementations.IO
_disposed = true;
}
}
-
- public class LibraryMonitorStartup : IServerEntryPoint
- {
- private readonly ILibraryMonitor _monitor;
-
- public LibraryMonitorStartup(ILibraryMonitor monitor)
- {
- _monitor = monitor;
- }
-
- public Task RunAsync()
- {
- _monitor.Start();
- return Task.CompletedTask;
- }
-
- public void Dispose()
- {
- }
- }
}
diff --git a/Emby.Server.Implementations/IO/LibraryMonitorStartup.cs b/Emby.Server.Implementations/IO/LibraryMonitorStartup.cs
new file mode 100644
index 000000000..c51cf0545
--- /dev/null
+++ b/Emby.Server.Implementations/IO/LibraryMonitorStartup.cs
@@ -0,0 +1,35 @@
+using System.Threading.Tasks;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Plugins;
+
+namespace Emby.Server.Implementations.IO
+{
+ /// <summary>
+ /// <see cref="IServerEntryPoint" /> which is responsible for starting the library monitor.
+ /// </summary>
+ public sealed class LibraryMonitorStartup : IServerEntryPoint
+ {
+ private readonly ILibraryMonitor _monitor;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="LibraryMonitorStartup"/> class.
+ /// </summary>
+ /// <param name="monitor">The library monitor.</param>
+ public LibraryMonitorStartup(ILibraryMonitor monitor)
+ {
+ _monitor = monitor;
+ }
+
+ /// <inheritdoc />
+ public Task RunAsync()
+ {
+ _monitor.Start();
+ return Task.CompletedTask;
+ }
+
+ /// <inheritdoc />
+ public void Dispose()
+ {
+ }
+ }
+}
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index 7b770d940..375f09f5b 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -729,7 +729,7 @@ namespace Emby.Server.Implementations.Library
Directory.CreateDirectory(rootFolderPath);
var rootFolder = GetItemById(GetNewItemId(rootFolderPath, typeof(AggregateFolder))) as AggregateFolder ??
- ((Folder) ResolvePath(_fileSystem.GetDirectoryInfo(rootFolderPath)))
+ ((Folder)ResolvePath(_fileSystem.GetDirectoryInfo(rootFolderPath)))
.DeepCopy<Folder, AggregateFolder>();
// In case program data folder was moved
@@ -771,7 +771,7 @@ namespace Emby.Server.Implementations.Library
if (folder.ParentId != rootFolder.Id)
{
folder.ParentId = rootFolder.Id;
- folder.UpdateToRepository(ItemUpdateType.MetadataImport, CancellationToken.None);
+ folder.UpdateToRepositoryAsync(ItemUpdateType.MetadataImport, CancellationToken.None).GetAwaiter().GetResult();
}
rootFolder.AddVirtualChild(folder);
@@ -1868,7 +1868,8 @@ namespace Emby.Server.Implementations.Library
return image.Path != null && !image.IsLocalFile;
}
- public void UpdateImages(BaseItem item, bool forceUpdate = false)
+ /// <inheritdoc />
+ public async Task UpdateImagesAsync(BaseItem item, bool forceUpdate = false)
{
if (item == null)
{
@@ -1891,7 +1892,7 @@ namespace Emby.Server.Implementations.Library
try
{
var index = item.GetImageIndex(img);
- image = ConvertImageToLocal(item, img, index).ConfigureAwait(false).GetAwaiter().GetResult();
+ image = await ConvertImageToLocal(item, img, index).ConfigureAwait(false);
}
catch (ArgumentException)
{
@@ -1913,7 +1914,7 @@ namespace Emby.Server.Implementations.Library
}
catch (Exception ex)
{
- _logger.LogError(ex, "Cannnot get image dimensions for {0}", image.Path);
+ _logger.LogError(ex, "Cannot get image dimensions for {0}", image.Path);
image.Width = 0;
image.Height = 0;
continue;
@@ -1943,10 +1944,8 @@ namespace Emby.Server.Implementations.Library
RegisterItem(item);
}
- /// <summary>
- /// Updates the item.
- /// </summary>
- public void UpdateItems(IReadOnlyList<BaseItem> items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
+ /// <inheritdoc />
+ public async Task UpdateItemsAsync(IReadOnlyList<BaseItem> items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
{
foreach (var item in items)
{
@@ -1957,7 +1956,7 @@ namespace Emby.Server.Implementations.Library
item.DateLastSaved = DateTime.UtcNow;
- UpdateImages(item, updateReason >= ItemUpdateType.ImageUpdate);
+ await UpdateImagesAsync(item, updateReason >= ItemUpdateType.ImageUpdate).ConfigureAwait(false);
}
_itemRepository.SaveItems(items, cancellationToken);
@@ -1991,17 +1990,9 @@ namespace Emby.Server.Implementations.Library
}
}
- /// <summary>
- /// Updates the item.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="parent">The parent item.</param>
- /// <param name="updateReason">The update reason.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- public void UpdateItem(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
- {
- UpdateItems(new[] { item }, parent, updateReason, cancellationToken);
- }
+ /// <inheritdoc />
+ public Task UpdateItemAsync(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
+ => UpdateItemsAsync(new[] { item }, parent, updateReason, cancellationToken);
/// <summary>
/// Reports the item removed.
@@ -2233,7 +2224,7 @@ namespace Emby.Server.Implementations.Library
if (refresh)
{
- item.UpdateToRepository(ItemUpdateType.MetadataImport, CancellationToken.None);
+ item.UpdateToRepositoryAsync(ItemUpdateType.MetadataImport, CancellationToken.None).GetAwaiter().GetResult();
ProviderManager.QueueRefresh(item.Id, new MetadataRefreshOptions(new DirectoryService(_fileSystem)), RefreshPriority.Normal);
}
@@ -2420,7 +2411,7 @@ namespace Emby.Server.Implementations.Library
if (!string.Equals(viewType, item.ViewType, StringComparison.OrdinalIgnoreCase))
{
item.ViewType = viewType;
- item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
+ item.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).GetAwaiter().GetResult();
}
var refresh = isNew || DateTime.UtcNow - item.DateLastRefreshed >= _viewRefreshInterval;
@@ -2902,7 +2893,7 @@ namespace Emby.Server.Implementations.Library
await ProviderManager.SaveImage(item, url, image.Type, imageIndex, CancellationToken.None).ConfigureAwait(false);
- item.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None);
+ await item.UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false);
return item.GetImageInfo(image.Type, imageIndex);
}
@@ -2920,7 +2911,7 @@ namespace Emby.Server.Implementations.Library
// Remove this image to prevent it from retrying over and over
item.RemoveImage(image);
- item.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None);
+ await item.UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false);
throw new InvalidOperationException();
}
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
index 80e09f0a3..09c52d95b 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
@@ -13,6 +13,7 @@ using System.Threading.Tasks;
using System.Xml;
using Emby.Server.Implementations.Library;
using Jellyfin.Data.Enums;
+using Jellyfin.Data.Events;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
@@ -29,7 +30,6 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.MediaInfo;
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
index d8ec107ec..612dc5238 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
@@ -230,7 +230,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
if (filters.Count > 0)
{
- output += string.Format(" -vf \"{0}\"", string.Join(",", filters.ToArray()));
+ output += string.Format(CultureInfo.InvariantCulture, " -vf \"{0}\"", string.Join(",", filters.ToArray()));
}
return output;
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs
index 69a9cb78a..a2ec2df37 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs
@@ -5,7 +5,7 @@ using MediaBrowser.Controller.Plugins;
namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
- public class EntryPoint : IServerEntryPoint
+ public sealed class EntryPoint : IServerEntryPoint
{
/// <inheritdoc />
public Task RunAsync()
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
index 285a59a24..dd479b7d1 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
@@ -5,8 +5,8 @@ using System.Collections.Concurrent;
using System.Globalization;
using System.Linq;
using System.Threading;
+using Jellyfin.Data.Events;
using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Serialization;
using Microsoft.Extensions.Logging;
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
index 77a7069eb..c4d5cc58a 100644
--- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
@@ -929,7 +929,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
private static string NormalizeName(string value)
{
- return value.Replace(" ", string.Empty).Replace("-", string.Empty);
+ return value.Replace(" ", string.Empty, StringComparison.Ordinal).Replace("-", string.Empty, StringComparison.Ordinal);
}
public class ScheduleDirect
diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
index 0a93c4674..f33d07174 100644
--- a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
+++ b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
@@ -237,7 +237,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
&& !programInfo.IsRepeat
&& (programInfo.EpisodeNumber ?? 0) == 0)
{
- programInfo.ShowId = programInfo.ShowId + programInfo.StartDate.Ticks.ToString(CultureInfo.InvariantCulture);
+ programInfo.ShowId += programInfo.StartDate.Ticks.ToString(CultureInfo.InvariantCulture);
}
}
else
@@ -246,7 +246,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
}
// Construct an id from the channel and start date
- programInfo.Id = string.Format("{0}_{1:O}", program.ChannelId, program.StartDate);
+ programInfo.Id = string.Format(CultureInfo.InvariantCulture, "{0}_{1:O}", program.ChannelId, program.StartDate);
if (programInfo.IsMovie)
{
@@ -296,7 +296,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
Name = c.DisplayName,
ImageUrl = c.Icon != null && !string.IsNullOrEmpty(c.Icon.Source) ? c.Icon.Source : null,
Number = string.IsNullOrWhiteSpace(c.Number) ? c.Id : c.Number
-
}).ToList();
}
}
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
index 1b075d86a..a898a564f 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
@@ -10,6 +10,7 @@ using System.Threading.Tasks;
using Emby.Server.Implementations.Library;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
+using Jellyfin.Data.Events;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Progress;
@@ -24,7 +25,6 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.LiveTv;
@@ -41,6 +41,7 @@ namespace Emby.Server.Implementations.LiveTv
/// </summary>
public class LiveTvManager : ILiveTvManager, IDisposable
{
+ private const int MaxGuideDays = 14;
private const string ExternalServiceTag = "ExternalServiceId";
private const string EtagKey = "ProgramEtag";
@@ -421,7 +422,7 @@ namespace Emby.Server.Implementations.LiveTv
}
}
- private LiveTvChannel GetChannel(ChannelInfo channelInfo, string serviceName, BaseItem parentFolder, CancellationToken cancellationToken)
+ private async Task<LiveTvChannel> GetChannelAsync(ChannelInfo channelInfo, string serviceName, BaseItem parentFolder, CancellationToken cancellationToken)
{
var parentFolderId = parentFolder.Id;
var isNew = false;
@@ -511,7 +512,7 @@ namespace Emby.Server.Implementations.LiveTv
}
else if (forceUpdate)
{
- _libraryManager.UpdateItem(item, parentFolder, ItemUpdateType.MetadataImport, cancellationToken);
+ await _libraryManager.UpdateItemAsync(item, parentFolder, ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false);
}
return item;
@@ -560,7 +561,7 @@ namespace Emby.Server.Implementations.LiveTv
item.Audio = info.Audio;
item.ChannelId = channel.Id;
- item.CommunityRating = item.CommunityRating ?? info.CommunityRating;
+ item.CommunityRating ??= info.CommunityRating;
if ((item.CommunityRating ?? 0).Equals(0))
{
item.CommunityRating = null;
@@ -645,8 +646,8 @@ namespace Emby.Server.Implementations.LiveTv
item.IsSeries = isSeries;
item.Name = info.Name;
- item.OfficialRating = item.OfficialRating ?? info.OfficialRating;
- item.Overview = item.Overview ?? info.Overview;
+ item.OfficialRating ??= info.OfficialRating;
+ item.Overview ??= info.Overview;
item.RunTimeTicks = (info.EndDate - info.StartDate).Ticks;
item.ProviderIds = info.ProviderIds;
@@ -683,19 +684,23 @@ namespace Emby.Server.Implementations.LiveTv
{
if (!string.IsNullOrWhiteSpace(info.ImagePath))
{
- item.SetImage(new ItemImageInfo
- {
- Path = info.ImagePath,
- Type = ImageType.Primary
- }, 0);
+ item.SetImage(
+ new ItemImageInfo
+ {
+ Path = info.ImagePath,
+ Type = ImageType.Primary
+ },
+ 0);
}
else if (!string.IsNullOrWhiteSpace(info.ImageUrl))
{
- item.SetImage(new ItemImageInfo
- {
- Path = info.ImageUrl,
- Type = ImageType.Primary
- }, 0);
+ item.SetImage(
+ new ItemImageInfo
+ {
+ Path = info.ImageUrl,
+ Type = ImageType.Primary
+ },
+ 0);
}
}
@@ -703,11 +708,13 @@ namespace Emby.Server.Implementations.LiveTv
{
if (!string.IsNullOrWhiteSpace(info.ThumbImageUrl))
{
- item.SetImage(new ItemImageInfo
- {
- Path = info.ThumbImageUrl,
- Type = ImageType.Thumb
- }, 0);
+ item.SetImage(
+ new ItemImageInfo
+ {
+ Path = info.ThumbImageUrl,
+ Type = ImageType.Thumb
+ },
+ 0);
}
}
@@ -715,11 +722,13 @@ namespace Emby.Server.Implementations.LiveTv
{
if (!string.IsNullOrWhiteSpace(info.LogoImageUrl))
{
- item.SetImage(new ItemImageInfo
- {
- Path = info.LogoImageUrl,
- Type = ImageType.Logo
- }, 0);
+ item.SetImage(
+ new ItemImageInfo
+ {
+ Path = info.LogoImageUrl,
+ Type = ImageType.Logo
+ },
+ 0);
}
}
@@ -727,11 +736,13 @@ namespace Emby.Server.Implementations.LiveTv
{
if (!string.IsNullOrWhiteSpace(info.BackdropImageUrl))
{
- item.SetImage(new ItemImageInfo
- {
- Path = info.BackdropImageUrl,
- Type = ImageType.Backdrop
- }, 0);
+ item.SetImage(
+ new ItemImageInfo
+ {
+ Path = info.BackdropImageUrl,
+ Type = ImageType.Backdrop
+ },
+ 0);
}
}
@@ -786,7 +797,6 @@ namespace Emby.Server.Implementations.LiveTv
if (query.OrderBy.Count == 0)
{
-
// Unless something else was specified, order by start date to take advantage of a specialized index
query.OrderBy = new[]
{
@@ -824,7 +834,7 @@ namespace Emby.Server.Implementations.LiveTv
if (!string.IsNullOrWhiteSpace(query.SeriesTimerId))
{
- var seriesTimers = await GetSeriesTimersInternal(new SeriesTimerQuery { }, cancellationToken).ConfigureAwait(false);
+ var seriesTimers = await GetSeriesTimersInternal(new SeriesTimerQuery(), cancellationToken).ConfigureAwait(false);
var seriesTimer = seriesTimers.Items.FirstOrDefault(i => string.Equals(_tvDtoService.GetInternalSeriesTimerId(i.Id).ToString("N", CultureInfo.InvariantCulture), query.SeriesTimerId, StringComparison.OrdinalIgnoreCase));
if (seriesTimer != null)
{
@@ -847,13 +857,11 @@ namespace Emby.Server.Implementations.LiveTv
var returnArray = _dtoService.GetBaseItemDtos(queryResult.Items, options, user);
- var result = new QueryResult<BaseItemDto>
+ return new QueryResult<BaseItemDto>
{
Items = returnArray,
TotalRecordCount = queryResult.TotalRecordCount
};
-
- return result;
}
public QueryResult<BaseItem> GetRecommendedProgramsInternal(InternalItemsQuery query, DtoOptions options, CancellationToken cancellationToken)
@@ -1121,7 +1129,7 @@ namespace Emby.Server.Implementations.LiveTv
try
{
- var item = GetChannel(channelInfo.Item2, channelInfo.Item1, parentFolder, cancellationToken);
+ var item = await GetChannelAsync(channelInfo.Item2, channelInfo.Item1, parentFolder, cancellationToken).ConfigureAwait(false);
list.Add(item);
}
@@ -1138,7 +1146,7 @@ namespace Emby.Server.Implementations.LiveTv
double percent = numComplete;
percent /= allChannelsList.Count;
- progress.Report(5 * percent + 10);
+ progress.Report((5 * percent) + 10);
}
progress.Report(15);
@@ -1173,7 +1181,6 @@ namespace Emby.Server.Implementations.LiveTv
var existingPrograms = _libraryManager.GetItemList(new InternalItemsQuery
{
-
IncludeItemTypes = new string[] { typeof(LiveTvProgram).Name },
ChannelIds = new Guid[] { currentChannel.Id },
DtoOptions = new DtoOptions(true)
@@ -1214,7 +1221,11 @@ namespace Emby.Server.Implementations.LiveTv
if (updatedPrograms.Count > 0)
{
- _libraryManager.UpdateItems(updatedPrograms, currentChannel, ItemUpdateType.MetadataImport, cancellationToken);
+ await _libraryManager.UpdateItemsAsync(
+ updatedPrograms,
+ currentChannel,
+ ItemUpdateType.MetadataImport,
+ cancellationToken).ConfigureAwait(false);
}
currentChannel.IsMovie = isMovie;
@@ -1227,7 +1238,7 @@ namespace Emby.Server.Implementations.LiveTv
currentChannel.AddTag("Kids");
}
- currentChannel.UpdateToRepository(ItemUpdateType.MetadataImport, cancellationToken);
+ await currentChannel.UpdateToRepositoryAsync(ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false);
await currentChannel.RefreshMetadata(
new MetadataRefreshOptions(new DirectoryService(_fileSystem))
{
@@ -1298,8 +1309,6 @@ namespace Emby.Server.Implementations.LiveTv
}
}
- private const int MaxGuideDays = 14;
-
private double GetGuideDays()
{
var config = GetConfiguration();
@@ -1712,7 +1721,7 @@ namespace Emby.Server.Implementations.LiveTv
if (timer == null)
{
- throw new ResourceNotFoundException(string.Format("Timer with Id {0} not found", id));
+ throw new ResourceNotFoundException(string.Format(CultureInfo.InvariantCulture, "Timer with Id {0} not found", id));
}
var service = GetService(timer.ServiceName);
@@ -1731,7 +1740,7 @@ namespace Emby.Server.Implementations.LiveTv
if (timer == null)
{
- throw new ResourceNotFoundException(string.Format("SeriesTimer with Id {0} not found", id));
+ throw new ResourceNotFoundException(string.Format(CultureInfo.InvariantCulture, "SeriesTimer with Id {0} not found", id));
}
var service = GetService(timer.ServiceName);
@@ -1743,10 +1752,12 @@ namespace Emby.Server.Implementations.LiveTv
public async Task<TimerInfoDto> GetTimer(string id, CancellationToken cancellationToken)
{
- var results = await GetTimers(new TimerQuery
- {
- Id = id
- }, cancellationToken).ConfigureAwait(false);
+ var results = await GetTimers(
+ new TimerQuery
+ {
+ Id = id
+ },
+ cancellationToken).ConfigureAwait(false);
return results.Items.FirstOrDefault(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase));
}
@@ -1794,10 +1805,7 @@ namespace Emby.Server.Implementations.LiveTv
}
var returnArray = timers
- .Select(i =>
- {
- return i.Item1;
- })
+ .Select(i => i.Item1)
.ToArray();
return new QueryResult<SeriesTimerInfo>
@@ -1968,7 +1976,7 @@ namespace Emby.Server.Implementations.LiveTv
if (service == null)
{
- service = _services.First();
+ service = _services[0];
}
var info = await service.GetNewTimerDefaultsAsync(cancellationToken, programInfo).ConfigureAwait(false);
@@ -1994,9 +2002,7 @@ namespace Emby.Server.Implementations.LiveTv
{
var info = await GetNewTimerDefaultsInternal(cancellationToken).ConfigureAwait(false);
- var obj = _tvDtoService.GetSeriesTimerInfoDto(info.Item1, info.Item2, null);
-
- return obj;
+ return _tvDtoService.GetSeriesTimerInfoDto(info.Item1, info.Item2, null);
}
public async Task<SeriesTimerInfoDto> GetNewTimerDefaults(string programId, CancellationToken cancellationToken)
@@ -2125,6 +2131,7 @@ namespace Emby.Server.Implementations.LiveTv
public void Dispose()
{
Dispose(true);
+ GC.SuppressFinalize(this);
}
private bool _disposed = false;
@@ -2447,8 +2454,7 @@ namespace Emby.Server.Implementations.LiveTv
.SelectMany(i => i.Locations)
.Distinct(StringComparer.OrdinalIgnoreCase)
.Select(i => _libraryManager.FindByPath(i, true))
- .Where(i => i != null)
- .Where(i => i.IsVisibleStandalone(user))
+ .Where(i => i != null && i.IsVisibleStandalone(user))
.SelectMany(i => _libraryManager.GetCollectionFolders(i))
.GroupBy(x => x.Id)
.Select(x => x.First())
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
index c61189c0a..2b5f69d41 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
@@ -37,6 +37,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
private readonly INetworkManager _networkManager;
private readonly IStreamHelper _streamHelper;
+ private readonly Dictionary<string, DiscoverResponse> _modelCache = new Dictionary<string, DiscoverResponse>();
+
public HdHomerunHost(
IServerConfigurationManager config,
ILogger<HdHomerunHost> logger,
@@ -114,7 +116,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
}).Cast<ChannelInfo>().ToList();
}
- private readonly Dictionary<string, DiscoverResponse> _modelCache = new Dictionary<string, DiscoverResponse>();
private async Task<DiscoverResponse> GetModelInfo(TunerHostInfo info, bool throwAllExceptions, CancellationToken cancellationToken)
{
var cacheKey = info.Id;
@@ -157,10 +158,10 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
if (!throwAllExceptions && ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound)
{
- var defaultValue = "HDHR";
+ const string DefaultValue = "HDHR";
var response = new DiscoverResponse
{
- ModelNumber = defaultValue
+ ModelNumber = DefaultValue
};
if (!string.IsNullOrEmpty(cacheKey))
{
@@ -182,12 +183,14 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
var model = await GetModelInfo(info, false, cancellationToken).ConfigureAwait(false);
- using (var response = await _httpClient.SendAsync(new HttpRequestOptions()
- {
- Url = string.Format("{0}/tuners.html", GetApiUrl(info)),
- CancellationToken = cancellationToken,
- BufferContent = false
- }, HttpMethod.Get).ConfigureAwait(false))
+ using (var response = await _httpClient.SendAsync(
+ new HttpRequestOptions()
+ {
+ Url = string.Format(CultureInfo.InvariantCulture, "{0}/tuners.html", GetApiUrl(info)),
+ CancellationToken = cancellationToken,
+ BufferContent = false
+ },
+ HttpMethod.Get).ConfigureAwait(false))
using (var stream = response.Content)
using (var sr = new StreamReader(stream, System.Text.Encoding.UTF8))
{
@@ -730,7 +733,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
// Need a way to set the Receive timeout on the socket otherwise this might never timeout?
try
{
- await udpClient.SendToAsync(discBytes, 0, discBytes.Length, new IPEndPoint(IPAddress.Parse("255.255.255.255"), 65001), cancellationToken);
+ await udpClient.SendToAsync(discBytes, 0, discBytes.Length, new IPEndPoint(IPAddress.Parse("255.255.255.255"), 65001), cancellationToken).ConfigureAwait(false);
var receiveBuffer = new byte[8192];
while (!cancellationToken.IsCancellationRequested)
diff --git a/Emby.Server.Implementations/Localization/Core/id.json b/Emby.Server.Implementations/Localization/Core/id.json
index ccb72ff93..b0dfc312e 100644
--- a/Emby.Server.Implementations/Localization/Core/id.json
+++ b/Emby.Server.Implementations/Localization/Core/id.json
@@ -1,7 +1,7 @@
{
"Albums": "Album",
"AuthenticationSucceededWithUserName": "{0} berhasil diautentikasi",
- "AppDeviceValues": "Aplikasi: {0}, Alat: {1}",
+ "AppDeviceValues": "Aplikasi : {0}, Alat : {1}",
"LabelRunningTimeValue": "Waktu berjalan: {0}",
"MessageApplicationUpdatedTo": "Jellyfin Server sudah diperbarui ke {0}",
"MessageApplicationUpdated": "Jellyfin Server sudah diperbarui",
@@ -22,7 +22,7 @@
"HeaderContinueWatching": "Lanjutkan Menonton",
"HeaderCameraUploads": "Unggahan Kamera",
"HeaderAlbumArtists": "Album Artis",
- "Genres": "Genre",
+ "Genres": "Aliran",
"Folders": "Folder",
"Favorites": "Favorit",
"Collections": "Koleksi",
diff --git a/Emby.Server.Implementations/Localization/Core/ru.json b/Emby.Server.Implementations/Localization/Core/ru.json
index 71ee6446c..648aa384b 100644
--- a/Emby.Server.Implementations/Localization/Core/ru.json
+++ b/Emby.Server.Implementations/Localization/Core/ru.json
@@ -21,7 +21,7 @@
"HeaderFavoriteAlbums": "Избранные альбомы",
"HeaderFavoriteArtists": "Избранные исполнители",
"HeaderFavoriteEpisodes": "Избранные эпизоды",
- "HeaderFavoriteShows": "Избранные передачи",
+ "HeaderFavoriteShows": "Избранные сериалы",
"HeaderFavoriteSongs": "Избранные композиции",
"HeaderLiveTV": "Эфир",
"HeaderNextUp": "Очередное",
diff --git a/Emby.Server.Implementations/Localization/Core/th.json b/Emby.Server.Implementations/Localization/Core/th.json
index 576aaeb1b..42500670d 100644
--- a/Emby.Server.Implementations/Localization/Core/th.json
+++ b/Emby.Server.Implementations/Localization/Core/th.json
@@ -69,5 +69,8 @@
"AppDeviceValues": "App: {0}, อุปกรณ์: {1}",
"Albums": "อัลบั้ม",
"ScheduledTaskStartedWithName": "{0} เริ่มต้น",
- "ScheduledTaskFailedWithName": "{0} ล้มเหลว"
+ "ScheduledTaskFailedWithName": "{0} ล้มเหลว",
+ "Songs": "เพลง",
+ "Shows": "แสดง",
+ "ServerNameNeedsToBeRestarted": "{0} ต้องการรีสตาร์ท"
}
diff --git a/Emby.Server.Implementations/Playlists/PlaylistManager.cs b/Emby.Server.Implementations/Playlists/PlaylistManager.cs
index 38ceadedb..d3b64fb31 100644
--- a/Emby.Server.Implementations/Playlists/PlaylistManager.cs
+++ b/Emby.Server.Implementations/Playlists/PlaylistManager.cs
@@ -152,10 +152,10 @@ namespace Emby.Server.Implementations.Playlists
if (options.ItemIdList.Length > 0)
{
- AddToPlaylistInternal(playlist.Id.ToString("N", CultureInfo.InvariantCulture), options.ItemIdList, user, new DtoOptions(false)
+ await AddToPlaylistInternal(playlist.Id, options.ItemIdList, user, new DtoOptions(false)
{
EnableImages = true
- });
+ }).ConfigureAwait(false);
}
return new PlaylistCreationResult(playlist.Id.ToString("N", CultureInfo.InvariantCulture));
@@ -184,17 +184,17 @@ namespace Emby.Server.Implementations.Playlists
return Playlist.GetPlaylistItems(playlistMediaType, items, user, options);
}
- public void AddToPlaylist(string playlistId, ICollection<Guid> itemIds, Guid userId)
+ public Task AddToPlaylistAsync(Guid playlistId, ICollection<Guid> itemIds, Guid userId)
{
var user = userId.Equals(Guid.Empty) ? null : _userManager.GetUserById(userId);
- AddToPlaylistInternal(playlistId, itemIds, user, new DtoOptions(false)
+ return AddToPlaylistInternal(playlistId, itemIds, user, new DtoOptions(false)
{
EnableImages = true
});
}
- private void AddToPlaylistInternal(string playlistId, ICollection<Guid> newItemIds, User user, DtoOptions options)
+ private async Task AddToPlaylistInternal(Guid playlistId, ICollection<Guid> newItemIds, User user, DtoOptions options)
{
// Retrieve the existing playlist
var playlist = _libraryManager.GetItemById(playlistId) as Playlist
@@ -238,7 +238,7 @@ namespace Emby.Server.Implementations.Playlists
// Update the playlist in the repository
playlist.LinkedChildren = newLinkedChildren;
- playlist.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
+ await playlist.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
// Update the playlist on disk
if (playlist.IsFile)
@@ -256,7 +256,7 @@ namespace Emby.Server.Implementations.Playlists
RefreshPriority.High);
}
- public void RemoveFromPlaylist(string playlistId, IEnumerable<string> entryIds)
+ public async Task RemoveFromPlaylistAsync(string playlistId, IEnumerable<string> entryIds)
{
if (!(_libraryManager.GetItemById(playlistId) is Playlist playlist))
{
@@ -273,7 +273,7 @@ namespace Emby.Server.Implementations.Playlists
.Select(i => i.Item1)
.ToArray();
- playlist.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
+ await playlist.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
if (playlist.IsFile)
{
@@ -289,7 +289,7 @@ namespace Emby.Server.Implementations.Playlists
RefreshPriority.High);
}
- public void MoveItem(string playlistId, string entryId, int newIndex)
+ public async Task MoveItemAsync(string playlistId, string entryId, int newIndex)
{
if (!(_libraryManager.GetItemById(playlistId) is Playlist playlist))
{
@@ -322,7 +322,7 @@ namespace Emby.Server.Implementations.Playlists
playlist.LinkedChildren = newList.ToArray();
- playlist.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
+ await playlist.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
if (playlist.IsFile)
{
diff --git a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
index 8a900f42c..bc01f9543 100644
--- a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
@@ -6,11 +6,10 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Events;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Progress;
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Tasks;
using Microsoft.Extensions.Logging;
@@ -22,37 +21,53 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// </summary>
public class ScheduledTaskWorker : IScheduledTaskWorker
{
- public event EventHandler<GenericEventArgs<double>> TaskProgress;
-
- /// <summary>
- /// Gets the scheduled task.
- /// </summary>
- /// <value>The scheduled task.</value>
- public IScheduledTask ScheduledTask { get; private set; }
-
/// <summary>
/// Gets or sets the json serializer.
/// </summary>
/// <value>The json serializer.</value>
- private IJsonSerializer JsonSerializer { get; set; }
+ private readonly IJsonSerializer _jsonSerializer;
/// <summary>
/// Gets or sets the application paths.
/// </summary>
/// <value>The application paths.</value>
- private IApplicationPaths ApplicationPaths { get; set; }
+ private readonly IApplicationPaths _applicationPaths;
/// <summary>
- /// Gets the logger.
+ /// Gets or sets the logger.
/// </summary>
/// <value>The logger.</value>
- private ILogger Logger { get; set; }
+ private readonly ILogger _logger;
/// <summary>
- /// Gets the task manager.
+ /// Gets or sets the task manager.
/// </summary>
/// <value>The task manager.</value>
- private ITaskManager TaskManager { get; set; }
+ private readonly ITaskManager _taskManager;
+
+ /// <summary>
+ /// The _last execution result sync lock.
+ /// </summary>
+ private readonly object _lastExecutionResultSyncLock = new object();
+
+ private bool _readFromFile = false;
+
+ /// <summary>
+ /// The _last execution result.
+ /// </summary>
+ private TaskResult _lastExecutionResult;
+
+ private Task _currentTask;
+
+ /// <summary>
+ /// The _triggers.
+ /// </summary>
+ private Tuple<TaskTriggerInfo, ITaskTrigger>[] _triggers;
+
+ /// <summary>
+ /// The _id.
+ /// </summary>
+ private string _id;
/// <summary>
/// Initializes a new instance of the <see cref="ScheduledTaskWorker" /> class.
@@ -71,7 +86,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// or
/// jsonSerializer
/// or
- /// logger
+ /// logger.
/// </exception>
public ScheduledTaskWorker(IScheduledTask scheduledTask, IApplicationPaths applicationPaths, ITaskManager taskManager, IJsonSerializer jsonSerializer, ILogger logger)
{
@@ -101,23 +116,22 @@ namespace Emby.Server.Implementations.ScheduledTasks
}
ScheduledTask = scheduledTask;
- ApplicationPaths = applicationPaths;
- TaskManager = taskManager;
- JsonSerializer = jsonSerializer;
- Logger = logger;
+ _applicationPaths = applicationPaths;
+ _taskManager = taskManager;
+ _jsonSerializer = jsonSerializer;
+ _logger = logger;
InitTriggerEvents();
}
- private bool _readFromFile = false;
- /// <summary>
- /// The _last execution result.
- /// </summary>
- private TaskResult _lastExecutionResult;
+ public event EventHandler<GenericEventArgs<double>> TaskProgress;
+
/// <summary>
- /// The _last execution result sync lock.
+ /// Gets the scheduled task.
/// </summary>
- private readonly object _lastExecutionResultSyncLock = new object();
+ /// <value>The scheduled task.</value>
+ public IScheduledTask ScheduledTask { get; private set; }
+
/// <summary>
/// Gets the last execution result.
/// </summary>
@@ -136,11 +150,11 @@ namespace Emby.Server.Implementations.ScheduledTasks
{
try
{
- _lastExecutionResult = JsonSerializer.DeserializeFromFile<TaskResult>(path);
+ _lastExecutionResult = _jsonSerializer.DeserializeFromFile<TaskResult>(path);
}
catch (Exception ex)
{
- Logger.LogError(ex, "Error deserializing {File}", path);
+ _logger.LogError(ex, "Error deserializing {File}", path);
}
}
@@ -160,7 +174,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
lock (_lastExecutionResultSyncLock)
{
- JsonSerializer.SerializeToFile(value, path);
+ _jsonSerializer.SerializeToFile(value, path);
}
}
}
@@ -184,7 +198,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
public string Category => ScheduledTask.Category;
/// <summary>
- /// Gets the current cancellation token.
+ /// Gets or sets the current cancellation token.
/// </summary>
/// <value>The current cancellation token source.</value>
private CancellationTokenSource CurrentCancellationTokenSource { get; set; }
@@ -221,12 +235,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
public double? CurrentProgress { get; private set; }
/// <summary>
- /// The _triggers.
- /// </summary>
- private Tuple<TaskTriggerInfo, ITaskTrigger>[] _triggers;
-
- /// <summary>
- /// Gets the triggers that define when the task will run.
+ /// Gets or sets the triggers that define when the task will run.
/// </summary>
/// <value>The triggers.</value>
private Tuple<TaskTriggerInfo, ITaskTrigger>[] InternalTriggers
@@ -255,7 +264,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// Gets the triggers that define when the task will run.
/// </summary>
/// <value>The triggers.</value>
- /// <exception cref="ArgumentNullException">value</exception>
+ /// <exception cref="ArgumentNullException"><c>value</c> is <c>null</c>.</exception>
public TaskTriggerInfo[] Triggers
{
get
@@ -281,11 +290,6 @@ namespace Emby.Server.Implementations.ScheduledTasks
}
/// <summary>
- /// The _id.
- /// </summary>
- private string _id;
-
- /// <summary>
/// Gets the unique id.
/// </summary>
/// <value>The unique id.</value>
@@ -325,9 +329,9 @@ namespace Emby.Server.Implementations.ScheduledTasks
trigger.Stop();
- trigger.Triggered -= trigger_Triggered;
- trigger.Triggered += trigger_Triggered;
- trigger.Start(LastExecutionResult, Logger, Name, isApplicationStartup);
+ trigger.Triggered -= OnTriggerTriggered;
+ trigger.Triggered += OnTriggerTriggered;
+ trigger.Start(LastExecutionResult, _logger, Name, isApplicationStartup);
}
}
@@ -336,7 +340,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
- async void trigger_Triggered(object sender, EventArgs e)
+ private async void OnTriggerTriggered(object sender, EventArgs e)
{
var trigger = (ITaskTrigger)sender;
@@ -347,19 +351,17 @@ namespace Emby.Server.Implementations.ScheduledTasks
return;
}
- Logger.LogInformation("{0} fired for task: {1}", trigger.GetType().Name, Name);
+ _logger.LogInformation("{0} fired for task: {1}", trigger.GetType().Name, Name);
trigger.Stop();
- TaskManager.QueueScheduledTask(ScheduledTask, trigger.TaskOptions);
+ _taskManager.QueueScheduledTask(ScheduledTask, trigger.TaskOptions);
await Task.Delay(1000).ConfigureAwait(false);
- trigger.Start(LastExecutionResult, Logger, Name, false);
+ trigger.Start(LastExecutionResult, _logger, Name, false);
}
- private Task _currentTask;
-
/// <summary>
/// Executes the task.
/// </summary>
@@ -395,9 +397,9 @@ namespace Emby.Server.Implementations.ScheduledTasks
CurrentCancellationTokenSource = new CancellationTokenSource();
- Logger.LogInformation("Executing {0}", Name);
+ _logger.LogInformation("Executing {0}", Name);
- ((TaskManager)TaskManager).OnTaskExecuting(this);
+ ((TaskManager)_taskManager).OnTaskExecuting(this);
progress.ProgressChanged += OnProgressChanged;
@@ -423,7 +425,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
}
catch (Exception ex)
{
- Logger.LogError(ex, "Error");
+ _logger.LogError(ex, "Error");
failureException = ex;
@@ -476,7 +478,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
{
if (State == TaskState.Running)
{
- Logger.LogInformation("Attempting to cancel Scheduled Task {0}", Name);
+ _logger.LogInformation("Attempting to cancel Scheduled Task {0}", Name);
CurrentCancellationTokenSource.Cancel();
}
}
@@ -487,7 +489,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// <returns>System.String.</returns>
private string GetScheduledTasksConfigurationDirectory()
{
- return Path.Combine(ApplicationPaths.ConfigurationDirectoryPath, "ScheduledTasks");
+ return Path.Combine(_applicationPaths.ConfigurationDirectoryPath, "ScheduledTasks");
}
/// <summary>
@@ -496,7 +498,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// <returns>System.String.</returns>
private string GetScheduledTasksDataDirectory()
{
- return Path.Combine(ApplicationPaths.DataPath, "ScheduledTasks");
+ return Path.Combine(_applicationPaths.DataPath, "ScheduledTasks");
}
/// <summary>
@@ -535,7 +537,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
TaskTriggerInfo[] list = null;
if (File.Exists(path))
{
- list = JsonSerializer.DeserializeFromFile<TaskTriggerInfo[]>(path);
+ list = _jsonSerializer.DeserializeFromFile<TaskTriggerInfo[]>(path);
}
// Return defaults if file doesn't exist.
@@ -571,7 +573,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
Directory.CreateDirectory(Path.GetDirectoryName(path));
- JsonSerializer.SerializeToFile(triggers, path);
+ _jsonSerializer.SerializeToFile(triggers, path);
}
/// <summary>
@@ -585,7 +587,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
{
var elapsedTime = endTime - startTime;
- Logger.LogInformation("{0} {1} after {2} minute(s) and {3} seconds", Name, status, Math.Truncate(elapsedTime.TotalMinutes), elapsedTime.Seconds);
+ _logger.LogInformation("{0} {1} after {2} minute(s) and {3} seconds", Name, status, Math.Truncate(elapsedTime.TotalMinutes), elapsedTime.Seconds);
var result = new TaskResult
{
@@ -606,7 +608,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
LastExecutionResult = result;
- ((TaskManager)TaskManager).OnTaskCompleted(this, result);
+ ((TaskManager)_taskManager).OnTaskCompleted(this, result);
}
/// <summary>
@@ -615,6 +617,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
public void Dispose()
{
Dispose(true);
+ GC.SuppressFinalize(this);
}
/// <summary>
@@ -635,12 +638,12 @@ namespace Emby.Server.Implementations.ScheduledTasks
{
try
{
- Logger.LogInformation(Name + ": Cancelling");
+ _logger.LogInformation(Name + ": Cancelling");
token.Cancel();
}
catch (Exception ex)
{
- Logger.LogError(ex, "Error calling CancellationToken.Cancel();");
+ _logger.LogError(ex, "Error calling CancellationToken.Cancel();");
}
}
@@ -649,21 +652,21 @@ namespace Emby.Server.Implementations.ScheduledTasks
{
try
{
- Logger.LogInformation(Name + ": Waiting on Task");
+ _logger.LogInformation(Name + ": Waiting on Task");
var exited = Task.WaitAll(new[] { task }, 2000);
if (exited)
{
- Logger.LogInformation(Name + ": Task exited");
+ _logger.LogInformation(Name + ": Task exited");
}
else
{
- Logger.LogInformation(Name + ": Timed out waiting for task to stop");
+ _logger.LogInformation(Name + ": Timed out waiting for task to stop");
}
}
catch (Exception ex)
{
- Logger.LogError(ex, "Error calling Task.WaitAll();");
+ _logger.LogError(ex, "Error calling Task.WaitAll();");
}
}
@@ -671,12 +674,12 @@ namespace Emby.Server.Implementations.ScheduledTasks
{
try
{
- Logger.LogDebug(Name + ": Disposing CancellationToken");
+ _logger.LogDebug(Name + ": Disposing CancellationToken");
token.Dispose();
}
catch (Exception ex)
{
- Logger.LogError(ex, "Error calling CancellationToken.Dispose();");
+ _logger.LogError(ex, "Error calling CancellationToken.Dispose();");
}
}
@@ -692,8 +695,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// </summary>
/// <param name="info">The info.</param>
/// <returns>BaseTaskTrigger.</returns>
- /// <exception cref="ArgumentNullException"></exception>
- /// <exception cref="ArgumentException">Invalid trigger type: + info.Type</exception>
+ /// <exception cref="ArgumentException">Invalid trigger type: + info.Type.</exception>
private ITaskTrigger GetTrigger(TaskTriggerInfo info)
{
var options = new TaskOptions
@@ -765,7 +767,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
foreach (var triggerInfo in InternalTriggers)
{
var trigger = triggerInfo.Item2;
- trigger.Triggered -= trigger_Triggered;
+ trigger.Triggered -= OnTriggerTriggered;
trigger.Stop();
}
}
diff --git a/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs b/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs
index 81096026b..6f81bf49b 100644
--- a/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs
@@ -5,8 +5,8 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
+using Jellyfin.Data.Events;
using MediaBrowser.Common.Configuration;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Tasks;
using Microsoft.Extensions.Logging;
@@ -207,6 +207,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
public void Dispose()
{
Dispose(true);
+ GC.SuppressFinalize(this);
}
/// <summary>
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs
index 402b39a26..54e18eaea 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs
@@ -1,12 +1,13 @@
using System;
using System.Collections.Generic;
+using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
+using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Tasks;
-using MediaBrowser.Model.Globalization;
namespace Emby.Server.Implementations.ScheduledTasks.Tasks
{
@@ -15,12 +16,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
/// </summary>
public class DeleteLogFileTask : IScheduledTask, IConfigurableScheduledTask
{
- /// <summary>
- /// Gets or sets the configuration manager.
- /// </summary>
- /// <value>The configuration manager.</value>
- private IConfigurationManager ConfigurationManager { get; set; }
-
+ private readonly IConfigurationManager _configurationManager;
private readonly IFileSystem _fileSystem;
private readonly ILocalizationManager _localization;
@@ -32,18 +28,43 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
/// <param name="localization">The localization manager.</param>
public DeleteLogFileTask(IConfigurationManager configurationManager, IFileSystem fileSystem, ILocalizationManager localization)
{
- ConfigurationManager = configurationManager;
+ _configurationManager = configurationManager;
_fileSystem = fileSystem;
_localization = localization;
}
+ /// <inheritdoc />
+ public string Name => _localization.GetLocalizedString("TaskCleanLogs");
+
+ /// <inheritdoc />
+ public string Description => string.Format(
+ CultureInfo.InvariantCulture,
+ _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;
+
/// <summary>
/// Creates the triggers that define when the task will run.
/// </summary>
/// <returns>IEnumerable{BaseTaskTrigger}.</returns>
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
- return new[] {
+ return new[]
+ {
new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks}
};
}
@@ -57,10 +78,10 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
public Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
{
// Delete log files more than n days old
- var minDateModified = DateTime.UtcNow.AddDays(-ConfigurationManager.CommonConfiguration.LogFileRetentionDays);
+ var minDateModified = DateTime.UtcNow.AddDays(-_configurationManager.CommonConfiguration.LogFileRetentionDays);
// Only delete the .txt log files, the *.log files created by serilog get managed by itself
- var filesToDelete = _fileSystem.GetFiles(ConfigurationManager.CommonApplicationPaths.LogDirectoryPath, new[] { ".txt" }, true, true)
+ var filesToDelete = _fileSystem.GetFiles(_configurationManager.CommonApplicationPaths.LogDirectoryPath, new[] { ".txt" }, true, true)
.Where(f => _fileSystem.GetLastWriteTimeUtc(f) < minDateModified)
.ToList();
@@ -83,26 +104,5 @@ 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/Services/ServicePath.cs b/Emby.Server.Implementations/Services/ServicePath.cs
index 442b2ab1c..0d4728b43 100644
--- a/Emby.Server.Implementations/Services/ServicePath.cs
+++ b/Emby.Server.Implementations/Services/ServicePath.cs
@@ -80,8 +80,8 @@ namespace Emby.Server.Implementations.Services
public static List<string> GetFirstMatchWildCardHashKeys(string[] pathPartsForMatching)
{
- const string hashPrefix = WildCard + PathSeperator;
- return GetPotentialMatchesWithPrefix(hashPrefix, pathPartsForMatching);
+ const string HashPrefix = WildCard + PathSeperator;
+ return GetPotentialMatchesWithPrefix(HashPrefix, pathPartsForMatching);
}
private static List<string> GetPotentialMatchesWithPrefix(string hashPrefix, string[] pathPartsForMatching)
@@ -92,7 +92,7 @@ namespace Emby.Server.Implementations.Services
{
list.Add(hashPrefix + part);
- if (part.IndexOf(ComponentSeperator) == -1)
+ if (part.IndexOf(ComponentSeperator, StringComparison.Ordinal) == -1)
{
continue;
}
@@ -130,7 +130,7 @@ namespace Emby.Server.Implementations.Services
}
if (component.IndexOf(VariablePrefix, StringComparison.OrdinalIgnoreCase) != -1
- && component.IndexOf(ComponentSeperator) != -1)
+ && component.IndexOf(ComponentSeperator, StringComparison.Ordinal) != -1)
{
hasSeparators.Add(true);
componentsList.AddRange(component.Split(ComponentSeperator));
diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs
index 862a7296c..dad414142 100644
--- a/Emby.Server.Implementations/Session/SessionManager.cs
+++ b/Emby.Server.Implementations/Session/SessionManager.cs
@@ -9,6 +9,7 @@ using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
+using Jellyfin.Data.Events;
using MediaBrowser.Common.Events;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller;
@@ -17,6 +18,8 @@ using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Controller.Events.Session;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Security;
@@ -24,7 +27,6 @@ using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Devices;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.Library;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Session;
@@ -40,25 +42,16 @@ namespace Emby.Server.Implementations.Session
/// </summary>
public class SessionManager : ISessionManager, IDisposable
{
- /// <summary>
- /// The user data repository.
- /// </summary>
private readonly IUserDataManager _userDataManager;
-
- /// <summary>
- /// The logger.
- /// </summary>
private readonly ILogger<SessionManager> _logger;
-
+ private readonly IEventManager _eventManager;
private readonly ILibraryManager _libraryManager;
private readonly IUserManager _userManager;
private readonly IMusicManager _musicManager;
private readonly IDtoService _dtoService;
private readonly IImageProcessor _imageProcessor;
private readonly IMediaSourceManager _mediaSourceManager;
-
private readonly IServerApplicationHost _appHost;
-
private readonly IAuthenticationRepository _authRepo;
private readonly IDeviceManager _deviceManager;
@@ -75,6 +68,7 @@ namespace Emby.Server.Implementations.Session
public SessionManager(
ILogger<SessionManager> logger,
+ IEventManager eventManager,
IUserDataManager userDataManager,
ILibraryManager libraryManager,
IUserManager userManager,
@@ -87,6 +81,7 @@ namespace Emby.Server.Implementations.Session
IMediaSourceManager mediaSourceManager)
{
_logger = logger;
+ _eventManager = eventManager;
_userDataManager = userDataManager;
_libraryManager = libraryManager;
_userManager = userManager;
@@ -209,6 +204,8 @@ namespace Emby.Server.Implementations.Session
}
}
+ _eventManager.Publish(new SessionStartedEventArgs(info));
+
EventHelper.QueueEventIfNotNull(
SessionStarted,
this,
@@ -230,6 +227,8 @@ namespace Emby.Server.Implementations.Session
},
_logger);
+ _eventManager.Publish(new SessionEndedEventArgs(info));
+
info.Dispose();
}
@@ -667,22 +666,26 @@ namespace Emby.Server.Implementations.Session
}
}
+ var eventArgs = new PlaybackProgressEventArgs
+ {
+ Item = libraryItem,
+ Users = users,
+ MediaSourceId = info.MediaSourceId,
+ MediaInfo = info.Item,
+ DeviceName = session.DeviceName,
+ ClientName = session.Client,
+ DeviceId = session.DeviceId,
+ Session = session
+ };
+
+ await _eventManager.PublishAsync(eventArgs).ConfigureAwait(false);
+
// Nothing to save here
// Fire events to inform plugins
EventHelper.QueueEventIfNotNull(
PlaybackStart,
this,
- new PlaybackProgressEventArgs
- {
- Item = libraryItem,
- Users = users,
- MediaSourceId = info.MediaSourceId,
- MediaInfo = info.Item,
- DeviceName = session.DeviceName,
- ClientName = session.Client,
- DeviceId = session.DeviceId,
- Session = session
- },
+ eventArgs,
_logger);
StartIdleCheckTimer();
@@ -750,23 +753,25 @@ namespace Emby.Server.Implementations.Session
}
}
- PlaybackProgress?.Invoke(
- this,
- new PlaybackProgressEventArgs
- {
- Item = libraryItem,
- Users = users,
- PlaybackPositionTicks = session.PlayState.PositionTicks,
- MediaSourceId = session.PlayState.MediaSourceId,
- MediaInfo = info.Item,
- DeviceName = session.DeviceName,
- ClientName = session.Client,
- DeviceId = session.DeviceId,
- IsPaused = info.IsPaused,
- PlaySessionId = info.PlaySessionId,
- IsAutomated = isAutomated,
- Session = session
- });
+ var eventArgs = new PlaybackProgressEventArgs
+ {
+ Item = libraryItem,
+ Users = users,
+ PlaybackPositionTicks = session.PlayState.PositionTicks,
+ MediaSourceId = session.PlayState.MediaSourceId,
+ MediaInfo = info.Item,
+ DeviceName = session.DeviceName,
+ ClientName = session.Client,
+ DeviceId = session.DeviceId,
+ IsPaused = info.IsPaused,
+ PlaySessionId = info.PlaySessionId,
+ IsAutomated = isAutomated,
+ Session = session
+ };
+
+ await _eventManager.PublishAsync(eventArgs).ConfigureAwait(false);
+
+ PlaybackProgress?.Invoke(this, eventArgs);
if (!isAutomated)
{
@@ -943,23 +948,23 @@ namespace Emby.Server.Implementations.Session
}
}
- EventHelper.QueueEventIfNotNull(
- PlaybackStopped,
- this,
- new PlaybackStopEventArgs
- {
- Item = libraryItem,
- Users = users,
- PlaybackPositionTicks = info.PositionTicks,
- PlayedToCompletion = playedToCompletion,
- MediaSourceId = info.MediaSourceId,
- MediaInfo = info.Item,
- DeviceName = session.DeviceName,
- ClientName = session.Client,
- DeviceId = session.DeviceId,
- Session = session
- },
- _logger);
+ var eventArgs = new PlaybackStopEventArgs
+ {
+ Item = libraryItem,
+ Users = users,
+ PlaybackPositionTicks = info.PositionTicks,
+ PlayedToCompletion = playedToCompletion,
+ MediaSourceId = info.MediaSourceId,
+ MediaInfo = info.Item,
+ DeviceName = session.DeviceName,
+ ClientName = session.Client,
+ DeviceId = session.DeviceId,
+ Session = session
+ };
+
+ await _eventManager.PublishAsync(eventArgs).ConfigureAwait(false);
+
+ EventHelper.QueueEventIfNotNull(PlaybackStopped, this, eventArgs, _logger);
}
private bool OnPlaybackStopped(User user, BaseItem item, long? positionTicks, bool playbackFailed)
diff --git a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs
index 8bebd37dc..1da7a6473 100644
--- a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs
+++ b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs
@@ -4,9 +4,9 @@ using System.Linq;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Events;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Session;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.Net;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
diff --git a/Jellyfin.Api/Controllers/AudioController.cs b/Jellyfin.Api/Controllers/AudioController.cs
index d300b4891..802cd026e 100644
--- a/Jellyfin.Api/Controllers/AudioController.cs
+++ b/Jellyfin.Api/Controllers/AudioController.cs
@@ -83,9 +83,9 @@ namespace Jellyfin.Api.Controllers
/// <param name="streamOptions">Optional. The streaming options.</param>
/// <response code="200">Audio stream returned.</response>
/// <returns>A <see cref="FileResult"/> containing the audio file.</returns>
- [HttpGet("{itemId}/{stream=stream}.{container?}", Name = "GetAudioStreamByContainer")]
+ [HttpGet("{itemId}/stream.{container}", Name = "GetAudioStreamByContainer")]
[HttpGet("{itemId}/stream", Name = "GetAudioStream")]
- [HttpHead("{itemId}/{stream=stream}.{container?}", Name = "HeadAudioStreamByContainer")]
+ [HttpHead("{itemId}/stream.{container}", Name = "HeadAudioStreamByContainer")]
[HttpHead("{itemId}/stream", Name = "HeadAudioStream")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult> GetAudioStream(
diff --git a/Jellyfin.Api/Controllers/CollectionController.cs b/Jellyfin.Api/Controllers/CollectionController.cs
index 53821a188..c5910d6e8 100644
--- a/Jellyfin.Api/Controllers/CollectionController.cs
+++ b/Jellyfin.Api/Controllers/CollectionController.cs
@@ -1,5 +1,6 @@
using System;
using System.ComponentModel.DataAnnotations;
+using System.Threading.Tasks;
using Jellyfin.Api.Constants;
using Jellyfin.Api.Extensions;
using Jellyfin.Api.Helpers;
@@ -51,7 +52,7 @@ namespace Jellyfin.Api.Controllers
/// <returns>A <see cref="CollectionCreationOptions"/> with information about the new collection.</returns>
[HttpPost]
[ProducesResponseType(StatusCodes.Status200OK)]
- public ActionResult<CollectionCreationResult> CreateCollection(
+ public async Task<ActionResult<CollectionCreationResult>> CreateCollection(
[FromQuery] string? name,
[FromQuery] string? ids,
[FromQuery] Guid? parentId,
@@ -59,14 +60,14 @@ namespace Jellyfin.Api.Controllers
{
var userId = _authContext.GetAuthorizationInfo(Request).UserId;
- var item = _collectionManager.CreateCollection(new CollectionCreationOptions
+ var item = await _collectionManager.CreateCollectionAsync(new CollectionCreationOptions
{
IsLocked = isLocked,
Name = name,
ParentId = parentId,
ItemIdList = RequestHelpers.Split(ids, ',', true),
UserIds = new[] { userId }
- });
+ }).ConfigureAwait(false);
var dtoOptions = new DtoOptions().AddClientFields(Request);
@@ -87,9 +88,9 @@ namespace Jellyfin.Api.Controllers
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
[HttpPost("{collectionId}/Items")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
- public ActionResult AddToCollection([FromRoute] Guid collectionId, [FromQuery, Required] string? itemIds)
+ public async Task<ActionResult> AddToCollection([FromRoute] Guid collectionId, [FromQuery, Required] string? itemIds)
{
- _collectionManager.AddToCollection(collectionId, RequestHelpers.Split(itemIds, ',', true));
+ await _collectionManager.AddToCollectionAsync(collectionId, RequestHelpers.GetGuids(itemIds)).ConfigureAwait(true);
return NoContent();
}
@@ -102,9 +103,9 @@ namespace Jellyfin.Api.Controllers
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
[HttpDelete("{collectionId}/Items")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
- public ActionResult RemoveFromCollection([FromRoute] Guid collectionId, [FromQuery, Required] string? itemIds)
+ public async Task<ActionResult> RemoveFromCollection([FromRoute] Guid collectionId, [FromQuery, Required] string? itemIds)
{
- _collectionManager.RemoveFromCollection(collectionId, RequestHelpers.Split(itemIds, ',', true));
+ await _collectionManager.RemoveFromCollectionAsync(collectionId, RequestHelpers.GetGuids(itemIds)).ConfigureAwait(false);
return NoContent();
}
}
diff --git a/Jellyfin.Api/Controllers/ConfigurationController.cs b/Jellyfin.Api/Controllers/ConfigurationController.cs
index 019703dae..20fb0ec87 100644
--- a/Jellyfin.Api/Controllers/ConfigurationController.cs
+++ b/Jellyfin.Api/Controllers/ConfigurationController.cs
@@ -117,7 +117,7 @@ namespace Jellyfin.Api.Controllers
[HttpPost("MediaEncoder/Path")]
[Authorize(Policy = Policies.FirstTimeSetupOrElevated)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
- public ActionResult UpdateMediaEncoderPath([FromForm, Required] MediaEncoderPathDto mediaEncoderPath)
+ public ActionResult UpdateMediaEncoderPath([FromBody, Required] MediaEncoderPathDto mediaEncoderPath)
{
_mediaEncoder.UpdateEncoderPath(mediaEncoderPath.Path, mediaEncoderPath.PathType);
return NoContent();
diff --git a/Jellyfin.Api/Controllers/DlnaServerController.cs b/Jellyfin.Api/Controllers/DlnaServerController.cs
index 0100d642b..ee87d917b 100644
--- a/Jellyfin.Api/Controllers/DlnaServerController.cs
+++ b/Jellyfin.Api/Controllers/DlnaServerController.cs
@@ -221,16 +221,15 @@ namespace Jellyfin.Api.Controllers
private Task<ControlResponse> ProcessControlRequestInternalAsync(string id, Stream requestStream, IUpnpService service)
{
- return service.ProcessControlRequestAsync(new ControlRequest
+ return service.ProcessControlRequestAsync(new ControlRequest(Request.Headers)
{
- Headers = Request.Headers,
InputXml = requestStream,
TargetServerUuId = id,
RequestedUrl = GetAbsoluteUri()
});
}
- private EventSubscriptionResponse ProcessEventRequest(IEventManager eventManager)
+ private EventSubscriptionResponse ProcessEventRequest(IDlnaEventManager dlnaEventManager)
{
var subscriptionId = Request.Headers["SID"];
if (string.Equals(Request.Method, "subscribe", StringComparison.OrdinalIgnoreCase))
@@ -241,17 +240,17 @@ namespace Jellyfin.Api.Controllers
if (string.IsNullOrEmpty(notificationType))
{
- return eventManager.RenewEventSubscription(
+ return dlnaEventManager.RenewEventSubscription(
subscriptionId,
notificationType,
timeoutString,
callback);
}
- return eventManager.CreateEventSubscription(notificationType, timeoutString, callback);
+ return dlnaEventManager.CreateEventSubscription(notificationType, timeoutString, callback);
}
- return eventManager.CancelEventSubscription(subscriptionId);
+ return dlnaEventManager.CancelEventSubscription(subscriptionId);
}
}
}
diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs
index b4fe3bc8f..b115ac6cd 100644
--- a/Jellyfin.Api/Controllers/DynamicHlsController.cs
+++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs
@@ -1356,7 +1356,7 @@ namespace Jellyfin.Api.Controllers
return string.Format(
CultureInfo.InvariantCulture,
- "{0} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4} {5} -copyts -avoid_negative_ts disabled -f hls -max_delay 5000000 -hls_time {6} -individual_header_trailer 0 -hls_segment_type {7} -start_number {8} -hls_segment_filename \"{9}\" -hls_playlist_type vod -hls_list_size 0 -y \"{10}\"",
+ "{0} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4} {5} -copyts -avoid_negative_ts disabled -max_muxing_queue_size 2048 -f hls -max_delay 5000000 -hls_time {6} -individual_header_trailer 0 -hls_segment_type {7} -start_number {8} -hls_segment_filename \"{9}\" -hls_playlist_type vod -hls_list_size 0 -y \"{10}\"",
inputModifier,
_encodingHelper.GetInputArgument(state, encodingOptions),
threads,
diff --git a/Jellyfin.Api/Controllers/ImageController.cs b/Jellyfin.Api/Controllers/ImageController.cs
index 8f5c6beb3..a204fe35c 100644
--- a/Jellyfin.Api/Controllers/ImageController.cs
+++ b/Jellyfin.Api/Controllers/ImageController.cs
@@ -113,7 +113,7 @@ namespace Jellyfin.Api.Controllers
user.ProfileImage = new Data.Entities.ImageInfo(Path.Combine(userDataPath, "profile" + MimeTypes.ToExtension(mimeType)));
await _providerManager
- .SaveImage(user, memoryStream, mimeType, user.ProfileImage.Path)
+ .SaveImage(memoryStream, mimeType, user.ProfileImage.Path)
.ConfigureAwait(false);
await _userManager.UpdateUserAsync(user).ConfigureAwait(false);
@@ -174,7 +174,7 @@ namespace Jellyfin.Api.Controllers
[Authorize(Policy = Policies.RequiresElevation)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
- public ActionResult DeleteItemImage(
+ public async Task<ActionResult> DeleteItemImage(
[FromRoute] Guid itemId,
[FromRoute] ImageType imageType,
[FromRoute] int? imageIndex = null)
@@ -185,7 +185,7 @@ namespace Jellyfin.Api.Controllers
return NotFound();
}
- item.DeleteImage(imageType, imageIndex ?? 0);
+ await item.DeleteImageAsync(imageType, imageIndex ?? 0).ConfigureAwait(false);
return NoContent();
}
@@ -218,7 +218,7 @@ namespace Jellyfin.Api.Controllers
// Handle image/png; charset=utf-8
var mimeType = Request.ContentType.Split(';').FirstOrDefault();
await _providerManager.SaveImage(item, Request.Body, mimeType, imageType, null, CancellationToken.None).ConfigureAwait(false);
- item.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None);
+ await item.UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false);
return NoContent();
}
@@ -237,7 +237,7 @@ namespace Jellyfin.Api.Controllers
[Authorize(Policy = Policies.RequiresElevation)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
- public ActionResult UpdateItemImageIndex(
+ public async Task<ActionResult> UpdateItemImageIndex(
[FromRoute] Guid itemId,
[FromRoute] ImageType imageType,
[FromRoute] int imageIndex,
@@ -249,7 +249,7 @@ namespace Jellyfin.Api.Controllers
return NotFound();
}
- item.SwapImages(imageType, imageIndex, newIndex);
+ await item.SwapImagesAsync(imageType, imageIndex, newIndex).ConfigureAwait(false);
return NoContent();
}
@@ -264,7 +264,7 @@ namespace Jellyfin.Api.Controllers
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
- public ActionResult<IEnumerable<ImageInfo>> GetItemImageInfos([FromRoute] Guid itemId)
+ public async Task<ActionResult<IEnumerable<ImageInfo>>> GetItemImageInfos([FromRoute] Guid itemId)
{
var item = _libraryManager.GetItemById(itemId);
if (item == null)
@@ -281,7 +281,7 @@ namespace Jellyfin.Api.Controllers
return list;
}
- _libraryManager.UpdateImages(item); // this makes sure dimensions and hashes are correct
+ await _libraryManager.UpdateImagesAsync(item).ConfigureAwait(false); // this makes sure dimensions and hashes are correct
foreach (var image in itemImages)
{
@@ -1281,9 +1281,9 @@ namespace Jellyfin.Api.Controllers
Response.Headers.Add(HeaderNames.LastModified, dateImageModified.ToUniversalTime().ToString("ddd, dd MMM yyyy HH:mm:ss \"GMT\"", new CultureInfo("en-US", false)));
// if the image was not modified since "ifModifiedSinceHeader"-header, return a HTTP status code 304 not modified
- if (!(dateImageModified > ifModifiedSinceHeader))
+ if (!(dateImageModified > ifModifiedSinceHeader) && cacheDuration.HasValue)
{
- if (ifModifiedSinceHeader.Add(cacheDuration!.Value) < DateTime.UtcNow)
+ if (ifModifiedSinceHeader.Add(cacheDuration.Value) < DateTime.UtcNow)
{
Response.StatusCode = StatusCodes.Status304NotModified;
return new ContentResult();
diff --git a/Jellyfin.Api/Controllers/ItemUpdateController.cs b/Jellyfin.Api/Controllers/ItemUpdateController.cs
index 4b40c6ada..ec52f4996 100644
--- a/Jellyfin.Api/Controllers/ItemUpdateController.cs
+++ b/Jellyfin.Api/Controllers/ItemUpdateController.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading;
+using System.Threading.Tasks;
using Jellyfin.Api.Constants;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
@@ -67,7 +68,7 @@ namespace Jellyfin.Api.Controllers
[HttpPost("Items/{itemId}")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
- public ActionResult UpdateItem([FromRoute] Guid itemId, [FromBody, Required] BaseItemDto request)
+ public async Task<ActionResult> UpdateItem([FromRoute] Guid itemId, [FromBody, Required] BaseItemDto request)
{
var item = _libraryManager.GetItemById(itemId);
if (item == null)
@@ -101,7 +102,7 @@ namespace Jellyfin.Api.Controllers
item.OnMetadataChanged();
- item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
+ await item.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
if (isLockedChanged && item.IsFolder)
{
@@ -110,7 +111,7 @@ namespace Jellyfin.Api.Controllers
foreach (var child in folder.GetRecursiveChildren())
{
child.IsLocked = newLockData;
- child.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
+ await child.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
}
}
diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs
index 1b8b68313..f9273bad6 100644
--- a/Jellyfin.Api/Controllers/ItemsController.cs
+++ b/Jellyfin.Api/Controllers/ItemsController.cs
@@ -4,10 +4,10 @@ using System.Linq;
using Jellyfin.Api.Constants;
using Jellyfin.Api.Extensions;
using Jellyfin.Api.Helpers;
-using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
@@ -266,7 +266,9 @@ namespace Jellyfin.Api.Controllers
bool isInEnabledFolder = user!.GetPreference(PreferenceKind.EnabledFolders).Any(i => new Guid(i) == item.Id)
// Assume all folders inside an EnabledChannel are enabled
- || user.GetPreference(PreferenceKind.EnabledChannels).Any(i => new Guid(i) == item.Id);
+ || user.GetPreference(PreferenceKind.EnabledChannels).Any(i => new Guid(i) == item.Id)
+ // Assume all items inside an EnabledChannel are enabled
+ || user.GetPreference(PreferenceKind.EnabledChannels).Any(i => new Guid(i) == item.ChannelId);
var collectionFolders = _libraryManager.GetCollectionFolders(item);
foreach (var collectionFolder in collectionFolders)
diff --git a/Jellyfin.Api/Controllers/LibraryController.cs b/Jellyfin.Api/Controllers/LibraryController.cs
index 4548e202a..a30873e9e 100644
--- a/Jellyfin.Api/Controllers/LibraryController.cs
+++ b/Jellyfin.Api/Controllers/LibraryController.cs
@@ -19,6 +19,7 @@ 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.Net;
@@ -35,8 +36,6 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Book = MediaBrowser.Controller.Entities.Book;
-using Movie = Jellyfin.Data.Entities.Movie;
-using MusicAlbum = Jellyfin.Data.Entities.MusicAlbum;
namespace Jellyfin.Api.Controllers
{
@@ -619,7 +618,7 @@ namespace Jellyfin.Api.Controllers
[Authorize(Policy = Policies.Download)]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
- public ActionResult GetDownload([FromRoute] Guid itemId)
+ public async Task<ActionResult> GetDownload([FromRoute] Guid itemId)
{
var item = _libraryManager.GetItemById(itemId);
if (item == null)
@@ -648,7 +647,7 @@ namespace Jellyfin.Api.Controllers
if (user != null)
{
- LogDownload(item, user, auth);
+ await LogDownloadAsync(item, user, auth).ConfigureAwait(false);
}
var path = item.Path;
@@ -861,17 +860,17 @@ namespace Jellyfin.Api.Controllers
: item;
}
- private void LogDownload(BaseItem item, User user, AuthorizationInfo auth)
+ private async Task LogDownloadAsync(BaseItem item, User user, AuthorizationInfo auth)
{
try
{
- _activityManager.Create(new ActivityLog(
+ await _activityManager.CreateAsync(new ActivityLog(
string.Format(CultureInfo.InvariantCulture, _localization.GetLocalizedString("UserDownloadingItemWithValues"), user.Username, item.Name),
"UserDownloadingContent",
auth.UserId)
{
ShortOverview = string.Format(CultureInfo.InvariantCulture, _localization.GetLocalizedString("AppDeviceValues"), auth.Client, auth.Device),
- });
+ }).ConfigureAwait(false);
}
catch
{
diff --git a/Jellyfin.Api/Controllers/LibraryStructureController.cs b/Jellyfin.Api/Controllers/LibraryStructureController.cs
index cdab4f356..d290e3c5b 100644
--- a/Jellyfin.Api/Controllers/LibraryStructureController.cs
+++ b/Jellyfin.Api/Controllers/LibraryStructureController.cs
@@ -76,7 +76,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] string? name,
[FromQuery] string? collectionType,
[FromQuery] string[] paths,
- [FromBody] LibraryOptionsDto? libraryOptionsDto,
+ [FromBody] AddVirtualFolderDto? libraryOptionsDto,
[FromQuery] bool refreshLibrary = false)
{
var libraryOptions = libraryOptionsDto?.LibraryOptions ?? new LibraryOptions();
@@ -312,19 +312,17 @@ namespace Jellyfin.Api.Controllers
/// <summary>
/// Update library options.
/// </summary>
- /// <param name="id">The library name.</param>
- /// <param name="libraryOptions">The library options.</param>
+ /// <param name="request">The library name and options.</param>
/// <response code="204">Library updated.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
[HttpPost("LibraryOptions")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult UpdateLibraryOptions(
- [FromQuery] string? id,
- [FromBody] LibraryOptions? libraryOptions)
+ [FromBody] UpdateLibraryOptionsDto request)
{
- var collectionFolder = (CollectionFolder)_libraryManager.GetItemById(id);
+ var collectionFolder = (CollectionFolder)_libraryManager.GetItemById(request.Id);
- collectionFolder.UpdateLibraryOptions(libraryOptions);
+ collectionFolder.UpdateLibraryOptions(request.LibraryOptions);
return NoContent();
}
}
diff --git a/Jellyfin.Api/Controllers/MediaInfoController.cs b/Jellyfin.Api/Controllers/MediaInfoController.cs
index 7faf479a5..1e154a039 100644
--- a/Jellyfin.Api/Controllers/MediaInfoController.cs
+++ b/Jellyfin.Api/Controllers/MediaInfoController.cs
@@ -269,9 +269,9 @@ namespace Jellyfin.Api.Controllers
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
[HttpPost("LiveStreams/Close")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
- public ActionResult CloseLiveStream([FromQuery, Required] string? liveStreamId)
+ public async Task<ActionResult> CloseLiveStream([FromQuery, Required] string? liveStreamId)
{
- _mediaSourceManager.CloseLiveStream(liveStreamId).GetAwaiter().GetResult();
+ await _mediaSourceManager.CloseLiveStream(liveStreamId).ConfigureAwait(false);
return NoContent();
}
diff --git a/Jellyfin.Api/Controllers/MoviesController.cs b/Jellyfin.Api/Controllers/MoviesController.cs
index 148d8a18e..7fcfc749d 100644
--- a/Jellyfin.Api/Controllers/MoviesController.cs
+++ b/Jellyfin.Api/Controllers/MoviesController.cs
@@ -10,6 +10,7 @@ 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.Model.Dto;
@@ -181,7 +182,7 @@ namespace Jellyfin.Api.Controllers
DtoOptions dtoOptions,
RecommendationType type)
{
- var itemTypes = new List<string> { nameof(MediaBrowser.Controller.Entities.Movies.Movie) };
+ var itemTypes = new List<string> { nameof(Movie) };
if (_serverConfigurationManager.Configuration.EnableExternalContentInSuggestions)
{
itemTypes.Add(nameof(Trailer));
diff --git a/Jellyfin.Api/Controllers/PlaylistsController.cs b/Jellyfin.Api/Controllers/PlaylistsController.cs
index 12c87d7c3..f4c6a9253 100644
--- a/Jellyfin.Api/Controllers/PlaylistsController.cs
+++ b/Jellyfin.Api/Controllers/PlaylistsController.cs
@@ -83,12 +83,12 @@ namespace Jellyfin.Api.Controllers
/// <returns>An <see cref="NoContentResult"/> on success.</returns>
[HttpPost("{playlistId}/Items")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
- public ActionResult AddToPlaylist(
- [FromRoute] string? playlistId,
+ public async Task<ActionResult> AddToPlaylist(
+ [FromRoute] Guid playlistId,
[FromQuery] string? ids,
[FromQuery] Guid? userId)
{
- _playlistManager.AddToPlaylist(playlistId, RequestHelpers.GetGuids(ids), userId ?? Guid.Empty);
+ await _playlistManager.AddToPlaylistAsync(playlistId, RequestHelpers.GetGuids(ids), userId ?? Guid.Empty).ConfigureAwait(false);
return NoContent();
}
@@ -102,12 +102,12 @@ namespace Jellyfin.Api.Controllers
/// <returns>An <see cref="NoContentResult"/> on success.</returns>
[HttpPost("{playlistId}/Items/{itemId}/Move/{newIndex}")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
- public ActionResult MoveItem(
+ public async Task<ActionResult> MoveItem(
[FromRoute] string? playlistId,
[FromRoute] string? itemId,
[FromRoute] int newIndex)
{
- _playlistManager.MoveItem(playlistId, itemId, newIndex);
+ await _playlistManager.MoveItemAsync(playlistId, itemId, newIndex).ConfigureAwait(false);
return NoContent();
}
@@ -120,9 +120,9 @@ namespace Jellyfin.Api.Controllers
/// <returns>An <see cref="NoContentResult"/> on success.</returns>
[HttpDelete("{playlistId}/Items")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
- public ActionResult RemoveFromPlaylist([FromRoute] string? playlistId, [FromQuery] string? entryIds)
+ public async Task<ActionResult> RemoveFromPlaylist([FromRoute] string? playlistId, [FromQuery] string? entryIds)
{
- _playlistManager.RemoveFromPlaylist(playlistId, RequestHelpers.Split(entryIds, ',', true));
+ await _playlistManager.RemoveFromPlaylistAsync(playlistId, RequestHelpers.Split(entryIds, ',', true)).ConfigureAwait(false);
return NoContent();
}
diff --git a/Jellyfin.Api/Controllers/PluginsController.cs b/Jellyfin.Api/Controllers/PluginsController.cs
index b2f34680b..a82f2621a 100644
--- a/Jellyfin.Api/Controllers/PluginsController.cs
+++ b/Jellyfin.Api/Controllers/PluginsController.cs
@@ -120,10 +120,14 @@ namespace Jellyfin.Api.Controllers
return NotFound();
}
- var configuration = (BasePluginConfiguration)await JsonSerializer.DeserializeAsync(Request.Body, plugin.ConfigurationType, _serializerOptions)
+ var configuration = (BasePluginConfiguration?)await JsonSerializer.DeserializeAsync(Request.Body, plugin.ConfigurationType, _serializerOptions)
.ConfigureAwait(false);
- plugin.UpdateConfiguration(configuration);
+ if (configuration != null)
+ {
+ plugin.UpdateConfiguration(configuration);
+ }
+
return NoContent();
}
diff --git a/Jellyfin.Api/Controllers/RemoteImageController.cs b/Jellyfin.Api/Controllers/RemoteImageController.cs
index a203c50b9..30a4f73fc 100644
--- a/Jellyfin.Api/Controllers/RemoteImageController.cs
+++ b/Jellyfin.Api/Controllers/RemoteImageController.cs
@@ -221,7 +221,7 @@ namespace Jellyfin.Api.Controllers
await _providerManager.SaveImage(item, imageUrl, type, null, CancellationToken.None)
.ConfigureAwait(false);
- item.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None);
+ await item.UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false);
return NoContent();
}
diff --git a/Jellyfin.Api/Controllers/SessionController.cs b/Jellyfin.Api/Controllers/SessionController.cs
index 4795fb0cf..ba8d51598 100644
--- a/Jellyfin.Api/Controllers/SessionController.cs
+++ b/Jellyfin.Api/Controllers/SessionController.cs
@@ -153,7 +153,6 @@ namespace Jellyfin.Api.Controllers
/// <param name="itemIds">The ids of the items to play, comma delimited.</param>
/// <param name="startPositionTicks">The starting position of the first item.</param>
/// <param name="playCommand">The type of play command to issue (PlayNow, PlayNext, PlayLast). Clients who have not yet implemented play next and play last may play now.</param>
- /// <param name="playRequest">The <see cref="PlayRequest"/>.</param>
/// <response code="204">Instruction sent to session.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
[HttpPost("Sessions/{sessionId}/Playing")]
@@ -163,17 +162,14 @@ namespace Jellyfin.Api.Controllers
[FromRoute, Required] string? sessionId,
[FromQuery] Guid[] itemIds,
[FromQuery] long? startPositionTicks,
- [FromQuery] PlayCommand playCommand,
- [FromBody, Required] PlayRequest playRequest)
+ [FromQuery] PlayCommand playCommand)
{
- if (playRequest == null)
+ var playRequest = new PlayRequest
{
- throw new ArgumentException("Request Body may not be null");
- }
-
- playRequest.ItemIds = itemIds;
- playRequest.StartPositionTicks = startPositionTicks;
- playRequest.PlayCommand = playCommand;
+ ItemIds = itemIds,
+ StartPositionTicks = startPositionTicks,
+ PlayCommand = playCommand
+ };
_sessionManager.SendPlayCommand(
RequestHelpers.GetSession(_sessionManager, _authContext, Request).Id,
diff --git a/Jellyfin.Api/Controllers/StartupController.cs b/Jellyfin.Api/Controllers/StartupController.cs
index c8e3cc4f5..9c259cc19 100644
--- a/Jellyfin.Api/Controllers/StartupController.cs
+++ b/Jellyfin.Api/Controllers/StartupController.cs
@@ -1,3 +1,4 @@
+using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using Jellyfin.Api.Constants;
@@ -64,21 +65,16 @@ namespace Jellyfin.Api.Controllers
/// <summary>
/// Sets the initial startup wizard configuration.
/// </summary>
- /// <param name="uiCulture">The UI language culture.</param>
- /// <param name="metadataCountryCode">The metadata country code.</param>
- /// <param name="preferredMetadataLanguage">The preferred language for metadata.</param>
+ /// <param name="startupConfiguration">The updated startup configuration.</param>
/// <response code="204">Configuration saved.</response>
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
[HttpPost("Configuration")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
- public ActionResult UpdateInitialConfiguration(
- [FromForm] string? uiCulture,
- [FromForm] string? metadataCountryCode,
- [FromForm] string? preferredMetadataLanguage)
+ public ActionResult UpdateInitialConfiguration([FromBody, Required] StartupConfigurationDto startupConfiguration)
{
- _config.Configuration.UICulture = uiCulture;
- _config.Configuration.MetadataCountryCode = metadataCountryCode;
- _config.Configuration.PreferredMetadataLanguage = preferredMetadataLanguage;
+ _config.Configuration.UICulture = startupConfiguration.UICulture;
+ _config.Configuration.MetadataCountryCode = startupConfiguration.MetadataCountryCode;
+ _config.Configuration.PreferredMetadataLanguage = startupConfiguration.PreferredMetadataLanguage;
_config.SaveConfiguration();
return NoContent();
}
@@ -86,16 +82,15 @@ namespace Jellyfin.Api.Controllers
/// <summary>
/// Sets remote access and UPnP.
/// </summary>
- /// <param name="enableRemoteAccess">Enable remote access.</param>
- /// <param name="enableAutomaticPortMapping">Enable UPnP.</param>
+ /// <param name="startupRemoteAccessDto">The startup remote access dto.</param>
/// <response code="204">Configuration saved.</response>
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
[HttpPost("RemoteAccess")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
- public ActionResult SetRemoteAccess([FromForm] bool enableRemoteAccess, [FromForm] bool enableAutomaticPortMapping)
+ public ActionResult SetRemoteAccess([FromBody, Required] StartupRemoteAccessDto startupRemoteAccessDto)
{
- _config.Configuration.EnableRemoteAccess = enableRemoteAccess;
- _config.Configuration.EnableUPnP = enableAutomaticPortMapping;
+ _config.Configuration.EnableRemoteAccess = startupRemoteAccessDto.EnableRemoteAccess;
+ _config.Configuration.EnableUPnP = startupRemoteAccessDto.EnableAutomaticPortMapping;
_config.SaveConfiguration();
return NoContent();
}
@@ -131,7 +126,7 @@ namespace Jellyfin.Api.Controllers
/// </returns>
[HttpPost("User")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
- public async Task<ActionResult> UpdateStartupUser([FromForm] StartupUserDto startupUserDto)
+ public async Task<ActionResult> UpdateStartupUser([FromBody] StartupUserDto startupUserDto)
{
var user = _userManager.Users.First();
diff --git a/Jellyfin.Api/Controllers/UniversalAudioController.cs b/Jellyfin.Api/Controllers/UniversalAudioController.cs
index 3994fb995..b13cf9fa5 100644
--- a/Jellyfin.Api/Controllers/UniversalAudioController.cs
+++ b/Jellyfin.Api/Controllers/UniversalAudioController.cs
@@ -85,9 +85,9 @@ namespace Jellyfin.Api.Controllers
/// <response code="302">Redirected to remote audio stream.</response>
/// <returns>A <see cref="Task"/> containing the audio file.</returns>
[HttpGet("Audio/{itemId}/universal")]
- [HttpGet("Audio/{itemId}/{universal=universal}.{container?}", Name = "GetUniversalAudioStream_2")]
+ [HttpGet("Audio/{itemId}/universal.{container}", Name = "GetUniversalAudioStream_2")]
[HttpHead("Audio/{itemId}/universal", Name = "HeadUniversalAudioStream")]
- [HttpHead("Audio/{itemId}/{universal=universal}.{container?}", Name = "HeadUniversalAudioStream_2")]
+ [HttpHead("Audio/{itemId}/universal.{container}", Name = "HeadUniversalAudioStream_2")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status302Found)]
diff --git a/Jellyfin.Api/Controllers/VideosController.cs b/Jellyfin.Api/Controllers/VideosController.cs
index 14d3f2460..f42810c94 100644
--- a/Jellyfin.Api/Controllers/VideosController.cs
+++ b/Jellyfin.Api/Controllers/VideosController.cs
@@ -161,7 +161,7 @@ namespace Jellyfin.Api.Controllers
[Authorize(Policy = Policies.RequiresElevation)]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
- public ActionResult DeleteAlternateSources([FromRoute] Guid itemId)
+ public async Task<ActionResult> DeleteAlternateSources([FromRoute] Guid itemId)
{
var video = (Video)_libraryManager.GetItemById(itemId);
@@ -180,12 +180,12 @@ namespace Jellyfin.Api.Controllers
link.SetPrimaryVersionId(null);
link.LinkedAlternateVersions = Array.Empty<LinkedChild>();
- link.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
+ await link.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
}
video.LinkedAlternateVersions = Array.Empty<LinkedChild>();
video.SetPrimaryVersionId(null);
- video.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
+ await video.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
return NoContent();
}
@@ -201,7 +201,7 @@ namespace Jellyfin.Api.Controllers
[Authorize(Policy = Policies.RequiresElevation)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
- public ActionResult MergeVersions([FromQuery, Required] string? itemIds)
+ public async Task<ActionResult> MergeVersions([FromQuery, Required] string? itemIds)
{
var items = RequestHelpers.Split(itemIds, ',', true)
.Select(i => _libraryManager.GetItemById(i))
@@ -239,7 +239,7 @@ namespace Jellyfin.Api.Controllers
{
item.SetPrimaryVersionId(primaryVersion.Id.ToString("N", CultureInfo.InvariantCulture));
- item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
+ await item.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
list.Add(new LinkedChild
{
@@ -258,12 +258,12 @@ namespace Jellyfin.Api.Controllers
if (item.LinkedAlternateVersions.Length > 0)
{
item.LinkedAlternateVersions = Array.Empty<LinkedChild>();
- item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
+ await item.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
}
}
primaryVersion.LinkedAlternateVersions = list.ToArray();
- primaryVersion.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
+ await primaryVersion.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
return NoContent();
}
diff --git a/Jellyfin.Api/Helpers/MediaInfoHelper.cs b/Jellyfin.Api/Helpers/MediaInfoHelper.cs
index d2d1855a4..3a736d1e8 100644
--- a/Jellyfin.Api/Helpers/MediaInfoHelper.cs
+++ b/Jellyfin.Api/Helpers/MediaInfoHelper.cs
@@ -127,7 +127,11 @@ namespace Jellyfin.Api.Helpers
{
// Since we're going to be setting properties on MediaSourceInfos that come out of _mediaSourceManager, we should clone it
// Should we move this directly into MediaSourceManager?
- result.MediaSources = JsonSerializer.Deserialize<MediaSourceInfo[]>(JsonSerializer.SerializeToUtf8Bytes(mediaSources));
+ var mediaSourcesClone = JsonSerializer.Deserialize<MediaSourceInfo[]>(JsonSerializer.SerializeToUtf8Bytes(mediaSources));
+ if (mediaSourcesClone != null)
+ {
+ result.MediaSources = mediaSourcesClone;
+ }
result.PlaySessionId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
}
diff --git a/Jellyfin.Api/Models/LibraryStructureDto/LibraryOptionsDto.cs b/Jellyfin.Api/Models/LibraryStructureDto/AddVirtualFolderDto.cs
index a13cb90db..ab68d5223 100644
--- a/Jellyfin.Api/Models/LibraryStructureDto/LibraryOptionsDto.cs
+++ b/Jellyfin.Api/Models/LibraryStructureDto/AddVirtualFolderDto.cs
@@ -3,13 +3,13 @@
namespace Jellyfin.Api.Models.LibraryStructureDto
{
/// <summary>
- /// Library options dto.
+ /// Add virtual folder dto.
/// </summary>
- public class LibraryOptionsDto
+ public class AddVirtualFolderDto
{
/// <summary>
/// Gets or sets library options.
/// </summary>
public LibraryOptions? LibraryOptions { get; set; }
}
-} \ No newline at end of file
+}
diff --git a/Jellyfin.Api/Models/LibraryStructureDto/UpdateLibraryOptionsDto.cs b/Jellyfin.Api/Models/LibraryStructureDto/UpdateLibraryOptionsDto.cs
new file mode 100644
index 000000000..c78ed51f7
--- /dev/null
+++ b/Jellyfin.Api/Models/LibraryStructureDto/UpdateLibraryOptionsDto.cs
@@ -0,0 +1,21 @@
+using System;
+using MediaBrowser.Model.Configuration;
+
+namespace Jellyfin.Api.Models.LibraryStructureDto
+{
+ /// <summary>
+ /// Update library options dto.
+ /// </summary>
+ public class UpdateLibraryOptionsDto
+ {
+ /// <summary>
+ /// Gets or sets the library item id.
+ /// </summary>
+ public Guid Id { get; set; }
+
+ /// <summary>
+ /// Gets or sets library options.
+ /// </summary>
+ public LibraryOptions? LibraryOptions { get; set; }
+ }
+}
diff --git a/Jellyfin.Api/Models/StartupDtos/StartupRemoteAccessDto.cs b/Jellyfin.Api/Models/StartupDtos/StartupRemoteAccessDto.cs
new file mode 100644
index 000000000..4027ba41a
--- /dev/null
+++ b/Jellyfin.Api/Models/StartupDtos/StartupRemoteAccessDto.cs
@@ -0,0 +1,22 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace Jellyfin.Api.Models.StartupDtos
+{
+ /// <summary>
+ /// Startup remote access dto.
+ /// </summary>
+ public class StartupRemoteAccessDto
+ {
+ /// <summary>
+ /// Gets or sets a value indicating whether enable remote access.
+ /// </summary>
+ [Required]
+ public bool EnableRemoteAccess { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether enable automatic port mapping.
+ /// </summary>
+ [Required]
+ public bool EnableAutomaticPortMapping { get; set; }
+ }
+}
diff --git a/Jellyfin.Api/WebSocketListeners/ActivityLogWebSocketListener.cs b/Jellyfin.Api/WebSocketListeners/ActivityLogWebSocketListener.cs
index 6395b8d62..849b3b709 100644
--- a/Jellyfin.Api/WebSocketListeners/ActivityLogWebSocketListener.cs
+++ b/Jellyfin.Api/WebSocketListeners/ActivityLogWebSocketListener.cs
@@ -1,8 +1,8 @@
using System;
using System.Threading.Tasks;
+using Jellyfin.Data.Events;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Activity;
-using MediaBrowser.Model.Events;
using Microsoft.Extensions.Logging;
namespace Jellyfin.Api.WebSocketListeners
diff --git a/Jellyfin.Api/WebSocketListeners/ScheduledTasksWebSocketListener.cs b/Jellyfin.Api/WebSocketListeners/ScheduledTasksWebSocketListener.cs
index 12f815ff7..8a966c137 100644
--- a/Jellyfin.Api/WebSocketListeners/ScheduledTasksWebSocketListener.cs
+++ b/Jellyfin.Api/WebSocketListeners/ScheduledTasksWebSocketListener.cs
@@ -1,8 +1,8 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
+using Jellyfin.Data.Events;
using MediaBrowser.Controller.Net;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.Tasks;
using Microsoft.Extensions.Logging;
diff --git a/Jellyfin.Data/DayOfWeekHelper.cs b/Jellyfin.Data/DayOfWeekHelper.cs
index 32a41368d..4e75f4cfd 100644
--- a/Jellyfin.Data/DayOfWeekHelper.cs
+++ b/Jellyfin.Data/DayOfWeekHelper.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using Jellyfin.Data.Enums;
diff --git a/Jellyfin.Data/Entities/ActivityLog.cs b/Jellyfin.Data/Entities/ActivityLog.cs
index 522c20664..3858916d1 100644
--- a/Jellyfin.Data/Entities/ActivityLog.cs
+++ b/Jellyfin.Data/Entities/ActivityLog.cs
@@ -1,6 +1,9 @@
+#pragma warning disable CS1591
+
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
+using Jellyfin.Data.Interfaces;
using Microsoft.Extensions.Logging;
namespace Jellyfin.Data.Entities
@@ -8,7 +11,7 @@ namespace Jellyfin.Data.Entities
/// <summary>
/// An entity referencing an activity log entry.
/// </summary>
- public partial class ActivityLog : ISavingChanges
+ public partial class ActivityLog : IHasConcurrencyToken
{
/// <summary>
/// Initializes a new instance of the <see cref="ActivityLog"/> class.
diff --git a/Jellyfin.Data/Entities/Artwork.cs b/Jellyfin.Data/Entities/Artwork.cs
deleted file mode 100644
index 6ed32eac3..000000000
--- a/Jellyfin.Data/Entities/Artwork.cs
+++ /dev/null
@@ -1,208 +0,0 @@
-using System;
-using System.ComponentModel.DataAnnotations;
-
-namespace Jellyfin.Data.Entities
-{
- public partial class Artwork
- {
- partial void Init();
-
- /// <summary>
- /// Default constructor. Protected due to required properties, but present because EF needs it.
- /// </summary>
- protected Artwork()
- {
- Init();
- }
-
- /// <summary>
- /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
- /// </summary>
- public static Artwork CreateArtworkUnsafe()
- {
- return new Artwork();
- }
-
- /// <summary>
- /// Public constructor with required data.
- /// </summary>
- /// <param name="path"></param>
- /// <param name="kind"></param>
- /// <param name="_metadata0"></param>
- /// <param name="_personrole1"></param>
- public Artwork(string path, Enums.ArtKind kind, Metadata _metadata0, PersonRole _personrole1)
- {
- if (string.IsNullOrEmpty(path))
- {
- throw new ArgumentNullException(nameof(path));
- }
-
- this.Path = path;
-
- this.Kind = kind;
-
- if (_metadata0 == null)
- {
- throw new ArgumentNullException(nameof(_metadata0));
- }
-
- _metadata0.Artwork.Add(this);
-
- if (_personrole1 == null)
- {
- throw new ArgumentNullException(nameof(_personrole1));
- }
-
- _personrole1.Artwork = this;
-
- Init();
- }
-
- /// <summary>
- /// Static create function (for use in LINQ queries, etc.)
- /// </summary>
- /// <param name="path"></param>
- /// <param name="kind"></param>
- /// <param name="_metadata0"></param>
- /// <param name="_personrole1"></param>
- public static Artwork Create(string path, Enums.ArtKind kind, Metadata _metadata0, PersonRole _personrole1)
- {
- return new Artwork(path, kind, _metadata0, _personrole1);
- }
-
- /*************************************************************************
- * Properties
- *************************************************************************/
-
- /// <summary>
- /// Backing field for Id.
- /// </summary>
- internal int _Id;
- /// <summary>
- /// When provided in a partial class, allows value of Id to be changed before setting.
- /// </summary>
- partial void SetId(int oldValue, ref int newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Id to be changed before returning.
- /// </summary>
- partial void GetId(ref int result);
-
- /// <summary>
- /// Identity, Indexed, Required.
- /// </summary>
- [Key]
- [Required]
- public int Id
- {
- get
- {
- int value = _Id;
- GetId(ref value);
- return _Id = value;
- }
-
- protected set
- {
- int oldValue = _Id;
- SetId(oldValue, ref value);
- if (oldValue != value)
- {
- _Id = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for Path.
- /// </summary>
- protected string _Path;
- /// <summary>
- /// When provided in a partial class, allows value of Path to be changed before setting.
- /// </summary>
- partial void SetPath(string oldValue, ref string newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Path to be changed before returning.
- /// </summary>
- partial void GetPath(ref string result);
-
- /// <summary>
- /// Required, Max length = 65535
- /// </summary>
- [Required]
- [MaxLength(65535)]
- [StringLength(65535)]
- public string Path
- {
- get
- {
- string value = _Path;
- GetPath(ref value);
- return _Path = value;
- }
-
- set
- {
- string oldValue = _Path;
- SetPath(oldValue, ref value);
- if (oldValue != value)
- {
- _Path = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for Kind.
- /// </summary>
- internal Enums.ArtKind _Kind;
- /// <summary>
- /// When provided in a partial class, allows value of Kind to be changed before setting.
- /// </summary>
- partial void SetKind(Enums.ArtKind oldValue, ref Enums.ArtKind newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Kind to be changed before returning.
- /// </summary>
- partial void GetKind(ref Enums.ArtKind result);
-
- /// <summary>
- /// Indexed, Required.
- /// </summary>
- [Required]
- public Enums.ArtKind Kind
- {
- get
- {
- Enums.ArtKind value = _Kind;
- GetKind(ref value);
- return _Kind = value;
- }
-
- set
- {
- Enums.ArtKind oldValue = _Kind;
- SetKind(oldValue, ref value);
- if (oldValue != value)
- {
- _Kind = value;
- }
- }
- }
-
- /// <summary>
- /// Required, ConcurrenyToken.
- /// </summary>
- [ConcurrencyCheck]
- [Required]
- public uint RowVersion { get; set; }
-
- public void OnSavingChanges()
- {
- RowVersion++;
- }
-
- /*************************************************************************
- * Navigation properties
- *************************************************************************/
- }
-}
-
diff --git a/Jellyfin.Data/Entities/Book.cs b/Jellyfin.Data/Entities/Book.cs
deleted file mode 100644
index c4d12496e..000000000
--- a/Jellyfin.Data/Entities/Book.cs
+++ /dev/null
@@ -1,68 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Jellyfin.Data.Entities
-{
- public partial class Book : LibraryItem
- {
- partial void Init();
-
- /// <summary>
- /// Default constructor. Protected due to required properties, but present because EF needs it.
- /// </summary>
- protected Book()
- {
- BookMetadata = new HashSet<BookMetadata>();
- Releases = new HashSet<Release>();
-
- Init();
- }
-
- /// <summary>
- /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
- /// </summary>
- public static Book CreateBookUnsafe()
- {
- return new Book();
- }
-
- /// <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>
- public Book(Guid urlid, DateTime dateadded)
- {
- this.UrlId = urlid;
-
- this.BookMetadata = new HashSet<BookMetadata>();
- this.Releases = new HashSet<Release>();
-
- Init();
- }
-
- /// <summary>
- /// Static create function (for use in LINQ queries, etc.)
- /// </summary>
- /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param>
- public static Book Create(Guid urlid, DateTime dateadded)
- {
- return new Book(urlid, dateadded);
- }
-
- /*************************************************************************
- * Properties
- *************************************************************************/
-
- /*************************************************************************
- * Navigation properties
- *************************************************************************/
-
- [ForeignKey("BookMetadata_BookMetadata_Id")]
- public virtual ICollection<BookMetadata> BookMetadata { get; protected set; }
-
- [ForeignKey("Release_Releases_Id")]
- public virtual ICollection<Release> Releases { get; protected set; }
- }
-}
-
diff --git a/Jellyfin.Data/Entities/BookMetadata.cs b/Jellyfin.Data/Entities/BookMetadata.cs
deleted file mode 100644
index df43090d3..000000000
--- a/Jellyfin.Data/Entities/BookMetadata.cs
+++ /dev/null
@@ -1,119 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Jellyfin.Data.Entities
-{
- public partial class BookMetadata : Metadata
- {
- partial void Init();
-
- /// <summary>
- /// Default constructor. Protected due to required properties, but present because EF needs it.
- /// </summary>
- protected BookMetadata()
- {
- Publishers = new HashSet<Company>();
-
- Init();
- }
-
- /// <summary>
- /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
- /// </summary>
- public static BookMetadata CreateBookMetadataUnsafe()
- {
- return new BookMetadata();
- }
-
- /// <summary>
- /// Public constructor with required data.
- /// </summary>
- /// <param name="title">The title or name of the object.</param>
- /// <param name="language">ISO-639-3 3-character language codes.</param>
- /// <param name="_book0"></param>
- public BookMetadata(string title, string language, DateTime dateadded, DateTime datemodified, Book _book0)
- {
- if (string.IsNullOrEmpty(title))
- {
- throw new ArgumentNullException(nameof(title));
- }
-
- this.Title = title;
-
- if (string.IsNullOrEmpty(language))
- {
- throw new ArgumentNullException(nameof(language));
- }
-
- this.Language = language;
-
- if (_book0 == null)
- {
- throw new ArgumentNullException(nameof(_book0));
- }
-
- _book0.BookMetadata.Add(this);
-
- this.Publishers = new HashSet<Company>();
-
- Init();
- }
-
- /// <summary>
- /// Static create function (for use in LINQ queries, etc.)
- /// </summary>
- /// <param name="title">The title or name of the object.</param>
- /// <param name="language">ISO-639-3 3-character language codes.</param>
- /// <param name="_book0"></param>
- public static BookMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, Book _book0)
- {
- return new BookMetadata(title, language, dateadded, datemodified, _book0);
- }
-
- /*************************************************************************
- * Properties
- *************************************************************************/
-
- /// <summary>
- /// Backing field for ISBN.
- /// </summary>
- protected long? _ISBN;
- /// <summary>
- /// When provided in a partial class, allows value of ISBN to be changed before setting.
- /// </summary>
- partial void SetISBN(long? oldValue, ref long? newValue);
- /// <summary>
- /// When provided in a partial class, allows value of ISBN to be changed before returning.
- /// </summary>
- partial void GetISBN(ref long? result);
-
- public long? ISBN
- {
- get
- {
- long? value = _ISBN;
- GetISBN(ref value);
- return _ISBN = value;
- }
-
- set
- {
- long? oldValue = _ISBN;
- SetISBN(oldValue, ref value);
- if (oldValue != value)
- {
- _ISBN = value;
- }
- }
- }
-
- /*************************************************************************
- * Navigation properties
- *************************************************************************/
-
- [ForeignKey("Company_Publishers_Id")]
- public virtual ICollection<Company> Publishers { get; protected set; }
- }
-}
-
diff --git a/Jellyfin.Data/Entities/Chapter.cs b/Jellyfin.Data/Entities/Chapter.cs
deleted file mode 100644
index 4575cdb4d..000000000
--- a/Jellyfin.Data/Entities/Chapter.cs
+++ /dev/null
@@ -1,275 +0,0 @@
-using System;
-using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Jellyfin.Data.Entities
-{
- public partial class Chapter
- {
- partial void Init();
-
- /// <summary>
- /// Default constructor. Protected due to required properties, but present because EF needs it.
- /// </summary>
- protected Chapter()
- {
- Init();
- }
-
- /// <summary>
- /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
- /// </summary>
- public static Chapter CreateChapterUnsafe()
- {
- return new Chapter();
- }
-
- /// <summary>
- /// Public constructor with required data.
- /// </summary>
- /// <param name="language">ISO-639-3 3-character language codes.</param>
- /// <param name="timestart"></param>
- /// <param name="_release0"></param>
- public Chapter(string language, long timestart, Release _release0)
- {
- if (string.IsNullOrEmpty(language))
- {
- throw new ArgumentNullException(nameof(language));
- }
-
- this.Language = language;
-
- this.TimeStart = timestart;
-
- if (_release0 == null)
- {
- throw new ArgumentNullException(nameof(_release0));
- }
-
- _release0.Chapters.Add(this);
-
-
- Init();
- }
-
- /// <summary>
- /// Static create function (for use in LINQ queries, etc.)
- /// </summary>
- /// <param name="language">ISO-639-3 3-character language codes.</param>
- /// <param name="timestart"></param>
- /// <param name="_release0"></param>
- public static Chapter Create(string language, long timestart, Release _release0)
- {
- return new Chapter(language, timestart, _release0);
- }
-
- /*************************************************************************
- * Properties
- *************************************************************************/
-
- /// <summary>
- /// Backing field for Id.
- /// </summary>
- internal int _Id;
- /// <summary>
- /// When provided in a partial class, allows value of Id to be changed before setting.
- /// </summary>
- partial void SetId(int oldValue, ref int newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Id to be changed before returning.
- /// </summary>
- partial void GetId(ref int result);
-
- /// <summary>
- /// Identity, Indexed, Required.
- /// </summary>
- [Key]
- [Required]
- [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id
- {
- get
- {
- int value = _Id;
- GetId(ref value);
- return _Id = value;
- }
-
- protected set
- {
- int oldValue = _Id;
- SetId(oldValue, ref value);
- if (oldValue != value)
- {
- _Id = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for Name.
- /// </summary>
- protected string _Name;
- /// <summary>
- /// When provided in a partial class, allows value of Name to be changed before setting.
- /// </summary>
- partial void SetName(string oldValue, ref string newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Name to be changed before returning.
- /// </summary>
- partial void GetName(ref string result);
-
- /// <summary>
- /// Max length = 1024
- /// </summary>
- [MaxLength(1024)]
- [StringLength(1024)]
- public string Name
- {
- get
- {
- string value = _Name;
- GetName(ref value);
- return _Name = value;
- }
-
- set
- {
- string oldValue = _Name;
- SetName(oldValue, ref value);
- if (oldValue != value)
- {
- _Name = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for Language.
- /// </summary>
- protected string _Language;
- /// <summary>
- /// When provided in a partial class, allows value of Language to be changed before setting.
- /// </summary>
- partial void SetLanguage(string oldValue, ref string newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Language to be changed before returning.
- /// </summary>
- partial void GetLanguage(ref string result);
-
- /// <summary>
- /// Required, Min length = 3, Max length = 3
- /// ISO-639-3 3-character language codes.
- /// </summary>
- [Required]
- [MinLength(3)]
- [MaxLength(3)]
- [StringLength(3)]
- public string Language
- {
- get
- {
- string value = _Language;
- GetLanguage(ref value);
- return _Language = value;
- }
-
- set
- {
- string oldValue = _Language;
- SetLanguage(oldValue, ref value);
- if (oldValue != value)
- {
- _Language = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for TimeStart.
- /// </summary>
- protected long _TimeStart;
- /// <summary>
- /// When provided in a partial class, allows value of TimeStart to be changed before setting.
- /// </summary>
- partial void SetTimeStart(long oldValue, ref long newValue);
- /// <summary>
- /// When provided in a partial class, allows value of TimeStart to be changed before returning.
- /// </summary>
- partial void GetTimeStart(ref long result);
-
- /// <summary>
- /// Required.
- /// </summary>
- [Required]
- public long TimeStart
- {
- get
- {
- long value = _TimeStart;
- GetTimeStart(ref value);
- return _TimeStart = value;
- }
-
- set
- {
- long oldValue = _TimeStart;
- SetTimeStart(oldValue, ref value);
- if (oldValue != value)
- {
- _TimeStart = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for TimeEnd.
- /// </summary>
- protected long? _TimeEnd;
- /// <summary>
- /// When provided in a partial class, allows value of TimeEnd to be changed before setting.
- /// </summary>
- partial void SetTimeEnd(long? oldValue, ref long? newValue);
- /// <summary>
- /// When provided in a partial class, allows value of TimeEnd to be changed before returning.
- /// </summary>
- partial void GetTimeEnd(ref long? result);
-
- public long? TimeEnd
- {
- get
- {
- long? value = _TimeEnd;
- GetTimeEnd(ref value);
- return _TimeEnd = value;
- }
-
- set
- {
- long? oldValue = _TimeEnd;
- SetTimeEnd(oldValue, ref value);
- if (oldValue != value)
- {
- _TimeEnd = value;
- }
- }
- }
-
- /// <summary>
- /// Required, ConcurrenyToken.
- /// </summary>
- [ConcurrencyCheck]
- [Required]
- public uint RowVersion { get; set; }
-
- public void OnSavingChanges()
- {
- RowVersion++;
- }
-
- /*************************************************************************
- * Navigation properties
- *************************************************************************/
- }
-}
-
diff --git a/Jellyfin.Data/Entities/Collection.cs b/Jellyfin.Data/Entities/Collection.cs
deleted file mode 100644
index 01836d893..000000000
--- a/Jellyfin.Data/Entities/Collection.cs
+++ /dev/null
@@ -1,121 +0,0 @@
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Jellyfin.Data.Entities
-{
- public partial class Collection
- {
- partial void Init();
-
- /// <summary>
- /// Default constructor.
- /// </summary>
- public Collection()
- {
- CollectionItem = new LinkedList<CollectionItem>();
-
- Init();
- }
-
- /*************************************************************************
- * Properties
- *************************************************************************/
-
- /// <summary>
- /// Backing field for Id.
- /// </summary>
- internal int _Id;
- /// <summary>
- /// When provided in a partial class, allows value of Id to be changed before setting.
- /// </summary>
- partial void SetId(int oldValue, ref int newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Id to be changed before returning.
- /// </summary>
- partial void GetId(ref int result);
-
- /// <summary>
- /// Identity, Indexed, Required.
- /// </summary>
- [Key]
- [Required]
- [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id
- {
- get
- {
- int value = _Id;
- GetId(ref value);
- return _Id = value;
- }
-
- protected set
- {
- int oldValue = _Id;
- SetId(oldValue, ref value);
- if (oldValue != value)
- {
- _Id = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for Name.
- /// </summary>
- protected string _Name;
- /// <summary>
- /// When provided in a partial class, allows value of Name to be changed before setting.
- /// </summary>
- partial void SetName(string oldValue, ref string newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Name to be changed before returning.
- /// </summary>
- partial void GetName(ref string result);
-
- /// <summary>
- /// Max length = 1024
- /// </summary>
- [MaxLength(1024)]
- [StringLength(1024)]
- public string Name
- {
- get
- {
- string value = _Name;
- GetName(ref value);
- return _Name = value;
- }
-
- set
- {
- string oldValue = _Name;
- SetName(oldValue, ref value);
- if (oldValue != value)
- {
- _Name = value;
- }
- }
- }
-
- /// <summary>
- /// Required, ConcurrenyToken.
- /// </summary>
- [ConcurrencyCheck]
- [Required]
- public uint RowVersion { get; set; }
-
- public void OnSavingChanges()
- {
- RowVersion++;
- }
-
- /*************************************************************************
- * Navigation properties
- *************************************************************************/
- [ForeignKey("CollectionItem_CollectionItem_Id")]
- public virtual ICollection<CollectionItem> CollectionItem { get; protected set; }
- }
-}
-
diff --git a/Jellyfin.Data/Entities/CollectionItem.cs b/Jellyfin.Data/Entities/CollectionItem.cs
deleted file mode 100644
index d879806ee..000000000
--- a/Jellyfin.Data/Entities/CollectionItem.cs
+++ /dev/null
@@ -1,154 +0,0 @@
-using System;
-using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Jellyfin.Data.Entities
-{
- public partial class CollectionItem
- {
- partial void Init();
-
- /// <summary>
- /// Default constructor. Protected due to required properties, but present because EF needs it.
- /// </summary>
- protected CollectionItem()
- {
- // NOTE: This class has one-to-one associations with CollectionItem.
- // One-to-one associations are not validated in constructors since this causes a scenario where each one must be constructed before the other.
-
- Init();
- }
-
- /// <summary>
- /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
- /// </summary>
- public static CollectionItem CreateCollectionItemUnsafe()
- {
- return new CollectionItem();
- }
-
- /// <summary>
- /// Public constructor with required data.
- /// </summary>
- /// <param name="_collection0"></param>
- /// <param name="_collectionitem1"></param>
- /// <param name="_collectionitem2"></param>
- public CollectionItem(Collection _collection0, CollectionItem _collectionitem1, CollectionItem _collectionitem2)
- {
- // NOTE: This class has one-to-one associations with CollectionItem.
- // One-to-one associations are not validated in constructors since this causes a scenario where each one must be constructed before the other.
-
- if (_collection0 == null)
- {
- throw new ArgumentNullException(nameof(_collection0));
- }
-
- _collection0.CollectionItem.Add(this);
-
- if (_collectionitem1 == null)
- {
- throw new ArgumentNullException(nameof(_collectionitem1));
- }
-
- _collectionitem1.Next = this;
-
- if (_collectionitem2 == null)
- {
- throw new ArgumentNullException(nameof(_collectionitem2));
- }
-
- _collectionitem2.Previous = this;
-
- Init();
- }
-
- /// <summary>
- /// Static create function (for use in LINQ queries, etc.)
- /// </summary>
- /// <param name="_collection0"></param>
- /// <param name="_collectionitem1"></param>
- /// <param name="_collectionitem2"></param>
- public static CollectionItem Create(Collection _collection0, CollectionItem _collectionitem1, CollectionItem _collectionitem2)
- {
- return new CollectionItem(_collection0, _collectionitem1, _collectionitem2);
- }
-
- /*************************************************************************
- * Properties
- *************************************************************************/
-
- /// <summary>
- /// Backing field for Id.
- /// </summary>
- internal int _Id;
- /// <summary>
- /// When provided in a partial class, allows value of Id to be changed before setting.
- /// </summary>
- partial void SetId(int oldValue, ref int newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Id to be changed before returning.
- /// </summary>
- partial void GetId(ref int result);
-
- /// <summary>
- /// Identity, Indexed, Required.
- /// </summary>
- [Key]
- [Required]
- [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id
- {
- get
- {
- int value = _Id;
- GetId(ref value);
- return _Id = value;
- }
-
- protected set
- {
- int oldValue = _Id;
- SetId(oldValue, ref value);
- if (oldValue != value)
- {
- _Id = value;
- }
- }
- }
-
- /// <summary>
- /// Required, ConcurrenyToken.
- /// </summary>
- [ConcurrencyCheck]
- [Required]
- public uint RowVersion { get; set; }
-
- public void OnSavingChanges()
- {
- RowVersion++;
- }
-
- /*************************************************************************
- * Navigation properties
- *************************************************************************/
-
- /// <summary>
- /// Required.
- /// </summary>
- [ForeignKey("LibraryItem_Id")]
- public virtual LibraryItem LibraryItem { get; set; }
-
- /// <remarks>
- /// TODO check if this properly updated dependant and has the proper principal relationship
- /// </remarks>
- [ForeignKey("CollectionItem_Next_Id")]
- public virtual CollectionItem Next { get; set; }
-
- /// <remarks>
- /// TODO check if this properly updated dependant and has the proper principal relationship
- /// </remarks>
- [ForeignKey("CollectionItem_Previous_Id")]
- public virtual CollectionItem Previous { get; set; }
- }
-}
-
diff --git a/Jellyfin.Data/Entities/Company.cs b/Jellyfin.Data/Entities/Company.cs
deleted file mode 100644
index e905a17da..000000000
--- a/Jellyfin.Data/Entities/Company.cs
+++ /dev/null
@@ -1,157 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Jellyfin.Data.Entities
-{
- public partial class Company
- {
- partial void Init();
-
- /// <summary>
- /// Default constructor. Protected due to required properties, but present because EF needs it.
- /// </summary>
- protected Company()
- {
- CompanyMetadata = new HashSet<CompanyMetadata>();
-
- Init();
- }
-
- /// <summary>
- /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
- /// </summary>
- public static Company CreateCompanyUnsafe()
- {
- return new Company();
- }
-
- /// <summary>
- /// Public constructor with required data.
- /// </summary>
- /// <param name="_moviemetadata0"></param>
- /// <param name="_seriesmetadata1"></param>
- /// <param name="_musicalbummetadata2"></param>
- /// <param name="_bookmetadata3"></param>
- /// <param name="_company4"></param>
- public Company(MovieMetadata _moviemetadata0, SeriesMetadata _seriesmetadata1, MusicAlbumMetadata _musicalbummetadata2, BookMetadata _bookmetadata3, Company _company4)
- {
- if (_moviemetadata0 == null)
- {
- throw new ArgumentNullException(nameof(_moviemetadata0));
- }
-
- _moviemetadata0.Studios.Add(this);
-
- if (_seriesmetadata1 == null)
- {
- throw new ArgumentNullException(nameof(_seriesmetadata1));
- }
-
- _seriesmetadata1.Networks.Add(this);
-
- if (_musicalbummetadata2 == null)
- {
- throw new ArgumentNullException(nameof(_musicalbummetadata2));
- }
-
- _musicalbummetadata2.Labels.Add(this);
-
- if (_bookmetadata3 == null)
- {
- throw new ArgumentNullException(nameof(_bookmetadata3));
- }
-
- _bookmetadata3.Publishers.Add(this);
-
- if (_company4 == null)
- {
- throw new ArgumentNullException(nameof(_company4));
- }
-
- _company4.Parent = this;
-
- this.CompanyMetadata = new HashSet<CompanyMetadata>();
-
- Init();
- }
-
- /// <summary>
- /// Static create function (for use in LINQ queries, etc.)
- /// </summary>
- /// <param name="_moviemetadata0"></param>
- /// <param name="_seriesmetadata1"></param>
- /// <param name="_musicalbummetadata2"></param>
- /// <param name="_bookmetadata3"></param>
- /// <param name="_company4"></param>
- public static Company Create(MovieMetadata _moviemetadata0, SeriesMetadata _seriesmetadata1, MusicAlbumMetadata _musicalbummetadata2, BookMetadata _bookmetadata3, Company _company4)
- {
- return new Company(_moviemetadata0, _seriesmetadata1, _musicalbummetadata2, _bookmetadata3, _company4);
- }
-
- /*************************************************************************
- * Properties
- *************************************************************************/
-
- /// <summary>
- /// Backing field for Id.
- /// </summary>
- internal int _Id;
- /// <summary>
- /// When provided in a partial class, allows value of Id to be changed before setting.
- /// </summary>
- partial void SetId(int oldValue, ref int newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Id to be changed before returning.
- /// </summary>
- partial void GetId(ref int result);
-
- /// <summary>
- /// Identity, Indexed, Required.
- /// </summary>
- [Key]
- [Required]
- [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id
- {
- get
- {
- int value = _Id;
- GetId(ref value);
- return _Id = value;
- }
-
- protected set
- {
- int oldValue = _Id;
- SetId(oldValue, ref value);
- if (oldValue != value)
- {
- _Id = value;
- }
- }
- }
-
- /// <summary>
- /// Required, ConcurrenyToken.
- /// </summary>
- [ConcurrencyCheck]
- [Required]
- public uint RowVersion { get; set; }
-
- public void OnSavingChanges()
- {
- RowVersion++;
- }
-
- /*************************************************************************
- * Navigation properties
- *************************************************************************/
- [ForeignKey("CompanyMetadata_CompanyMetadata_Id")]
- public virtual ICollection<CompanyMetadata> CompanyMetadata { get; protected set; }
- [ForeignKey("Company_Parent_Id")]
- public virtual Company Parent { get; set; }
- }
-}
-
diff --git a/Jellyfin.Data/Entities/CompanyMetadata.cs b/Jellyfin.Data/Entities/CompanyMetadata.cs
deleted file mode 100644
index e75349cf2..000000000
--- a/Jellyfin.Data/Entities/CompanyMetadata.cs
+++ /dev/null
@@ -1,230 +0,0 @@
-using System;
-using System.ComponentModel.DataAnnotations;
-
-namespace Jellyfin.Data.Entities
-{
- public partial class CompanyMetadata : Metadata
- {
- partial void Init();
-
- /// <summary>
- /// Default constructor. Protected due to required properties, but present because EF needs it.
- /// </summary>
- protected CompanyMetadata()
- {
- Init();
- }
-
- /// <summary>
- /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
- /// </summary>
- public static CompanyMetadata CreateCompanyMetadataUnsafe()
- {
- return new CompanyMetadata();
- }
-
- /// <summary>
- /// Public constructor with required data.
- /// </summary>
- /// <param name="title">The title or name of the object.</param>
- /// <param name="language">ISO-639-3 3-character language codes.</param>
- /// <param name="_company0"></param>
- public CompanyMetadata(string title, string language, DateTime dateadded, DateTime datemodified, Company _company0)
- {
- if (string.IsNullOrEmpty(title))
- {
- throw new ArgumentNullException(nameof(title));
- }
-
- this.Title = title;
-
- if (string.IsNullOrEmpty(language))
- {
- throw new ArgumentNullException(nameof(language));
- }
-
- this.Language = language;
-
- if (_company0 == null)
- {
- throw new ArgumentNullException(nameof(_company0));
- }
-
- _company0.CompanyMetadata.Add(this);
-
- Init();
- }
-
- /// <summary>
- /// Static create function (for use in LINQ queries, etc.)
- /// </summary>
- /// <param name="title">The title or name of the object.</param>
- /// <param name="language">ISO-639-3 3-character language codes.</param>
- /// <param name="_company0"></param>
- public static CompanyMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, Company _company0)
- {
- return new CompanyMetadata(title, language, dateadded, datemodified, _company0);
- }
-
- /*************************************************************************
- * Properties
- *************************************************************************/
-
- /// <summary>
- /// Backing field for Description.
- /// </summary>
- protected string _Description;
- /// <summary>
- /// When provided in a partial class, allows value of Description to be changed before setting.
- /// </summary>
- partial void SetDescription(string oldValue, ref string newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Description to be changed before returning.
- /// </summary>
- partial void GetDescription(ref string result);
-
- /// <summary>
- /// Max length = 65535
- /// </summary>
- [MaxLength(65535)]
- [StringLength(65535)]
- public string Description
- {
- get
- {
- string value = _Description;
- GetDescription(ref value);
- return _Description = value;
- }
-
- set
- {
- string oldValue = _Description;
- SetDescription(oldValue, ref value);
- if (oldValue != value)
- {
- _Description = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for Headquarters.
- /// </summary>
- protected string _Headquarters;
- /// <summary>
- /// When provided in a partial class, allows value of Headquarters to be changed before setting.
- /// </summary>
- partial void SetHeadquarters(string oldValue, ref string newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Headquarters to be changed before returning.
- /// </summary>
- partial void GetHeadquarters(ref string result);
-
- /// <summary>
- /// Max length = 255
- /// </summary>
- [MaxLength(255)]
- [StringLength(255)]
- public string Headquarters
- {
- get
- {
- string value = _Headquarters;
- GetHeadquarters(ref value);
- return _Headquarters = value;
- }
-
- set
- {
- string oldValue = _Headquarters;
- SetHeadquarters(oldValue, ref value);
- if (oldValue != value)
- {
- _Headquarters = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for Country.
- /// </summary>
- protected string _Country;
- /// <summary>
- /// When provided in a partial class, allows value of Country to be changed before setting.
- /// </summary>
- partial void SetCountry(string oldValue, ref string newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Country to be changed before returning.
- /// </summary>
- partial void GetCountry(ref string result);
-
- /// <summary>
- /// Max length = 2
- /// </summary>
- [MaxLength(2)]
- [StringLength(2)]
- public string Country
- {
- get
- {
- string value = _Country;
- GetCountry(ref value);
- return _Country = value;
- }
-
- set
- {
- string oldValue = _Country;
- SetCountry(oldValue, ref value);
- if (oldValue != value)
- {
- _Country = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for Homepage.
- /// </summary>
- protected string _Homepage;
- /// <summary>
- /// When provided in a partial class, allows value of Homepage to be changed before setting.
- /// </summary>
- partial void SetHomepage(string oldValue, ref string newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Homepage to be changed before returning.
- /// </summary>
- partial void GetHomepage(ref string result);
-
- /// <summary>
- /// Max length = 1024
- /// </summary>
- [MaxLength(1024)]
- [StringLength(1024)]
- public string Homepage
- {
- get
- {
- string value = _Homepage;
- GetHomepage(ref value);
- return _Homepage = value;
- }
-
- set
- {
- string oldValue = _Homepage;
- SetHomepage(oldValue, ref value);
- if (oldValue != value)
- {
- _Homepage = value;
- }
- }
- }
-
- /*************************************************************************
- * Navigation properties
- *************************************************************************/
- }
-}
-
diff --git a/Jellyfin.Data/Entities/CustomItem.cs b/Jellyfin.Data/Entities/CustomItem.cs
deleted file mode 100644
index 446391591..000000000
--- a/Jellyfin.Data/Entities/CustomItem.cs
+++ /dev/null
@@ -1,67 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Jellyfin.Data.Entities
-{
- public partial class CustomItem : LibraryItem
- {
- partial void Init();
-
- /// <summary>
- /// Default constructor. Protected due to required properties, but present because EF needs it.
- /// </summary>
- protected CustomItem()
- {
- CustomItemMetadata = new HashSet<CustomItemMetadata>();
- Releases = new HashSet<Release>();
-
- Init();
- }
-
- /// <summary>
- /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
- /// </summary>
- public static CustomItem CreateCustomItemUnsafe()
- {
- return new CustomItem();
- }
-
- /// <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>
- public CustomItem(Guid urlid, DateTime dateadded)
- {
- this.UrlId = urlid;
-
- this.CustomItemMetadata = new HashSet<CustomItemMetadata>();
- this.Releases = new HashSet<Release>();
-
- Init();
- }
-
- /// <summary>
- /// Static create function (for use in LINQ queries, etc.)
- /// </summary>
- /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param>
- public static CustomItem Create(Guid urlid, DateTime dateadded)
- {
- return new CustomItem(urlid, dateadded);
- }
-
- /*************************************************************************
- * Properties
- *************************************************************************/
-
- /*************************************************************************
- * Navigation properties
- *************************************************************************/
- [ForeignKey("CustomItemMetadata_CustomItemMetadata_Id")]
- public virtual ICollection<CustomItemMetadata> CustomItemMetadata { get; protected set; }
-
- [ForeignKey("Release_Releases_Id")]
- public virtual ICollection<Release> Releases { get; protected set; }
- }
-}
-
diff --git a/Jellyfin.Data/Entities/CustomItemMetadata.cs b/Jellyfin.Data/Entities/CustomItemMetadata.cs
deleted file mode 100644
index 965ed731f..000000000
--- a/Jellyfin.Data/Entities/CustomItemMetadata.cs
+++ /dev/null
@@ -1,77 +0,0 @@
-using System;
-
-namespace Jellyfin.Data.Entities
-{
- public partial class CustomItemMetadata : Metadata
- {
- partial void Init();
-
- /// <summary>
- /// Default constructor. Protected due to required properties, but present because EF needs it.
- /// </summary>
- protected CustomItemMetadata()
- {
- Init();
- }
-
- /// <summary>
- /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
- /// </summary>
- public static CustomItemMetadata CreateCustomItemMetadataUnsafe()
- {
- return new CustomItemMetadata();
- }
-
- /// <summary>
- /// Public constructor with required data.
- /// </summary>
- /// <param name="title">The title or name of the object.</param>
- /// <param name="language">ISO-639-3 3-character language codes.</param>
- /// <param name="_customitem0"></param>
- public CustomItemMetadata(string title, string language, DateTime dateadded, DateTime datemodified, CustomItem _customitem0)
- {
- if (string.IsNullOrEmpty(title))
- {
- throw new ArgumentNullException(nameof(title));
- }
-
- this.Title = title;
-
- if (string.IsNullOrEmpty(language))
- {
- throw new ArgumentNullException(nameof(language));
- }
-
- this.Language = language;
-
- if (_customitem0 == null)
- {
- throw new ArgumentNullException(nameof(_customitem0));
- }
-
- _customitem0.CustomItemMetadata.Add(this);
-
- Init();
- }
-
- /// <summary>
- /// Static create function (for use in LINQ queries, etc.)
- /// </summary>
- /// <param name="title">The title or name of the object.</param>
- /// <param name="language">ISO-639-3 3-character language codes.</param>
- /// <param name="_customitem0"></param>
- public static CustomItemMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, CustomItem _customitem0)
- {
- return new CustomItemMetadata(title, language, dateadded, datemodified, _customitem0);
- }
-
- /*************************************************************************
- * Properties
- *************************************************************************/
-
- /*************************************************************************
- * Navigation properties
- *************************************************************************/
- }
-}
-
diff --git a/Jellyfin.Data/Entities/Episode.cs b/Jellyfin.Data/Entities/Episode.cs
deleted file mode 100644
index 57fbf894b..000000000
--- a/Jellyfin.Data/Entities/Episode.cs
+++ /dev/null
@@ -1,114 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Jellyfin.Data.Entities
-{
- public partial class Episode : LibraryItem
- {
- partial void Init();
-
- /// <summary>
- /// Default constructor. Protected due to required properties, but present because EF needs it.
- /// </summary>
- protected Episode()
- {
- // NOTE: This class has one-to-one associations with LibraryRoot, LibraryItem and CollectionItem.
- // One-to-one associations are not validated in constructors since this causes a scenario where each one must be constructed before the other.
-
- Releases = new HashSet<Release>();
- EpisodeMetadata = new HashSet<EpisodeMetadata>();
-
- Init();
- }
-
- /// <summary>
- /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
- /// </summary>
- public static Episode CreateEpisodeUnsafe()
- {
- return new Episode();
- }
-
- /// <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>
- /// <param name="_season0"></param>
- public Episode(Guid urlid, DateTime dateadded, Season _season0)
- {
- // NOTE: This class has one-to-one associations with LibraryRoot, LibraryItem and CollectionItem.
- // One-to-one associations are not validated in constructors since this causes a scenario where each one must be constructed before the other.
-
- this.UrlId = urlid;
-
- if (_season0 == null)
- {
- throw new ArgumentNullException(nameof(_season0));
- }
-
- _season0.Episodes.Add(this);
-
- this.Releases = new HashSet<Release>();
- this.EpisodeMetadata = new HashSet<EpisodeMetadata>();
-
- Init();
- }
-
- /// <summary>
- /// Static create function (for use in LINQ queries, etc.)
- /// </summary>
- /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param>
- /// <param name="_season0"></param>
- public static Episode Create(Guid urlid, DateTime dateadded, Season _season0)
- {
- return new Episode(urlid, dateadded, _season0);
- }
-
- /*************************************************************************
- * Properties
- *************************************************************************/
-
- /// <summary>
- /// Backing field for EpisodeNumber.
- /// </summary>
- protected int? _EpisodeNumber;
- /// <summary>
- /// When provided in a partial class, allows value of EpisodeNumber to be changed before setting.
- /// </summary>
- partial void SetEpisodeNumber(int? oldValue, ref int? newValue);
- /// <summary>
- /// When provided in a partial class, allows value of EpisodeNumber to be changed before returning.
- /// </summary>
- partial void GetEpisodeNumber(ref int? result);
-
- public int? EpisodeNumber
- {
- get
- {
- int? value = _EpisodeNumber;
- GetEpisodeNumber(ref value);
- return _EpisodeNumber = value;
- }
-
- set
- {
- int? oldValue = _EpisodeNumber;
- SetEpisodeNumber(oldValue, ref value);
- if (oldValue != value)
- {
- _EpisodeNumber = value;
- }
- }
- }
-
- /*************************************************************************
- * Navigation properties
- *************************************************************************/
- [ForeignKey("Release_Releases_Id")]
- public virtual ICollection<Release> Releases { get; protected set; }
- [ForeignKey("EpisodeMetadata_EpisodeMetadata_Id")]
- public virtual ICollection<EpisodeMetadata> EpisodeMetadata { get; protected set; }
- }
-}
-
diff --git a/Jellyfin.Data/Entities/EpisodeMetadata.cs b/Jellyfin.Data/Entities/EpisodeMetadata.cs
deleted file mode 100644
index 9a21fd50f..000000000
--- a/Jellyfin.Data/Entities/EpisodeMetadata.cs
+++ /dev/null
@@ -1,192 +0,0 @@
-using System;
-using System.ComponentModel.DataAnnotations;
-
-namespace Jellyfin.Data.Entities
-{
- public partial class EpisodeMetadata : Metadata
- {
- partial void Init();
-
- /// <summary>
- /// Default constructor. Protected due to required properties, but present because EF needs it.
- /// </summary>
- protected EpisodeMetadata()
- {
- Init();
- }
-
- /// <summary>
- /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
- /// </summary>
- public static EpisodeMetadata CreateEpisodeMetadataUnsafe()
- {
- return new EpisodeMetadata();
- }
-
- /// <summary>
- /// Public constructor with required data.
- /// </summary>
- /// <param name="title">The title or name of the object.</param>
- /// <param name="language">ISO-639-3 3-character language codes.</param>
- /// <param name="_episode0"></param>
- public EpisodeMetadata(string title, string language, DateTime dateadded, DateTime datemodified, Episode _episode0)
- {
- if (string.IsNullOrEmpty(title))
- {
- throw new ArgumentNullException(nameof(title));
- }
-
- this.Title = title;
-
- if (string.IsNullOrEmpty(language))
- {
- throw new ArgumentNullException(nameof(language));
- }
-
- this.Language = language;
-
- if (_episode0 == null)
- {
- throw new ArgumentNullException(nameof(_episode0));
- }
-
- _episode0.EpisodeMetadata.Add(this);
-
- Init();
- }
-
- /// <summary>
- /// Static create function (for use in LINQ queries, etc.)
- /// </summary>
- /// <param name="title">The title or name of the object.</param>
- /// <param name="language">ISO-639-3 3-character language codes.</param>
- /// <param name="_episode0"></param>
- public static EpisodeMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, Episode _episode0)
- {
- return new EpisodeMetadata(title, language, dateadded, datemodified, _episode0);
- }
-
- /*************************************************************************
- * Properties
- *************************************************************************/
-
- /// <summary>
- /// Backing field for Outline.
- /// </summary>
- protected string _Outline;
- /// <summary>
- /// When provided in a partial class, allows value of Outline to be changed before setting.
- /// </summary>
- partial void SetOutline(string oldValue, ref string newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Outline to be changed before returning.
- /// </summary>
- partial void GetOutline(ref string result);
-
- /// <summary>
- /// Max length = 1024
- /// </summary>
- [MaxLength(1024)]
- [StringLength(1024)]
- public string Outline
- {
- get
- {
- string value = _Outline;
- GetOutline(ref value);
- return _Outline = value;
- }
-
- set
- {
- string oldValue = _Outline;
- SetOutline(oldValue, ref value);
- if (oldValue != value)
- {
- _Outline = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for Plot.
- /// </summary>
- protected string _Plot;
- /// <summary>
- /// When provided in a partial class, allows value of Plot to be changed before setting.
- /// </summary>
- partial void SetPlot(string oldValue, ref string newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Plot to be changed before returning.
- /// </summary>
- partial void GetPlot(ref string result);
-
- /// <summary>
- /// Max length = 65535
- /// </summary>
- [MaxLength(65535)]
- [StringLength(65535)]
- public string Plot
- {
- get
- {
- string value = _Plot;
- GetPlot(ref value);
- return _Plot = value;
- }
-
- set
- {
- string oldValue = _Plot;
- SetPlot(oldValue, ref value);
- if (oldValue != value)
- {
- _Plot = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for Tagline.
- /// </summary>
- protected string _Tagline;
- /// <summary>
- /// When provided in a partial class, allows value of Tagline to be changed before setting.
- /// </summary>
- partial void SetTagline(string oldValue, ref string newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Tagline to be changed before returning.
- /// </summary>
- partial void GetTagline(ref string result);
-
- /// <summary>
- /// Max length = 1024
- /// </summary>
- [MaxLength(1024)]
- [StringLength(1024)]
- public string Tagline
- {
- get
- {
- string value = _Tagline;
- GetTagline(ref value);
- return _Tagline = value;
- }
-
- set
- {
- string oldValue = _Tagline;
- SetTagline(oldValue, ref value);
- if (oldValue != value)
- {
- _Tagline = value;
- }
- }
- }
-
- /*************************************************************************
- * Navigation properties
- *************************************************************************/
- }
-}
-
diff --git a/Jellyfin.Data/Entities/Genre.cs b/Jellyfin.Data/Entities/Genre.cs
deleted file mode 100644
index 24e6815d8..000000000
--- a/Jellyfin.Data/Entities/Genre.cs
+++ /dev/null
@@ -1,160 +0,0 @@
-using System;
-using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Jellyfin.Data.Entities
-{
- public partial class Genre
- {
- partial void Init();
-
- /// <summary>
- /// Default constructor. Protected due to required properties, but present because EF needs it.
- /// </summary>
- protected Genre()
- {
- Init();
- }
-
- /// <summary>
- /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
- /// </summary>
- public static Genre CreateGenreUnsafe()
- {
- return new Genre();
- }
-
- /// <summary>
- /// Public constructor with required data.
- /// </summary>
- /// <param name="name"></param>
- /// <param name="_metadata0"></param>
- public Genre(string name, Metadata _metadata0)
- {
- if (string.IsNullOrEmpty(name))
- {
- throw new ArgumentNullException(nameof(name));
- }
-
- this.Name = name;
-
- if (_metadata0 == null)
- {
- throw new ArgumentNullException(nameof(_metadata0));
- }
-
- _metadata0.Genres.Add(this);
-
- Init();
- }
-
- /// <summary>
- /// Static create function (for use in LINQ queries, etc.)
- /// </summary>
- /// <param name="name"></param>
- /// <param name="_metadata0"></param>
- public static Genre Create(string name, Metadata _metadata0)
- {
- return new Genre(name, _metadata0);
- }
-
- /*************************************************************************
- * Properties
- *************************************************************************/
-
- /// <summary>
- /// Backing field for Id.
- /// </summary>
- internal int _Id;
- /// <summary>
- /// When provided in a partial class, allows value of Id to be changed before setting.
- /// </summary>
- partial void SetId(int oldValue, ref int newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Id to be changed before returning.
- /// </summary>
- partial void GetId(ref int result);
-
- /// <summary>
- /// Identity, Indexed, Required.
- /// </summary>
- [Key]
- [Required]
- [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id
- {
- get
- {
- int value = _Id;
- GetId(ref value);
- return _Id = value;
- }
-
- protected set
- {
- int oldValue = _Id;
- SetId(oldValue, ref value);
- if (oldValue != value)
- {
- _Id = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for Name.
- /// </summary>
- internal string _Name;
- /// <summary>
- /// When provided in a partial class, allows value of Name to be changed before setting.
- /// </summary>
- partial void SetName(string oldValue, ref string newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Name to be changed before returning.
- /// </summary>
- partial void GetName(ref string result);
-
- /// <summary>
- /// Indexed, Required, Max length = 255
- /// </summary>
- [Required]
- [MaxLength(255)]
- [StringLength(255)]
- public string Name
- {
- get
- {
- string value = _Name;
- GetName(ref value);
- return _Name = value;
- }
-
- set
- {
- string oldValue = _Name;
- SetName(oldValue, ref value);
- if (oldValue != value)
- {
- _Name = value;
- }
- }
- }
-
- /// <summary>
- /// Required, ConcurrenyToken.
- /// </summary>
- [ConcurrencyCheck]
- [Required]
- public uint RowVersion { get; set; }
-
- public void OnSavingChanges()
- {
- RowVersion++;
- }
-
- /*************************************************************************
- * Navigation properties
- *************************************************************************/
- }
-}
-
diff --git a/Jellyfin.Data/Entities/Group.cs b/Jellyfin.Data/Entities/Group.cs
index 47833378e..ca12ba421 100644
--- a/Jellyfin.Data/Entities/Group.cs
+++ b/Jellyfin.Data/Entities/Group.cs
@@ -1,16 +1,19 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using Jellyfin.Data.Enums;
+using Jellyfin.Data.Interfaces;
namespace Jellyfin.Data.Entities
{
/// <summary>
/// An entity representing a group.
/// </summary>
- public partial class Group : IHasPermissions, ISavingChanges
+ public partial class Group : IHasPermissions, IHasConcurrencyToken
{
/// <summary>
/// Initializes a new instance of the <see cref="Group"/> class.
diff --git a/Jellyfin.Data/Entities/ImageInfo.cs b/Jellyfin.Data/Entities/ImageInfo.cs
index 64e36a791..cf0895ad4 100644
--- a/Jellyfin.Data/Entities/ImageInfo.cs
+++ b/Jellyfin.Data/Entities/ImageInfo.cs
@@ -1,4 +1,6 @@
-using System;
+#pragma warning disable CS1591
+
+using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
diff --git a/Jellyfin.Data/Entities/ItemDisplayPreferences.cs b/Jellyfin.Data/Entities/ItemDisplayPreferences.cs
index 95c08e6c6..023cdc740 100644
--- a/Jellyfin.Data/Entities/ItemDisplayPreferences.cs
+++ b/Jellyfin.Data/Entities/ItemDisplayPreferences.cs
@@ -1,4 +1,6 @@
-using System;
+#pragma warning disable CS1591
+
+using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Jellyfin.Data.Enums;
diff --git a/Jellyfin.Data/Entities/Libraries/Artwork.cs b/Jellyfin.Data/Entities/Libraries/Artwork.cs
new file mode 100644
index 000000000..7be22af25
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/Artwork.cs
@@ -0,0 +1,81 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Jellyfin.Data.Enums;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing artwork.
+ /// </summary>
+ public class Artwork : IHasConcurrencyToken
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Artwork"/> class.
+ /// </summary>
+ /// <param name="path">The path.</param>
+ /// <param name="kind">The kind of art.</param>
+ /// <param name="owner">The owner.</param>
+ public Artwork(string path, ArtKind kind, IHasArtwork owner)
+ {
+ if (string.IsNullOrEmpty(path))
+ {
+ throw new ArgumentNullException(nameof(path));
+ }
+
+ Path = path;
+ Kind = kind;
+
+ owner?.Artwork.Add(this);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Artwork"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected Artwork()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <remarks>
+ /// Identity, Indexed, Required.
+ /// </remarks>
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public int Id { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets the path.
+ /// </summary>
+ /// <remarks>
+ /// Required, Max length = 65535.
+ /// </remarks>
+ [Required]
+ [MaxLength(65535)]
+ [StringLength(65535)]
+ public string Path { get; set; }
+
+ /// <summary>
+ /// Gets or sets the kind of artwork.
+ /// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
+ public ArtKind Kind { get; set; }
+
+ /// <inheritdoc />
+ [ConcurrencyCheck]
+ public uint RowVersion { get; set; }
+
+ /// <inheritdoc />
+ public void OnSavingChanges()
+ {
+ RowVersion++;
+ }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/Book.cs b/Jellyfin.Data/Entities/Libraries/Book.cs
new file mode 100644
index 000000000..8337788dd
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/Book.cs
@@ -0,0 +1,28 @@
+using System.Collections.Generic;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing a book.
+ /// </summary>
+ public class Book : LibraryItem, IHasReleases
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Book"/> class.
+ /// </summary>
+ public Book()
+ {
+ BookMetadata = new HashSet<BookMetadata>();
+ Releases = new HashSet<Release>();
+ }
+
+ /// <summary>
+ /// Gets or sets a collection containing the metadata for this book.
+ /// </summary>
+ public virtual ICollection<BookMetadata> BookMetadata { get; protected set; }
+
+ /// <inheritdoc />
+ public virtual ICollection<Release> Releases { get; protected set; }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/BookMetadata.cs b/Jellyfin.Data/Entities/Libraries/BookMetadata.cs
new file mode 100644
index 000000000..bd716712b
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/BookMetadata.cs
@@ -0,0 +1,55 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations.Schema;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity containing metadata for a book.
+ /// </summary>
+ public class BookMetadata : Metadata, IHasCompanies
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="BookMetadata"/> class.
+ /// </summary>
+ /// <param name="title">The title or name of the object.</param>
+ /// <param name="language">ISO-639-3 3-character language codes.</param>
+ /// <param name="book">The book.</param>
+ public BookMetadata(string title, string language, Book book) : base(title, language)
+ {
+ if (book == null)
+ {
+ throw new ArgumentNullException(nameof(book));
+ }
+
+ book.BookMetadata.Add(this);
+
+ Publishers = new HashSet<Company>();
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="BookMetadata"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected BookMetadata()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the ISBN.
+ /// </summary>
+ public long? Isbn { get; set; }
+
+ /// <summary>
+ /// Gets or sets a collection of the publishers for this book.
+ /// </summary>
+ public virtual ICollection<Company> Publishers { get; protected set; }
+
+ /// <inheritdoc />
+ [NotMapped]
+ public ICollection<Company> Companies => Publishers;
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/Chapter.cs b/Jellyfin.Data/Entities/Libraries/Chapter.cs
new file mode 100644
index 000000000..d9293c3cc
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/Chapter.cs
@@ -0,0 +1,102 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing a chapter.
+ /// </summary>
+ public class Chapter : IHasConcurrencyToken
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Chapter"/> class.
+ /// </summary>
+ /// <param name="language">ISO-639-3 3-character language codes.</param>
+ /// <param name="startTime">The start time for this chapter.</param>
+ /// <param name="release">The release.</param>
+ public Chapter(string language, long startTime, Release release)
+ {
+ if (string.IsNullOrEmpty(language))
+ {
+ throw new ArgumentNullException(nameof(language));
+ }
+
+ Language = language;
+ StartTime = startTime;
+
+ if (release == null)
+ {
+ throw new ArgumentNullException(nameof(release));
+ }
+
+ release.Chapters.Add(this);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Chapter"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected Chapter()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <remarks>
+ /// Identity, Indexed, Required.
+ /// </remarks>
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public int Id { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 1024.
+ /// </remarks>
+ [MaxLength(1024)]
+ [StringLength(1024)]
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets or sets the language.
+ /// </summary>
+ /// <remarks>
+ /// Required, Min length = 3, Max length = 3
+ /// ISO-639-3 3-character language codes.
+ /// </remarks>
+ [Required]
+ [MinLength(3)]
+ [MaxLength(3)]
+ [StringLength(3)]
+ public string Language { get; set; }
+
+ /// <summary>
+ /// Gets or sets the start time.
+ /// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
+ public long StartTime { get; set; }
+
+ /// <summary>
+ /// Gets or sets the end time.
+ /// </summary>
+ public long? EndTime { get; set; }
+
+ /// <inheritdoc />
+ [ConcurrencyCheck]
+ public uint RowVersion { get; protected set; }
+
+ /// <inheritdoc />
+ public void OnSavingChanges()
+ {
+ RowVersion++;
+ }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/Collection.cs b/Jellyfin.Data/Entities/Libraries/Collection.cs
new file mode 100644
index 000000000..2e1bbcfb6
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/Collection.cs
@@ -0,0 +1,55 @@
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing a collection.
+ /// </summary>
+ public class Collection : IHasConcurrencyToken
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Collection"/> class.
+ /// </summary>
+ public Collection()
+ {
+ Items = new HashSet<CollectionItem>();
+ }
+
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <remarks>
+ /// Identity, Indexed, Required.
+ /// </remarks>
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public int Id { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 1024.
+ /// </remarks>
+ [MaxLength(1024)]
+ [StringLength(1024)]
+ public string Name { get; set; }
+
+ /// <inheritdoc />
+ [ConcurrencyCheck]
+ public uint RowVersion { get; set; }
+
+ /// <summary>
+ /// Gets or sets a collection containing this collection's items.
+ /// </summary>
+ public virtual ICollection<CollectionItem> Items { get; protected set; }
+
+ /// <inheritdoc />
+ public void OnSavingChanges()
+ {
+ RowVersion++;
+ }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/CollectionItem.cs b/Jellyfin.Data/Entities/Libraries/CollectionItem.cs
new file mode 100644
index 000000000..c9306f630
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/CollectionItem.cs
@@ -0,0 +1,94 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing a collection item.
+ /// </summary>
+ public class CollectionItem : IHasConcurrencyToken
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CollectionItem"/> class.
+ /// </summary>
+ /// <param name="collection">The collection.</param>
+ /// <param name="previous">The previous item.</param>
+ /// <param name="next">The next item.</param>
+ public CollectionItem(Collection collection, CollectionItem previous, CollectionItem next)
+ {
+ if (collection == null)
+ {
+ throw new ArgumentNullException(nameof(collection));
+ }
+
+ collection.Items.Add(this);
+
+ if (next != null)
+ {
+ Next = next;
+ next.Previous = this;
+ }
+
+ if (previous != null)
+ {
+ Previous = previous;
+ previous.Next = this;
+ }
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CollectionItem"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected CollectionItem()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <remarks>
+ /// Identity, Indexed, Required.
+ /// </remarks>
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public int Id { get; set; }
+
+ /// <inheritdoc />
+ [ConcurrencyCheck]
+ public uint RowVersion { get; set; }
+
+ /// <summary>
+ /// Gets or sets the library item.
+ /// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
+ public virtual LibraryItem LibraryItem { get; set; }
+
+ /// <summary>
+ /// Gets or sets the next item in the collection.
+ /// </summary>
+ /// <remarks>
+ /// TODO check if this properly updated dependant and has the proper principal relationship
+ /// </remarks>
+ public virtual CollectionItem Next { get; set; }
+
+ /// <summary>
+ /// Gets or sets the previous item in the collection.
+ /// </summary>
+ /// <remarks>
+ /// TODO check if this properly updated dependant and has the proper principal relationship
+ /// </remarks>
+ public virtual CollectionItem Previous { get; set; }
+
+ /// <inheritdoc />
+ public void OnSavingChanges()
+ {
+ RowVersion++;
+ }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/Company.cs b/Jellyfin.Data/Entities/Libraries/Company.cs
new file mode 100644
index 000000000..02da26bc2
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/Company.cs
@@ -0,0 +1,67 @@
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing a company.
+ /// </summary>
+ public class Company : IHasCompanies, IHasConcurrencyToken
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Company"/> class.
+ /// </summary>
+ /// <param name="owner">The owner of this company.</param>
+ public Company(IHasCompanies owner)
+ {
+ owner?.Companies.Add(this);
+
+ CompanyMetadata = new HashSet<CompanyMetadata>();
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Company"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected Company()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <remarks>
+ /// Identity, Indexed, Required.
+ /// </remarks>
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public int Id { get; protected set; }
+
+ /// <inheritdoc />
+ [ConcurrencyCheck]
+ public uint RowVersion { get; set; }
+
+ /// <summary>
+ /// Gets or sets a collection containing the metadata.
+ /// </summary>
+ public virtual ICollection<CompanyMetadata> CompanyMetadata { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets a collection containing this company's child companies.
+ /// </summary>
+ public virtual ICollection<Company> ChildCompanies { get; protected set; }
+
+ /// <inheritdoc />
+ [NotMapped]
+ public ICollection<Company> Companies => ChildCompanies;
+
+ /// <inheritdoc />
+ public void OnSavingChanges()
+ {
+ RowVersion++;
+ }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/CompanyMetadata.cs b/Jellyfin.Data/Entities/Libraries/CompanyMetadata.cs
new file mode 100644
index 000000000..60cc96a34
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/CompanyMetadata.cs
@@ -0,0 +1,74 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity holding metadata for a <see cref="Company"/>.
+ /// </summary>
+ public class CompanyMetadata : Metadata
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CompanyMetadata"/> class.
+ /// </summary>
+ /// <param name="title">The title or name of the object.</param>
+ /// <param name="language">ISO-639-3 3-character language codes.</param>
+ /// <param name="company">The company.</param>
+ public CompanyMetadata(string title, string language, Company company) : base(title, language)
+ {
+ if (company == null)
+ {
+ throw new ArgumentNullException(nameof(company));
+ }
+
+ company.CompanyMetadata.Add(this);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CompanyMetadata"/> class.
+ /// </summary>
+ protected CompanyMetadata()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the description.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 65535.
+ /// </remarks>
+ [MaxLength(65535)]
+ [StringLength(65535)]
+ public string Description { get; set; }
+
+ /// <summary>
+ /// Gets or sets the headquarters.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 255.
+ /// </remarks>
+ [MaxLength(255)]
+ [StringLength(255)]
+ public string Headquarters { get; set; }
+
+ /// <summary>
+ /// Gets or sets the country code.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 2.
+ /// </remarks>
+ [MaxLength(2)]
+ [StringLength(2)]
+ public string Country { get; set; }
+
+ /// <summary>
+ /// Gets or sets the homepage.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 1024.
+ /// </remarks>
+ [MaxLength(1024)]
+ [StringLength(1024)]
+ public string Homepage { get; set; }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/CustomItem.cs b/Jellyfin.Data/Entities/Libraries/CustomItem.cs
new file mode 100644
index 000000000..6a4f0a537
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/CustomItem.cs
@@ -0,0 +1,28 @@
+using System.Collections.Generic;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing a custom item.
+ /// </summary>
+ public class CustomItem : LibraryItem, IHasReleases
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CustomItem"/> class.
+ /// </summary>
+ public CustomItem()
+ {
+ CustomItemMetadata = new HashSet<CustomItemMetadata>();
+ Releases = new HashSet<Release>();
+ }
+
+ /// <summary>
+ /// Gets or sets a collection containing the metadata for this item.
+ /// </summary>
+ public virtual ICollection<CustomItemMetadata> CustomItemMetadata { get; protected set; }
+
+ /// <inheritdoc />
+ public virtual ICollection<Release> Releases { get; protected set; }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/CustomItemMetadata.cs b/Jellyfin.Data/Entities/Libraries/CustomItemMetadata.cs
new file mode 100644
index 000000000..bc1835528
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/CustomItemMetadata.cs
@@ -0,0 +1,36 @@
+using System;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity containing metadata for a custom item.
+ /// </summary>
+ public class CustomItemMetadata : Metadata
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CustomItemMetadata"/> class.
+ /// </summary>
+ /// <param name="title">The title or name of the object.</param>
+ /// <param name="language">ISO-639-3 3-character language codes.</param>
+ /// <param name="item">The item.</param>
+ public CustomItemMetadata(string title, string language, CustomItem item) : base(title, language)
+ {
+ if (item == null)
+ {
+ throw new ArgumentNullException(nameof(item));
+ }
+
+ item.CustomItemMetadata.Add(this);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CustomItemMetadata"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected CustomItemMetadata()
+ {
+ }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/Episode.cs b/Jellyfin.Data/Entities/Libraries/Episode.cs
new file mode 100644
index 000000000..430a11e3c
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/Episode.cs
@@ -0,0 +1,52 @@
+using System;
+using System.Collections.Generic;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing an episode.
+ /// </summary>
+ public class Episode : LibraryItem, IHasReleases
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Episode"/> class.
+ /// </summary>
+ /// <param name="season">The season.</param>
+ public Episode(Season season)
+ {
+ if (season == null)
+ {
+ throw new ArgumentNullException(nameof(season));
+ }
+
+ season.Episodes.Add(this);
+
+ Releases = new HashSet<Release>();
+ EpisodeMetadata = new HashSet<EpisodeMetadata>();
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Episode"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected Episode()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the episode number.
+ /// </summary>
+ public int? EpisodeNumber { get; set; }
+
+ /// <inheritdoc />
+ public virtual ICollection<Release> Releases { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets a collection containing the metadata for this episode.
+ /// </summary>
+ public virtual ICollection<EpisodeMetadata> EpisodeMetadata { get; protected set; }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/EpisodeMetadata.cs b/Jellyfin.Data/Entities/Libraries/EpisodeMetadata.cs
new file mode 100644
index 000000000..348100cb4
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/EpisodeMetadata.cs
@@ -0,0 +1,67 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity containing metadata for an <see cref="Episode"/>.
+ /// </summary>
+ public class EpisodeMetadata : Metadata
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="EpisodeMetadata"/> class.
+ /// </summary>
+ /// <param name="title">The title or name of the object.</param>
+ /// <param name="language">ISO-639-3 3-character language codes.</param>
+ /// <param name="episode">The episode.</param>
+ public EpisodeMetadata(string title, string language, Episode episode) : base(title, language)
+ {
+ if (episode == null)
+ {
+ throw new ArgumentNullException(nameof(episode));
+ }
+
+ episode.EpisodeMetadata.Add(this);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="EpisodeMetadata"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected EpisodeMetadata()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the outline.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 1024.
+ /// </remarks>
+ [MaxLength(1024)]
+ [StringLength(1024)]
+ public string Outline { get; set; }
+
+ /// <summary>
+ /// Gets or sets the plot.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 65535.
+ /// </remarks>
+ [MaxLength(65535)]
+ [StringLength(65535)]
+ public string Plot { get; set; }
+
+ /// <summary>
+ /// Gets or sets the tagline.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 1024.
+ /// </remarks>
+ [MaxLength(1024)]
+ [StringLength(1024)]
+ public string Tagline { get; set; }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/Genre.cs b/Jellyfin.Data/Entities/Libraries/Genre.cs
new file mode 100644
index 000000000..aeedd7bfd
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/Genre.cs
@@ -0,0 +1,75 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing a genre.
+ /// </summary>
+ public class Genre : IHasConcurrencyToken
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Genre"/> class.
+ /// </summary>
+ /// <param name="name">The name.</param>
+ /// <param name="metadata">The metadata.</param>
+ public Genre(string name, Metadata metadata)
+ {
+ if (string.IsNullOrEmpty(name))
+ {
+ throw new ArgumentNullException(nameof(name));
+ }
+
+ Name = name;
+
+ if (metadata == null)
+ {
+ throw new ArgumentNullException(nameof(metadata));
+ }
+
+ metadata.Genres.Add(this);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Genre"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected Genre()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <remarks>
+ /// Identity, Indexed, Required.
+ /// </remarks>
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public int Id { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ /// <remarks>
+ /// Indexed, Required, Max length = 255.
+ /// </remarks>
+ [Required]
+ [MaxLength(255)]
+ [StringLength(255)]
+ public string Name { get; set; }
+
+ /// <inheritdoc />
+ [ConcurrencyCheck]
+ public uint RowVersion { get; protected set; }
+
+ /// <inheritdoc />
+ public void OnSavingChanges()
+ {
+ RowVersion++;
+ }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/Library.cs b/Jellyfin.Data/Entities/Libraries/Library.cs
new file mode 100644
index 000000000..4f82a2e2a
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/Library.cs
@@ -0,0 +1,76 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing a library.
+ /// </summary>
+ public class Library : IHasConcurrencyToken
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Library"/> class.
+ /// </summary>
+ /// <param name="name">The name of the library.</param>
+ public Library(string name)
+ {
+ if (string.IsNullOrWhiteSpace(name))
+ {
+ throw new ArgumentNullException(nameof(name));
+ }
+
+ Name = name;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Library"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected Library()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <remarks>
+ /// Identity, Indexed, Required.
+ /// </remarks>
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public int Id { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ /// <remarks>
+ /// Required, Max length = 128.
+ /// </remarks>
+ [Required]
+ [MaxLength(128)]
+ [StringLength(128)]
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets or sets the root path of the library.
+ /// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
+ [Required]
+ public string Path { get; set; }
+
+ /// <inheritdoc />
+ [ConcurrencyCheck]
+ public uint RowVersion { get; set; }
+
+ /// <inheritdoc />
+ public void OnSavingChanges()
+ {
+ RowVersion++;
+ }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/LibraryItem.cs b/Jellyfin.Data/Entities/Libraries/LibraryItem.cs
new file mode 100644
index 000000000..a9167aa7f
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/LibraryItem.cs
@@ -0,0 +1,63 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing a library item.
+ /// </summary>
+ public abstract class LibraryItem : IHasConcurrencyToken
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="LibraryItem"/> class.
+ /// </summary>
+ /// <param name="library">The library of this item.</param>
+ protected LibraryItem(Library library)
+ {
+ DateAdded = DateTime.UtcNow;
+ Library = library;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="LibraryItem"/> class.
+ /// </summary>
+ protected LibraryItem()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <remarks>
+ /// Identity, Indexed, Required.
+ /// </remarks>
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public int Id { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets the date this library item was added.
+ /// </summary>
+ public DateTime DateAdded { get; protected set; }
+
+ /// <inheritdoc />
+ [ConcurrencyCheck]
+ public uint RowVersion { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets the library of this item.
+ /// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
+ [Required]
+ public virtual Library Library { get; set; }
+
+ /// <inheritdoc />
+ public void OnSavingChanges()
+ {
+ RowVersion++;
+ }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/MediaFile.cs b/Jellyfin.Data/Entities/Libraries/MediaFile.cs
new file mode 100644
index 000000000..8bc649c98
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/MediaFile.cs
@@ -0,0 +1,94 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Jellyfin.Data.Enums;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing a file on disk.
+ /// </summary>
+ public class MediaFile : IHasConcurrencyToken
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MediaFile"/> class.
+ /// </summary>
+ /// <param name="path">The path relative to the LibraryRoot.</param>
+ /// <param name="kind">The file kind.</param>
+ /// <param name="release">The release.</param>
+ public MediaFile(string path, MediaFileKind kind, Release release)
+ {
+ if (string.IsNullOrEmpty(path))
+ {
+ throw new ArgumentNullException(nameof(path));
+ }
+
+ Path = path;
+ Kind = kind;
+
+ if (release == null)
+ {
+ throw new ArgumentNullException(nameof(release));
+ }
+
+ release.MediaFiles.Add(this);
+
+ MediaFileStreams = new HashSet<MediaFileStream>();
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MediaFile"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected MediaFile()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <remarks>
+ /// Identity, Indexed, Required.
+ /// </remarks>
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public int Id { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets the path relative to the library root.
+ /// </summary>
+ /// <remarks>
+ /// Required, Max length = 65535.
+ /// </remarks>
+ [Required]
+ [MaxLength(65535)]
+ [StringLength(65535)]
+ public string Path { get; set; }
+
+ /// <summary>
+ /// Gets or sets the kind of media file.
+ /// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
+ public MediaFileKind Kind { get; set; }
+
+ /// <inheritdoc />
+ [ConcurrencyCheck]
+ public uint RowVersion { get; set; }
+
+ /// <summary>
+ /// Gets or sets a collection containing the streams in this file.
+ /// </summary>
+ public virtual ICollection<MediaFileStream> MediaFileStreams { get; protected set; }
+
+ /// <inheritdoc />
+ public void OnSavingChanges()
+ {
+ RowVersion++;
+ }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/MediaFileStream.cs b/Jellyfin.Data/Entities/Libraries/MediaFileStream.cs
new file mode 100644
index 000000000..5b03e260e
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/MediaFileStream.cs
@@ -0,0 +1,67 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing a stream in a media file.
+ /// </summary>
+ public class MediaFileStream : IHasConcurrencyToken
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MediaFileStream"/> class.
+ /// </summary>
+ /// <param name="streamNumber">The number of this stream.</param>
+ /// <param name="mediaFile">The media file.</param>
+ public MediaFileStream(int streamNumber, MediaFile mediaFile)
+ {
+ StreamNumber = streamNumber;
+
+ if (mediaFile == null)
+ {
+ throw new ArgumentNullException(nameof(mediaFile));
+ }
+
+ mediaFile.MediaFileStreams.Add(this);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MediaFileStream"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected MediaFileStream()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <remarks>
+ /// Identity, Indexed, Required.
+ /// </remarks>
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public int Id { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets the stream number.
+ /// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
+ public int StreamNumber { get; set; }
+
+ /// <inheritdoc />
+ [ConcurrencyCheck]
+ public uint RowVersion { get; set; }
+
+ /// <inheritdoc />
+ public void OnSavingChanges()
+ {
+ RowVersion++;
+ }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/Metadata.cs b/Jellyfin.Data/Entities/Libraries/Metadata.cs
new file mode 100644
index 000000000..877bb5fbd
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/Metadata.cs
@@ -0,0 +1,165 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An abstract class that holds metadata.
+ /// </summary>
+ public abstract class Metadata : IHasArtwork, IHasConcurrencyToken
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Metadata"/> class.
+ /// </summary>
+ /// <param name="title">The title or name of the object.</param>
+ /// <param name="language">ISO-639-3 3-character language codes.</param>
+ protected Metadata(string title, string language)
+ {
+ if (string.IsNullOrEmpty(title))
+ {
+ throw new ArgumentNullException(nameof(title));
+ }
+
+ if (string.IsNullOrEmpty(language))
+ {
+ throw new ArgumentNullException(nameof(language));
+ }
+
+ Title = title;
+ Language = language;
+ DateAdded = DateTime.UtcNow;
+ DateModified = DateAdded;
+
+ PersonRoles = new HashSet<PersonRole>();
+ Genres = new HashSet<Genre>();
+ Artwork = new HashSet<Artwork>();
+ Ratings = new HashSet<Rating>();
+ Sources = new HashSet<MetadataProviderId>();
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Metadata"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to being abstract.
+ /// </remarks>
+ protected Metadata()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <remarks>
+ /// Identity, Indexed, Required.
+ /// </remarks>
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public int Id { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets the title.
+ /// </summary>
+ /// <remarks>
+ /// Required, Max length = 1024.
+ /// </remarks>
+ [Required]
+ [MaxLength(1024)]
+ [StringLength(1024)]
+ public string Title { get; set; }
+
+ /// <summary>
+ /// Gets or sets the original title.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 1024.
+ /// </remarks>
+ [MaxLength(1024)]
+ [StringLength(1024)]
+ public string OriginalTitle { get; set; }
+
+ /// <summary>
+ /// Gets or sets the sort title.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 1024.
+ /// </remarks>
+ [MaxLength(1024)]
+ [StringLength(1024)]
+ public string SortTitle { get; set; }
+
+ /// <summary>
+ /// Gets or sets the language.
+ /// </summary>
+ /// <remarks>
+ /// Required, Min length = 3, Max length = 3.
+ /// ISO-639-3 3-character language codes.
+ /// </remarks>
+ [Required]
+ [MinLength(3)]
+ [MaxLength(3)]
+ [StringLength(3)]
+ public string Language { get; set; }
+
+ /// <summary>
+ /// Gets or sets the release date.
+ /// </summary>
+ public DateTimeOffset? ReleaseDate { get; set; }
+
+ /// <summary>
+ /// Gets or sets the date added.
+ /// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
+ public DateTime DateAdded { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets the date modified.
+ /// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
+ public DateTime DateModified { get; set; }
+
+ /// <summary>
+ /// Gets or sets the row version.
+ /// </summary>
+ /// <remarks>
+ /// Required, ConcurrencyToken.
+ /// </remarks>
+ [ConcurrencyCheck]
+ public uint RowVersion { get; set; }
+
+ /// <summary>
+ /// Gets or sets a collection containing the person roles for this item.
+ /// </summary>
+ public virtual ICollection<PersonRole> PersonRoles { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets a collection containing the generes for this item.
+ /// </summary>
+ public virtual ICollection<Genre> Genres { get; protected set; }
+
+ /// <inheritdoc />
+ public virtual ICollection<Artwork> Artwork { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets a collection containing the ratings for this item.
+ /// </summary>
+ public virtual ICollection<Rating> Ratings { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets a collection containing the metadata sources for this item.
+ /// </summary>
+ public virtual ICollection<MetadataProviderId> Sources { get; protected set; }
+
+ /// <inheritdoc />
+ public void OnSavingChanges()
+ {
+ RowVersion++;
+ }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/MetadataProvider.cs b/Jellyfin.Data/Entities/Libraries/MetadataProvider.cs
new file mode 100644
index 000000000..a18a612bc
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/MetadataProvider.cs
@@ -0,0 +1,67 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing a metadata provider.
+ /// </summary>
+ public class MetadataProvider : IHasConcurrencyToken
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MetadataProvider"/> class.
+ /// </summary>
+ /// <param name="name">The name of the metadata provider.</param>
+ public MetadataProvider(string name)
+ {
+ if (string.IsNullOrEmpty(name))
+ {
+ throw new ArgumentNullException(nameof(name));
+ }
+
+ Name = name;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MetadataProvider"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected MetadataProvider()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <remarks>
+ /// Identity, Indexed, Required.
+ /// </remarks>
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public int Id { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ /// <remarks>
+ /// Required, Max length = 1024.
+ /// </remarks>
+ [Required]
+ [MaxLength(1024)]
+ [StringLength(1024)]
+ public string Name { get; set; }
+
+ /// <inheritdoc />
+ [ConcurrencyCheck]
+ public uint RowVersion { get; set; }
+
+ /// <inheritdoc />
+ public void OnSavingChanges()
+ {
+ RowVersion++;
+ }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/MetadataProviderId.cs b/Jellyfin.Data/Entities/Libraries/MetadataProviderId.cs
new file mode 100644
index 000000000..6e6de598e
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/MetadataProviderId.cs
@@ -0,0 +1,83 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing a unique identifier for a metadata provider.
+ /// </summary>
+ public class MetadataProviderId : IHasConcurrencyToken
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MetadataProviderId"/> class.
+ /// </summary>
+ /// <param name="providerId">The provider id.</param>
+ /// <param name="metadata">The metadata entity.</param>
+ public MetadataProviderId(string providerId, Metadata metadata)
+ {
+ if (string.IsNullOrEmpty(providerId))
+ {
+ throw new ArgumentNullException(nameof(providerId));
+ }
+
+ ProviderId = providerId;
+
+ if (metadata == null)
+ {
+ throw new ArgumentNullException(nameof(metadata));
+ }
+
+ metadata.Sources.Add(this);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MetadataProviderId"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected MetadataProviderId()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <remarks>
+ /// Identity, Indexed, Required.
+ /// </remarks>
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public int Id { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets the provider id.
+ /// </summary>
+ /// <remarks>
+ /// Required, Max length = 255.
+ /// </remarks>
+ [Required]
+ [MaxLength(255)]
+ [StringLength(255)]
+ public string ProviderId { get; set; }
+
+ /// <inheritdoc />
+ [ConcurrencyCheck]
+ public uint RowVersion { get; set; }
+
+ /// <summary>
+ /// Gets or sets the metadata provider.
+ /// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
+ public virtual MetadataProvider MetadataProvider { get; set; }
+
+ /// <inheritdoc />
+ public void OnSavingChanges()
+ {
+ RowVersion++;
+ }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/Movie.cs b/Jellyfin.Data/Entities/Libraries/Movie.cs
new file mode 100644
index 000000000..0a8cc83dd
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/Movie.cs
@@ -0,0 +1,28 @@
+using System.Collections.Generic;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing a movie.
+ /// </summary>
+ public class Movie : LibraryItem, IHasReleases
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Movie"/> class.
+ /// </summary>
+ public Movie()
+ {
+ Releases = new HashSet<Release>();
+ MovieMetadata = new HashSet<MovieMetadata>();
+ }
+
+ /// <inheritdoc />
+ public virtual ICollection<Release> Releases { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets a collection containing the metadata for this movie.
+ /// </summary>
+ public virtual ICollection<MovieMetadata> MovieMetadata { get; protected set; }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/MovieMetadata.cs b/Jellyfin.Data/Entities/Libraries/MovieMetadata.cs
new file mode 100644
index 000000000..31102bf13
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/MovieMetadata.cs
@@ -0,0 +1,85 @@
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity holding the metadata for a movie.
+ /// </summary>
+ public class MovieMetadata : Metadata, IHasCompanies
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MovieMetadata"/> class.
+ /// </summary>
+ /// <param name="title">The title or name of the movie.</param>
+ /// <param name="language">ISO-639-3 3-character language codes.</param>
+ /// <param name="movie">The movie.</param>
+ public MovieMetadata(string title, string language, Movie movie) : base(title, language)
+ {
+ Studios = new HashSet<Company>();
+
+ movie.MovieMetadata.Add(this);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MovieMetadata"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected MovieMetadata()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the outline.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 1024.
+ /// </remarks>
+ [MaxLength(1024)]
+ [StringLength(1024)]
+ public string Outline { get; set; }
+
+ /// <summary>
+ /// Gets or sets the tagline.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 1024.
+ /// </remarks>
+ [MaxLength(1024)]
+ [StringLength(1024)]
+ public string Tagline { get; set; }
+
+ /// <summary>
+ /// Gets or sets the plot.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 65535.
+ /// </remarks>
+ [MaxLength(65535)]
+ [StringLength(65535)]
+ public string Plot { get; set; }
+
+ /// <summary>
+ /// Gets or sets the country code.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 2.
+ /// </remarks>
+ [MaxLength(2)]
+ [StringLength(2)]
+ public string Country { get; set; }
+
+ /// <summary>
+ /// Gets or sets the studios that produced this movie.
+ /// </summary>
+ public virtual ICollection<Company> Studios { get; protected set; }
+
+ /// <inheritdoc />
+ [NotMapped]
+ public ICollection<Company> Companies => Studios;
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/MusicAlbum.cs b/Jellyfin.Data/Entities/Libraries/MusicAlbum.cs
new file mode 100644
index 000000000..2ed1f78c5
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/MusicAlbum.cs
@@ -0,0 +1,29 @@
+using System.Collections.Generic;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing a music album.
+ /// </summary>
+ public class MusicAlbum : LibraryItem
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MusicAlbum"/> class.
+ /// </summary>
+ public MusicAlbum()
+ {
+ MusicAlbumMetadata = new HashSet<MusicAlbumMetadata>();
+ Tracks = new HashSet<Track>();
+ }
+
+ /// <summary>
+ /// Gets or sets a collection containing the album metadata.
+ /// </summary>
+ public virtual ICollection<MusicAlbumMetadata> MusicAlbumMetadata { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets a collection containing the tracks.
+ /// </summary>
+ public virtual ICollection<Track> Tracks { get; protected set; }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/MusicAlbumMetadata.cs b/Jellyfin.Data/Entities/Libraries/MusicAlbumMetadata.cs
new file mode 100644
index 000000000..cc5919bfe
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/MusicAlbumMetadata.cs
@@ -0,0 +1,69 @@
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity holding the metadata for a music album.
+ /// </summary>
+ public class MusicAlbumMetadata : Metadata
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MusicAlbumMetadata"/> class.
+ /// </summary>
+ /// <param name="title">The title or name of the album.</param>
+ /// <param name="language">ISO-639-3 3-character language codes.</param>
+ /// <param name="album">The music album.</param>
+ public MusicAlbumMetadata(string title, string language, MusicAlbum album) : base(title, language)
+ {
+ Labels = new HashSet<Company>();
+
+ album.MusicAlbumMetadata.Add(this);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MusicAlbumMetadata"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected MusicAlbumMetadata()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the barcode.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 255.
+ /// </remarks>
+ [MaxLength(255)]
+ [StringLength(255)]
+ public string Barcode { get; set; }
+
+ /// <summary>
+ /// Gets or sets the label number.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 255.
+ /// </remarks>
+ [MaxLength(255)]
+ [StringLength(255)]
+ public string LabelNumber { get; set; }
+
+ /// <summary>
+ /// Gets or sets the country code.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 2.
+ /// </remarks>
+ [MaxLength(2)]
+ [StringLength(2)]
+ public string Country { get; set; }
+
+ /// <summary>
+ /// Gets or sets a collection containing the labels.
+ /// </summary>
+ public virtual ICollection<Company> Labels { get; protected set; }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/Person.cs b/Jellyfin.Data/Entities/Libraries/Person.cs
new file mode 100644
index 000000000..8beb3dd08
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/Person.cs
@@ -0,0 +1,103 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing a person.
+ /// </summary>
+ public class Person : IHasConcurrencyToken
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Person"/> class.
+ /// </summary>
+ /// <param name="name">The name of the person.</param>
+ public Person(string name)
+ {
+ if (string.IsNullOrEmpty(name))
+ {
+ throw new ArgumentNullException(nameof(name));
+ }
+
+ Name = name;
+ DateAdded = DateTime.UtcNow;
+ DateModified = DateAdded;
+
+ Sources = new HashSet<MetadataProviderId>();
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Person"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected Person()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <remarks>
+ /// Identity, Indexed, Required.
+ /// </remarks>
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public int Id { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ /// <remarks>
+ /// Required, Max length = 1024.
+ /// </remarks>
+ [Required]
+ [MaxLength(1024)]
+ [StringLength(1024)]
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets or sets the source id.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 255.
+ /// </remarks>
+ [MaxLength(256)]
+ [StringLength(256)]
+ public string SourceId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the date added.
+ /// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
+ public DateTime DateAdded { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets the date modified.
+ /// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
+ public DateTime DateModified { get; set; }
+
+ /// <inheritdoc />
+ [ConcurrencyCheck]
+ public uint RowVersion { get; set; }
+
+ /// <summary>
+ /// Gets or sets a list of metadata sources for this person.
+ /// </summary>
+ public virtual ICollection<MetadataProviderId> Sources { get; protected set; }
+
+ /// <inheritdoc />
+ public void OnSavingChanges()
+ {
+ RowVersion++;
+ }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/PersonRole.cs b/Jellyfin.Data/Entities/Libraries/PersonRole.cs
new file mode 100644
index 000000000..5290228d6
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/PersonRole.cs
@@ -0,0 +1,98 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Jellyfin.Data.Enums;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing a person's role in media.
+ /// </summary>
+ public class PersonRole : IHasArtwork, IHasConcurrencyToken
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PersonRole"/> class.
+ /// </summary>
+ /// <param name="type">The role type.</param>
+ /// <param name="metadata">The metadata.</param>
+ public PersonRole(PersonRoleType type, Metadata metadata)
+ {
+ Type = type;
+
+ if (metadata == null)
+ {
+ throw new ArgumentNullException(nameof(metadata));
+ }
+
+ metadata.PersonRoles.Add(this);
+
+ Sources = new HashSet<MetadataProviderId>();
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PersonRole"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected PersonRole()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <remarks>
+ /// Identity, Indexed, Required.
+ /// </remarks>
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public int Id { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets the name of the person's role.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 1024.
+ /// </remarks>
+ [MaxLength(1024)]
+ [StringLength(1024)]
+ public string Role { get; set; }
+
+ /// <summary>
+ /// Gets or sets the person's role type.
+ /// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
+ public PersonRoleType Type { get; set; }
+
+ /// <inheritdoc />
+ [ConcurrencyCheck]
+ public uint RowVersion { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets the person.
+ /// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
+ [Required]
+ public virtual Person Person { get; set; }
+
+ /// <inheritdoc />
+ public virtual ICollection<Artwork> Artwork { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets a collection containing the metadata sources for this person role.
+ /// </summary>
+ public virtual ICollection<MetadataProviderId> Sources { get; protected set; }
+
+ /// <inheritdoc />
+ public void OnSavingChanges()
+ {
+ RowVersion++;
+ }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/Photo.cs b/Jellyfin.Data/Entities/Libraries/Photo.cs
new file mode 100644
index 000000000..44338a4ce
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/Photo.cs
@@ -0,0 +1,28 @@
+using System.Collections.Generic;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing a photo.
+ /// </summary>
+ public class Photo : LibraryItem, IHasReleases
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Photo"/> class.
+ /// </summary>
+ public Photo()
+ {
+ PhotoMetadata = new HashSet<PhotoMetadata>();
+ Releases = new HashSet<Release>();
+ }
+
+ /// <summary>
+ /// Gets or sets a collection containing the photo metadata.
+ /// </summary>
+ public virtual ICollection<PhotoMetadata> PhotoMetadata { get; protected set; }
+
+ /// <inheritdoc />
+ public virtual ICollection<Release> Releases { get; protected set; }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/PhotoMetadata.cs b/Jellyfin.Data/Entities/Libraries/PhotoMetadata.cs
new file mode 100644
index 000000000..1ef9dd5f9
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/PhotoMetadata.cs
@@ -0,0 +1,36 @@
+using System;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity that holds metadata for a photo.
+ /// </summary>
+ public class PhotoMetadata : Metadata
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PhotoMetadata"/> class.
+ /// </summary>
+ /// <param name="title">The title or name of the photo.</param>
+ /// <param name="language">ISO-639-3 3-character language codes.</param>
+ /// <param name="photo">The photo.</param>
+ public PhotoMetadata(string title, string language, Photo photo) : base(title, language)
+ {
+ if (photo == null)
+ {
+ throw new ArgumentNullException(nameof(photo));
+ }
+
+ photo.PhotoMetadata.Add(this);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PhotoMetadata"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected PhotoMetadata()
+ {
+ }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/Rating.cs b/Jellyfin.Data/Entities/Libraries/Rating.cs
new file mode 100644
index 000000000..ba054a39e
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/Rating.cs
@@ -0,0 +1,78 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing a rating for an entity.
+ /// </summary>
+ public class Rating : IHasConcurrencyToken
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Rating"/> class.
+ /// </summary>
+ /// <param name="value">The value.</param>
+ /// <param name="metadata">The metadata.</param>
+ public Rating(double value, Metadata metadata)
+ {
+ Value = value;
+
+ if (metadata == null)
+ {
+ throw new ArgumentNullException(nameof(metadata));
+ }
+
+ metadata.Ratings.Add(this);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Rating"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected Rating()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <remarks>
+ /// Identity, Indexed, Required.
+ /// </remarks>
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public int Id { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets the value.
+ /// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
+ public double Value { get; set; }
+
+ /// <summary>
+ /// Gets or sets the number of votes.
+ /// </summary>
+ public int? Votes { get; set; }
+
+ /// <inheritdoc />
+ [ConcurrencyCheck]
+ public uint RowVersion { get; set; }
+
+ /// <summary>
+ /// Gets or sets the rating type.
+ /// If this is <c>null</c> it's the internal user rating.
+ /// </summary>
+ public virtual RatingSource RatingType { get; set; }
+
+ /// <inheritdoc />
+ public void OnSavingChanges()
+ {
+ RowVersion++;
+ }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/RatingSource.cs b/Jellyfin.Data/Entities/Libraries/RatingSource.cs
new file mode 100644
index 000000000..549f41804
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/RatingSource.cs
@@ -0,0 +1,92 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// This is the entity to store review ratings, not age ratings.
+ /// </summary>
+ public class RatingSource : IHasConcurrencyToken
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="RatingSource"/> class.
+ /// </summary>
+ /// <param name="minimumValue">The minimum value.</param>
+ /// <param name="maximumValue">The maximum value.</param>
+ /// <param name="rating">The rating.</param>
+ public RatingSource(double minimumValue, double maximumValue, Rating rating)
+ {
+ MinimumValue = minimumValue;
+ MaximumValue = maximumValue;
+
+ if (rating == null)
+ {
+ throw new ArgumentNullException(nameof(rating));
+ }
+
+ rating.RatingType = this;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="RatingSource"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected RatingSource()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <remarks>
+ /// Identity, Indexed, Required.
+ /// </remarks>
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public int Id { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 1024.
+ /// </remarks>
+ [MaxLength(1024)]
+ [StringLength(1024)]
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets or sets the minimum value.
+ /// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
+ public double MinimumValue { get; set; }
+
+ /// <summary>
+ /// Gets or sets the maximum value.
+ /// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
+ public double MaximumValue { get; set; }
+
+ /// <inheritdoc />
+ [ConcurrencyCheck]
+ public uint RowVersion { get; set; }
+
+ /// <summary>
+ /// Gets or sets the metadata source.
+ /// </summary>
+ public virtual MetadataProviderId Source { get; set; }
+
+ /// <inheritdoc />
+ public void OnSavingChanges()
+ {
+ RowVersion++;
+ }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/Release.cs b/Jellyfin.Data/Entities/Libraries/Release.cs
new file mode 100644
index 000000000..43c7080d7
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/Release.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing a release for a library item, eg. Director's cut vs. standard.
+ /// </summary>
+ public class Release : IHasConcurrencyToken
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Release"/> class.
+ /// </summary>
+ /// <param name="name">The name of this release.</param>
+ /// <param name="owner">The owner of this release.</param>
+ public Release(string name, IHasReleases owner)
+ {
+ if (string.IsNullOrEmpty(name))
+ {
+ throw new ArgumentNullException(nameof(name));
+ }
+
+ Name = name;
+
+ owner?.Releases.Add(this);
+
+ MediaFiles = new HashSet<MediaFile>();
+ Chapters = new HashSet<Chapter>();
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Release"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected Release()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <remarks>
+ /// Identity, Indexed, Required.
+ /// </remarks>
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public int Id { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ /// <remarks>
+ /// Required, Max length = 1024.
+ /// </remarks>
+ [Required]
+ [MaxLength(1024)]
+ [StringLength(1024)]
+ public string Name { get; set; }
+
+ /// <inheritdoc />
+ [ConcurrencyCheck]
+ public uint RowVersion { get; set; }
+
+ /// <summary>
+ /// Gets or sets a collection containing the media files for this release.
+ /// </summary>
+ public virtual ICollection<MediaFile> MediaFiles { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets a collection containing the chapters for this release.
+ /// </summary>
+ public virtual ICollection<Chapter> Chapters { get; protected set; }
+
+ /// <inheritdoc />
+ public void OnSavingChanges()
+ {
+ RowVersion++;
+ }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/Season.cs b/Jellyfin.Data/Entities/Libraries/Season.cs
new file mode 100644
index 000000000..eef788bad
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/Season.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing a season.
+ /// </summary>
+ public class Season : LibraryItem
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Season"/> class.
+ /// </summary>
+ /// <param name="series">The series.</param>
+ public Season(Series series)
+ {
+ if (series == null)
+ {
+ throw new ArgumentNullException(nameof(series));
+ }
+
+ series.Seasons.Add(this);
+
+ Episodes = new HashSet<Episode>();
+ SeasonMetadata = new HashSet<SeasonMetadata>();
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Season"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected Season()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the season number.
+ /// </summary>
+ public int? SeasonNumber { get; set; }
+
+ /// <summary>
+ /// Gets or sets the season metadata.
+ /// </summary>
+ public virtual ICollection<SeasonMetadata> SeasonMetadata { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets a collection containing the number of episodes.
+ /// </summary>
+ public virtual ICollection<Episode> Episodes { get; protected set; }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/SeasonMetadata.cs b/Jellyfin.Data/Entities/Libraries/SeasonMetadata.cs
new file mode 100644
index 000000000..eedeb089e
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/SeasonMetadata.cs
@@ -0,0 +1,47 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity that holds metadata for seasons.
+ /// </summary>
+ public class SeasonMetadata : Metadata
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SeasonMetadata"/> class.
+ /// </summary>
+ /// <param name="title">The title or name of the object.</param>
+ /// <param name="language">ISO-639-3 3-character language codes.</param>
+ /// <param name="season">The season.</param>
+ public SeasonMetadata(string title, string language, Season season) : base(title, language)
+ {
+ if (season == null)
+ {
+ throw new ArgumentNullException(nameof(season));
+ }
+
+ season.SeasonMetadata.Add(this);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SeasonMetadata"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected SeasonMetadata()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the outline.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 1024.
+ /// </remarks>
+ [MaxLength(1024)]
+ [StringLength(1024)]
+ public string Outline { get; set; }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/Series.cs b/Jellyfin.Data/Entities/Libraries/Series.cs
new file mode 100644
index 000000000..e959c1fe0
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/Series.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Collections.Generic;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing a a series.
+ /// </summary>
+ public class Series : LibraryItem
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Series"/> class.
+ /// </summary>
+ public Series()
+ {
+ DateAdded = DateTime.UtcNow;
+ Seasons = new HashSet<Season>();
+ SeriesMetadata = new HashSet<SeriesMetadata>();
+ }
+
+ /// <summary>
+ /// Gets or sets the days of week.
+ /// </summary>
+ public DayOfWeek? AirsDayOfWeek { get; set; }
+
+ /// <summary>
+ /// Gets or sets the time the show airs, ignore the date portion.
+ /// </summary>
+ public DateTimeOffset? AirsTime { get; set; }
+
+ /// <summary>
+ /// Gets or sets the date the series first aired.
+ /// </summary>
+ public DateTime? FirstAired { get; set; }
+
+ /// <summary>
+ /// Gets or sets a collection containing the series metadata.
+ /// </summary>
+ public virtual ICollection<SeriesMetadata> SeriesMetadata { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets a collection containing the seasons.
+ /// </summary>
+ public virtual ICollection<Season> Seasons { get; protected set; }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/SeriesMetadata.cs b/Jellyfin.Data/Entities/Libraries/SeriesMetadata.cs
new file mode 100644
index 000000000..898f3006d
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/SeriesMetadata.cs
@@ -0,0 +1,91 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing series metadata.
+ /// </summary>
+ public class SeriesMetadata : Metadata, IHasCompanies
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SeriesMetadata"/> class.
+ /// </summary>
+ /// <param name="title">The title or name of the object.</param>
+ /// <param name="language">ISO-639-3 3-character language codes.</param>
+ /// <param name="series">The series.</param>
+ public SeriesMetadata(string title, string language, Series series) : base(title, language)
+ {
+ if (series == null)
+ {
+ throw new ArgumentNullException(nameof(series));
+ }
+
+ series.SeriesMetadata.Add(this);
+
+ Networks = new HashSet<Company>();
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SeriesMetadata"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected SeriesMetadata()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the outline.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 1024.
+ /// </remarks>
+ [MaxLength(1024)]
+ [StringLength(1024)]
+ public string Outline { get; set; }
+
+ /// <summary>
+ /// Gets or sets the plot.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 65535.
+ /// </remarks>
+ [MaxLength(65535)]
+ [StringLength(65535)]
+ public string Plot { get; set; }
+
+ /// <summary>
+ /// Gets or sets the tagline.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 1024.
+ /// </remarks>
+ [MaxLength(1024)]
+ [StringLength(1024)]
+ public string Tagline { get; set; }
+
+ /// <summary>
+ /// Gets or sets the country code.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 2.
+ /// </remarks>
+ [MaxLength(2)]
+ [StringLength(2)]
+ public string Country { get; set; }
+
+ /// <summary>
+ /// Gets or sets a collection containing the networks.
+ /// </summary>
+ public virtual ICollection<Company> Networks { get; protected set; }
+
+ /// <inheritdoc />
+ [NotMapped]
+ public ICollection<Company> Companies => Networks;
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/Track.cs b/Jellyfin.Data/Entities/Libraries/Track.cs
new file mode 100644
index 000000000..09ce82a9b
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/Track.cs
@@ -0,0 +1,52 @@
+using System;
+using System.Collections.Generic;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing a track.
+ /// </summary>
+ public class Track : LibraryItem, IHasReleases
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Track"/> class.
+ /// </summary>
+ /// <param name="album">The album.</param>
+ public Track(MusicAlbum album)
+ {
+ if (album == null)
+ {
+ throw new ArgumentNullException(nameof(album));
+ }
+
+ album.Tracks.Add(this);
+
+ Releases = new HashSet<Release>();
+ TrackMetadata = new HashSet<TrackMetadata>();
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Track"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected Track()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the track number.
+ /// </summary>
+ public int? TrackNumber { get; set; }
+
+ /// <inheritdoc />
+ public virtual ICollection<Release> Releases { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets a collection containing the track metadata.
+ /// </summary>
+ public virtual ICollection<TrackMetadata> TrackMetadata { get; protected set; }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/TrackMetadata.cs b/Jellyfin.Data/Entities/Libraries/TrackMetadata.cs
new file mode 100644
index 000000000..048068a1a
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/TrackMetadata.cs
@@ -0,0 +1,36 @@
+using System;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity holding metadata for a track.
+ /// </summary>
+ public class TrackMetadata : Metadata
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TrackMetadata"/> class.
+ /// </summary>
+ /// <param name="title">The title or name of the object.</param>
+ /// <param name="language">ISO-639-3 3-character language codes.</param>
+ /// <param name="track">The track.</param>
+ public TrackMetadata(string title, string language, Track track) : base(title, language)
+ {
+ if (track == null)
+ {
+ throw new ArgumentNullException(nameof(track));
+ }
+
+ track.TrackMetadata.Add(this);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TrackMetadata"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected TrackMetadata()
+ {
+ }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Library.cs b/Jellyfin.Data/Entities/Library.cs
deleted file mode 100644
index d935e43b1..000000000
--- a/Jellyfin.Data/Entities/Library.cs
+++ /dev/null
@@ -1,151 +0,0 @@
-using System;
-using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Jellyfin.Data.Entities
-{
- public partial class Library
- {
- partial void Init();
-
- /// <summary>
- /// Default constructor. Protected due to required properties, but present because EF needs it.
- /// </summary>
- protected Library()
- {
- Init();
- }
-
- /// <summary>
- /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
- /// </summary>
- public static Library CreateLibraryUnsafe()
- {
- return new Library();
- }
-
- /// <summary>
- /// Public constructor with required data.
- /// </summary>
- /// <param name="name"></param>
- public Library(string name)
- {
- if (string.IsNullOrEmpty(name))
- {
- throw new ArgumentNullException(nameof(name));
- }
-
- this.Name = name;
-
- Init();
- }
-
- /// <summary>
- /// Static create function (for use in LINQ queries, etc.)
- /// </summary>
- /// <param name="name"></param>
- public static Library Create(string name)
- {
- return new Library(name);
- }
-
- /*************************************************************************
- * Properties
- *************************************************************************/
-
- /// <summary>
- /// Backing field for Id.
- /// </summary>
- internal int _Id;
- /// <summary>
- /// When provided in a partial class, allows value of Id to be changed before setting.
- /// </summary>
- partial void SetId(int oldValue, ref int newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Id to be changed before returning.
- /// </summary>
- partial void GetId(ref int result);
-
- /// <summary>
- /// Identity, Indexed, Required.
- /// </summary>
- [Key]
- [Required]
- [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id
- {
- get
- {
- int value = _Id;
- GetId(ref value);
- return _Id = value;
- }
-
- protected set
- {
- int oldValue = _Id;
- SetId(oldValue, ref value);
- if (oldValue != value)
- {
- _Id = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for Name.
- /// </summary>
- protected string _Name;
- /// <summary>
- /// When provided in a partial class, allows value of Name to be changed before setting.
- /// </summary>
- partial void SetName(string oldValue, ref string newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Name to be changed before returning.
- /// </summary>
- partial void GetName(ref string result);
-
- /// <summary>
- /// Required, Max length = 1024
- /// </summary>
- [Required]
- [MaxLength(1024)]
- [StringLength(1024)]
- public string Name
- {
- get
- {
- string value = _Name;
- GetName(ref value);
- return _Name = value;
- }
-
- set
- {
- string oldValue = _Name;
- SetName(oldValue, ref value);
- if (oldValue != value)
- {
- _Name = value;
- }
- }
- }
-
- /// <summary>
- /// Required, ConcurrenyToken.
- /// </summary>
- [ConcurrencyCheck]
- [Required]
- public uint RowVersion { get; set; }
-
- public void OnSavingChanges()
- {
- RowVersion++;
- }
-
- /*************************************************************************
- * Navigation properties
- *************************************************************************/
- }
-}
-
diff --git a/Jellyfin.Data/Entities/LibraryItem.cs b/Jellyfin.Data/Entities/LibraryItem.cs
deleted file mode 100644
index f41753560..000000000
--- a/Jellyfin.Data/Entities/LibraryItem.cs
+++ /dev/null
@@ -1,172 +0,0 @@
-using System;
-using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Jellyfin.Data.Entities
-{
- public abstract partial class LibraryItem
- {
- partial void Init();
-
- /// <summary>
- /// Default constructor. Protected due to being abstract.
- /// </summary>
- protected LibraryItem()
- {
- Init();
- }
-
- /// <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>
- protected LibraryItem(Guid urlid, DateTime dateadded)
- {
- this.UrlId = urlid;
-
-
- Init();
- }
-
- /*************************************************************************
- * Properties
- *************************************************************************/
-
- /// <summary>
- /// Backing field for Id.
- /// </summary>
- internal int _Id;
- /// <summary>
- /// When provided in a partial class, allows value of Id to be changed before setting.
- /// </summary>
- partial void SetId(int oldValue, ref int newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Id to be changed before returning.
- /// </summary>
- partial void GetId(ref int result);
-
- /// <summary>
- /// Identity, Indexed, Required.
- /// </summary>
- [Key]
- [Required]
- [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id
- {
- get
- {
- int value = _Id;
- GetId(ref value);
- return _Id = value;
- }
-
- protected set
- {
- int oldValue = _Id;
- SetId(oldValue, ref value);
- if (oldValue != value)
- {
- _Id = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for UrlId.
- /// </summary>
- internal Guid _UrlId;
- /// <summary>
- /// When provided in a partial class, allows value of UrlId to be changed before setting.
- /// </summary>
- partial void SetUrlId(Guid oldValue, ref Guid newValue);
- /// <summary>
- /// When provided in a partial class, allows value of UrlId to be changed before returning.
- /// </summary>
- partial void GetUrlId(ref Guid result);
-
- /// <summary>
- /// Indexed, Required
- /// This is whats gets displayed in the Urls and API requests. This could also be a string.
- /// </summary>
- [Required]
- public Guid UrlId
- {
- get
- {
- Guid value = _UrlId;
- GetUrlId(ref value);
- return _UrlId = value;
- }
-
- set
- {
- Guid oldValue = _UrlId;
- SetUrlId(oldValue, ref value);
- if (oldValue != value)
- {
- _UrlId = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for DateAdded.
- /// </summary>
- protected DateTime _DateAdded;
- /// <summary>
- /// When provided in a partial class, allows value of DateAdded to be changed before setting.
- /// </summary>
- partial void SetDateAdded(DateTime oldValue, ref DateTime newValue);
- /// <summary>
- /// When provided in a partial class, allows value of DateAdded to be changed before returning.
- /// </summary>
- partial void GetDateAdded(ref DateTime result);
-
- /// <summary>
- /// Required.
- /// </summary>
- [Required]
- public DateTime DateAdded
- {
- get
- {
- DateTime value = _DateAdded;
- GetDateAdded(ref value);
- return _DateAdded = value;
- }
-
- internal set
- {
- DateTime oldValue = _DateAdded;
- SetDateAdded(oldValue, ref value);
- if (oldValue != value)
- {
- _DateAdded = value;
- }
- }
- }
-
- /// <summary>
- /// Required, ConcurrenyToken.
- /// </summary>
- [ConcurrencyCheck]
- [Required]
- public uint RowVersion { get; set; }
-
- public void OnSavingChanges()
- {
- RowVersion++;
- }
-
- /*************************************************************************
- * Navigation properties
- *************************************************************************/
-
- /// <summary>
- /// Required.
- /// </summary>
- [ForeignKey("LibraryRoot_Id")]
- public virtual LibraryRoot LibraryRoot { get; set; }
- }
-}
-
diff --git a/Jellyfin.Data/Entities/LibraryRoot.cs b/Jellyfin.Data/Entities/LibraryRoot.cs
deleted file mode 100644
index 9695ed638..000000000
--- a/Jellyfin.Data/Entities/LibraryRoot.cs
+++ /dev/null
@@ -1,197 +0,0 @@
-using System;
-using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Jellyfin.Data.Entities
-{
- public partial class LibraryRoot
- {
- partial void Init();
-
- /// <summary>
- /// Default constructor. Protected due to required properties, but present because EF needs it.
- /// </summary>
- protected LibraryRoot()
- {
- Init();
- }
-
- /// <summary>
- /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
- /// </summary>
- public static LibraryRoot CreateLibraryRootUnsafe()
- {
- return new LibraryRoot();
- }
-
- /// <summary>
- /// Public constructor with required data.
- /// </summary>
- /// <param name="path">Absolute Path.</param>
- public LibraryRoot(string path)
- {
- if (string.IsNullOrEmpty(path))
- {
- throw new ArgumentNullException(nameof(path));
- }
-
- this.Path = path;
-
- Init();
- }
-
- /// <summary>
- /// Static create function (for use in LINQ queries, etc.)
- /// </summary>
- /// <param name="path">Absolute Path.</param>
- public static LibraryRoot Create(string path)
- {
- return new LibraryRoot(path);
- }
-
- /*************************************************************************
- * Properties
- *************************************************************************/
-
- /// <summary>
- /// Backing field for Id.
- /// </summary>
- internal int _Id;
- /// <summary>
- /// When provided in a partial class, allows value of Id to be changed before setting.
- /// </summary>
- partial void SetId(int oldValue, ref int newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Id to be changed before returning.
- /// </summary>
- partial void GetId(ref int result);
-
- /// <summary>
- /// Identity, Indexed, Required.
- /// </summary>
- [Key]
- [Required]
- [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id
- {
- get
- {
- int value = _Id;
- GetId(ref value);
- return _Id = value;
- }
-
- protected set
- {
- int oldValue = _Id;
- SetId(oldValue, ref value);
- if (oldValue != value)
- {
- _Id = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for Path.
- /// </summary>
- protected string _Path;
- /// <summary>
- /// When provided in a partial class, allows value of Path to be changed before setting.
- /// </summary>
- partial void SetPath(string oldValue, ref string newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Path to be changed before returning.
- /// </summary>
- partial void GetPath(ref string result);
-
- /// <summary>
- /// Required, Max length = 65535
- /// Absolute Path.
- /// </summary>
- [Required]
- [MaxLength(65535)]
- [StringLength(65535)]
- public string Path
- {
- get
- {
- string value = _Path;
- GetPath(ref value);
- return _Path = value;
- }
-
- set
- {
- string oldValue = _Path;
- SetPath(oldValue, ref value);
- if (oldValue != value)
- {
- _Path = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for NetworkPath.
- /// </summary>
- protected string _NetworkPath;
- /// <summary>
- /// When provided in a partial class, allows value of NetworkPath to be changed before setting.
- /// </summary>
- partial void SetNetworkPath(string oldValue, ref string newValue);
- /// <summary>
- /// When provided in a partial class, allows value of NetworkPath to be changed before returning.
- /// </summary>
- partial void GetNetworkPath(ref string result);
-
- /// <summary>
- /// Max length = 65535
- /// Absolute network path, for example for transcoding sattelites.
- /// </summary>
- [MaxLength(65535)]
- [StringLength(65535)]
- public string NetworkPath
- {
- get
- {
- string value = _NetworkPath;
- GetNetworkPath(ref value);
- return _NetworkPath = value;
- }
-
- set
- {
- string oldValue = _NetworkPath;
- SetNetworkPath(oldValue, ref value);
- if (oldValue != value)
- {
- _NetworkPath = value;
- }
- }
- }
-
- /// <summary>
- /// Required, ConcurrenyToken.
- /// </summary>
- [ConcurrencyCheck]
- [Required]
- public uint RowVersion { get; set; }
-
- public void OnSavingChanges()
- {
- RowVersion++;
- }
-
- /*************************************************************************
- * Navigation properties
- *************************************************************************/
-
- /// <summary>
- /// Required.
- /// </summary>
- [ForeignKey("Library_Id")]
- public virtual Library Library { get; set; }
- }
-}
-
diff --git a/Jellyfin.Data/Entities/MediaFile.cs b/Jellyfin.Data/Entities/MediaFile.cs
deleted file mode 100644
index 7382cda95..000000000
--- a/Jellyfin.Data/Entities/MediaFile.cs
+++ /dev/null
@@ -1,210 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Jellyfin.Data.Entities
-{
- public partial class MediaFile
- {
- partial void Init();
-
- /// <summary>
- /// Default constructor. Protected due to required properties, but present because EF needs it.
- /// </summary>
- protected MediaFile()
- {
- MediaFileStreams = new HashSet<MediaFileStream>();
-
- Init();
- }
-
- /// <summary>
- /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
- /// </summary>
- public static MediaFile CreateMediaFileUnsafe()
- {
- return new MediaFile();
- }
-
- /// <summary>
- /// Public constructor with required data.
- /// </summary>
- /// <param name="path">Relative to the LibraryRoot.</param>
- /// <param name="kind"></param>
- /// <param name="_release0"></param>
- public MediaFile(string path, Enums.MediaFileKind kind, Release _release0)
- {
- if (string.IsNullOrEmpty(path))
- {
- throw new ArgumentNullException(nameof(path));
- }
-
- this.Path = path;
-
- this.Kind = kind;
-
- if (_release0 == null)
- {
- throw new ArgumentNullException(nameof(_release0));
- }
-
- _release0.MediaFiles.Add(this);
-
- this.MediaFileStreams = new HashSet<MediaFileStream>();
-
- Init();
- }
-
- /// <summary>
- /// Static create function (for use in LINQ queries, etc.)
- /// </summary>
- /// <param name="path">Relative to the LibraryRoot.</param>
- /// <param name="kind"></param>
- /// <param name="_release0"></param>
- public static MediaFile Create(string path, Enums.MediaFileKind kind, Release _release0)
- {
- return new MediaFile(path, kind, _release0);
- }
-
- /*************************************************************************
- * Properties
- *************************************************************************/
-
- /// <summary>
- /// Backing field for Id.
- /// </summary>
- internal int _Id;
- /// <summary>
- /// When provided in a partial class, allows value of Id to be changed before setting.
- /// </summary>
- partial void SetId(int oldValue, ref int newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Id to be changed before returning.
- /// </summary>
- partial void GetId(ref int result);
-
- /// <summary>
- /// Identity, Indexed, Required.
- /// </summary>
- [Key]
- [Required]
- [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id
- {
- get
- {
- int value = _Id;
- GetId(ref value);
- return _Id = value;
- }
-
- protected set
- {
- int oldValue = _Id;
- SetId(oldValue, ref value);
- if (oldValue != value)
- {
- _Id = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for Path.
- /// </summary>
- protected string _Path;
- /// <summary>
- /// When provided in a partial class, allows value of Path to be changed before setting.
- /// </summary>
- partial void SetPath(string oldValue, ref string newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Path to be changed before returning.
- /// </summary>
- partial void GetPath(ref string result);
-
- /// <summary>
- /// Required, Max length = 65535
- /// Relative to the LibraryRoot.
- /// </summary>
- [Required]
- [MaxLength(65535)]
- [StringLength(65535)]
- public string Path
- {
- get
- {
- string value = _Path;
- GetPath(ref value);
- return _Path = value;
- }
-
- set
- {
- string oldValue = _Path;
- SetPath(oldValue, ref value);
- if (oldValue != value)
- {
- _Path = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for Kind.
- /// </summary>
- protected Enums.MediaFileKind _Kind;
- /// <summary>
- /// When provided in a partial class, allows value of Kind to be changed before setting.
- /// </summary>
- partial void SetKind(Enums.MediaFileKind oldValue, ref Enums.MediaFileKind newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Kind to be changed before returning.
- /// </summary>
- partial void GetKind(ref Enums.MediaFileKind result);
-
- /// <summary>
- /// Required.
- /// </summary>
- [Required]
- public Enums.MediaFileKind Kind
- {
- get
- {
- Enums.MediaFileKind value = _Kind;
- GetKind(ref value);
- return _Kind = value;
- }
-
- set
- {
- Enums.MediaFileKind oldValue = _Kind;
- SetKind(oldValue, ref value);
- if (oldValue != value)
- {
- _Kind = value;
- }
- }
- }
-
- /// <summary>
- /// Required, ConcurrenyToken.
- /// </summary>
- [ConcurrencyCheck]
- [Required]
- public uint RowVersion { get; set; }
-
- public void OnSavingChanges()
- {
- RowVersion++;
- }
-
- /*************************************************************************
- * Navigation properties
- *************************************************************************/
-
- [ForeignKey("MediaFileStream_MediaFileStreams_Id")]
- public virtual ICollection<MediaFileStream> MediaFileStreams { get; protected set; }
- }
-}
-
diff --git a/Jellyfin.Data/Entities/MediaFileStream.cs b/Jellyfin.Data/Entities/MediaFileStream.cs
deleted file mode 100644
index 977fd54e1..000000000
--- a/Jellyfin.Data/Entities/MediaFileStream.cs
+++ /dev/null
@@ -1,153 +0,0 @@
-using System;
-using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Jellyfin.Data.Entities
-{
- public partial class MediaFileStream
- {
- partial void Init();
-
- /// <summary>
- /// Default constructor. Protected due to required properties, but present because EF needs it.
- /// </summary>
- protected MediaFileStream()
- {
- Init();
- }
-
- /// <summary>
- /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
- /// </summary>
- public static MediaFileStream CreateMediaFileStreamUnsafe()
- {
- return new MediaFileStream();
- }
-
- /// <summary>
- /// Public constructor with required data.
- /// </summary>
- /// <param name="streamnumber"></param>
- /// <param name="_mediafile0"></param>
- public MediaFileStream(int streamnumber, MediaFile _mediafile0)
- {
- this.StreamNumber = streamnumber;
-
- if (_mediafile0 == null)
- {
- throw new ArgumentNullException(nameof(_mediafile0));
- }
-
- _mediafile0.MediaFileStreams.Add(this);
-
- Init();
- }
-
- /// <summary>
- /// Static create function (for use in LINQ queries, etc.)
- /// </summary>
- /// <param name="streamnumber"></param>
- /// <param name="_mediafile0"></param>
- public static MediaFileStream Create(int streamnumber, MediaFile _mediafile0)
- {
- return new MediaFileStream(streamnumber, _mediafile0);
- }
-
- /*************************************************************************
- * Properties
- *************************************************************************/
-
- /// <summary>
- /// Backing field for Id.
- /// </summary>
- internal int _Id;
- /// <summary>
- /// When provided in a partial class, allows value of Id to be changed before setting.
- /// </summary>
- partial void SetId(int oldValue, ref int newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Id to be changed before returning.
- /// </summary>
- partial void GetId(ref int result);
-
- /// <summary>
- /// Identity, Indexed, Required.
- /// </summary>
- [Key]
- [Required]
- [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id
- {
- get
- {
- int value = _Id;
- GetId(ref value);
- return _Id = value;
- }
-
- protected set
- {
- int oldValue = _Id;
- SetId(oldValue, ref value);
- if (oldValue != value)
- {
- _Id = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for StreamNumber.
- /// </summary>
- protected int _StreamNumber;
- /// <summary>
- /// When provided in a partial class, allows value of StreamNumber to be changed before setting.
- /// </summary>
- partial void SetStreamNumber(int oldValue, ref int newValue);
- /// <summary>
- /// When provided in a partial class, allows value of StreamNumber to be changed before returning.
- /// </summary>
- partial void GetStreamNumber(ref int result);
-
- /// <summary>
- /// Required.
- /// </summary>
- [Required]
- public int StreamNumber
- {
- get
- {
- int value = _StreamNumber;
- GetStreamNumber(ref value);
- return _StreamNumber = value;
- }
-
- set
- {
- int oldValue = _StreamNumber;
- SetStreamNumber(oldValue, ref value);
- if (oldValue != value)
- {
- _StreamNumber = value;
- }
- }
- }
-
- /// <summary>
- /// Required, ConcurrenyToken.
- /// </summary>
- [ConcurrencyCheck]
- [Required]
- public uint RowVersion { get; set; }
-
- public void OnSavingChanges()
- {
- RowVersion++;
- }
-
- /*************************************************************************
- * Navigation properties
- *************************************************************************/
- }
-}
-
diff --git a/Jellyfin.Data/Entities/Metadata.cs b/Jellyfin.Data/Entities/Metadata.cs
deleted file mode 100644
index a4ac6dc54..000000000
--- a/Jellyfin.Data/Entities/Metadata.cs
+++ /dev/null
@@ -1,395 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Jellyfin.Data.Entities
-{
- public abstract partial class Metadata
- {
- partial void Init();
-
- /// <summary>
- /// Default constructor. Protected due to being abstract.
- /// </summary>
- protected Metadata()
- {
- PersonRoles = new HashSet<PersonRole>();
- Genres = new HashSet<Genre>();
- Artwork = new HashSet<Artwork>();
- Ratings = new HashSet<Rating>();
- Sources = new HashSet<MetadataProviderId>();
-
- Init();
- }
-
- /// <summary>
- /// Public constructor with required data.
- /// </summary>
- /// <param name="title">The title or name of the object.</param>
- /// <param name="language">ISO-639-3 3-character language codes.</param>
- protected Metadata(string title, string language, DateTime dateadded, DateTime datemodified)
- {
- if (string.IsNullOrEmpty(title))
- {
- throw new ArgumentNullException(nameof(title));
- }
-
- this.Title = title;
-
- if (string.IsNullOrEmpty(language))
- {
- throw new ArgumentNullException(nameof(language));
- }
-
- this.Language = language;
-
- this.PersonRoles = new HashSet<PersonRole>();
- this.Genres = new HashSet<Genre>();
- this.Artwork = new HashSet<Artwork>();
- this.Ratings = new HashSet<Rating>();
- this.Sources = new HashSet<MetadataProviderId>();
-
- Init();
- }
-
- /*************************************************************************
- * Properties
- *************************************************************************/
-
- /// <summary>
- /// Backing field for Id.
- /// </summary>
- internal int _Id;
- /// <summary>
- /// When provided in a partial class, allows value of Id to be changed before setting.
- /// </summary>
- partial void SetId(int oldValue, ref int newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Id to be changed before returning.
- /// </summary>
- partial void GetId(ref int result);
-
- /// <summary>
- /// Identity, Indexed, Required.
- /// </summary>
- [Key]
- [Required]
- [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id
- {
- get
- {
- int value = _Id;
- GetId(ref value);
- return _Id = value;
- }
-
- protected set
- {
- int oldValue = _Id;
- SetId(oldValue, ref value);
- if (oldValue != value)
- {
- _Id = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for Title.
- /// </summary>
- protected string _Title;
- /// <summary>
- /// When provided in a partial class, allows value of Title to be changed before setting.
- /// </summary>
- partial void SetTitle(string oldValue, ref string newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Title to be changed before returning.
- /// </summary>
- partial void GetTitle(ref string result);
-
- /// <summary>
- /// Required, Max length = 1024
- /// The title or name of the object.
- /// </summary>
- [Required]
- [MaxLength(1024)]
- [StringLength(1024)]
- public string Title
- {
- get
- {
- string value = _Title;
- GetTitle(ref value);
- return _Title = value;
- }
-
- set
- {
- string oldValue = _Title;
- SetTitle(oldValue, ref value);
- if (oldValue != value)
- {
- _Title = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for OriginalTitle.
- /// </summary>
- protected string _OriginalTitle;
- /// <summary>
- /// When provided in a partial class, allows value of OriginalTitle to be changed before setting.
- /// </summary>
- partial void SetOriginalTitle(string oldValue, ref string newValue);
- /// <summary>
- /// When provided in a partial class, allows value of OriginalTitle to be changed before returning.
- /// </summary>
- partial void GetOriginalTitle(ref string result);
-
- /// <summary>
- /// Max length = 1024
- /// </summary>
- [MaxLength(1024)]
- [StringLength(1024)]
- public string OriginalTitle
- {
- get
- {
- string value = _OriginalTitle;
- GetOriginalTitle(ref value);
- return _OriginalTitle = value;
- }
-
- set
- {
- string oldValue = _OriginalTitle;
- SetOriginalTitle(oldValue, ref value);
- if (oldValue != value)
- {
- _OriginalTitle = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for SortTitle.
- /// </summary>
- protected string _SortTitle;
- /// <summary>
- /// When provided in a partial class, allows value of SortTitle to be changed before setting.
- /// </summary>
- partial void SetSortTitle(string oldValue, ref string newValue);
- /// <summary>
- /// When provided in a partial class, allows value of SortTitle to be changed before returning.
- /// </summary>
- partial void GetSortTitle(ref string result);
-
- /// <summary>
- /// Max length = 1024
- /// </summary>
- [MaxLength(1024)]
- [StringLength(1024)]
- public string SortTitle
- {
- get
- {
- string value = _SortTitle;
- GetSortTitle(ref value);
- return _SortTitle = value;
- }
-
- set
- {
- string oldValue = _SortTitle;
- SetSortTitle(oldValue, ref value);
- if (oldValue != value)
- {
- _SortTitle = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for Language.
- /// </summary>
- protected string _Language;
- /// <summary>
- /// When provided in a partial class, allows value of Language to be changed before setting.
- /// </summary>
- partial void SetLanguage(string oldValue, ref string newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Language to be changed before returning.
- /// </summary>
- partial void GetLanguage(ref string result);
-
- /// <summary>
- /// Required, Min length = 3, Max length = 3
- /// ISO-639-3 3-character language codes.
- /// </summary>
- [Required]
- [MinLength(3)]
- [MaxLength(3)]
- [StringLength(3)]
- public string Language
- {
- get
- {
- string value = _Language;
- GetLanguage(ref value);
- return _Language = value;
- }
-
- set
- {
- string oldValue = _Language;
- SetLanguage(oldValue, ref value);
- if (oldValue != value)
- {
- _Language = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for ReleaseDate.
- /// </summary>
- protected DateTimeOffset? _ReleaseDate;
- /// <summary>
- /// When provided in a partial class, allows value of ReleaseDate to be changed before setting.
- /// </summary>
- partial void SetReleaseDate(DateTimeOffset? oldValue, ref DateTimeOffset? newValue);
- /// <summary>
- /// When provided in a partial class, allows value of ReleaseDate to be changed before returning.
- /// </summary>
- partial void GetReleaseDate(ref DateTimeOffset? result);
-
- public DateTimeOffset? ReleaseDate
- {
- get
- {
- DateTimeOffset? value = _ReleaseDate;
- GetReleaseDate(ref value);
- return _ReleaseDate = value;
- }
-
- set
- {
- DateTimeOffset? oldValue = _ReleaseDate;
- SetReleaseDate(oldValue, ref value);
- if (oldValue != value)
- {
- _ReleaseDate = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for DateAdded.
- /// </summary>
- protected DateTime _DateAdded;
- /// <summary>
- /// When provided in a partial class, allows value of DateAdded to be changed before setting.
- /// </summary>
- partial void SetDateAdded(DateTime oldValue, ref DateTime newValue);
- /// <summary>
- /// When provided in a partial class, allows value of DateAdded to be changed before returning.
- /// </summary>
- partial void GetDateAdded(ref DateTime result);
-
- /// <summary>
- /// Required.
- /// </summary>
- [Required]
- public DateTime DateAdded
- {
- get
- {
- DateTime value = _DateAdded;
- GetDateAdded(ref value);
- return _DateAdded = value;
- }
-
- internal set
- {
- DateTime oldValue = _DateAdded;
- SetDateAdded(oldValue, ref value);
- if (oldValue != value)
- {
- _DateAdded = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for DateModified.
- /// </summary>
- protected DateTime _DateModified;
- /// <summary>
- /// When provided in a partial class, allows value of DateModified to be changed before setting.
- /// </summary>
- partial void SetDateModified(DateTime oldValue, ref DateTime newValue);
- /// <summary>
- /// When provided in a partial class, allows value of DateModified to be changed before returning.
- /// </summary>
- partial void GetDateModified(ref DateTime result);
-
- /// <summary>
- /// Required.
- /// </summary>
- [Required]
- public DateTime DateModified
- {
- get
- {
- DateTime value = _DateModified;
- GetDateModified(ref value);
- return _DateModified = value;
- }
-
- internal set
- {
- DateTime oldValue = _DateModified;
- SetDateModified(oldValue, ref value);
- if (oldValue != value)
- {
- _DateModified = value;
- }
- }
- }
-
- /// <summary>
- /// Required, ConcurrenyToken.
- /// </summary>
- [ConcurrencyCheck]
- [Required]
- public uint RowVersion { get; set; }
-
- public void OnSavingChanges()
- {
- RowVersion++;
- }
-
- /*************************************************************************
- * Navigation properties
- *************************************************************************/
-
- [ForeignKey("PersonRole_PersonRoles_Id")]
- public virtual ICollection<PersonRole> PersonRoles { get; protected set; }
-
- [ForeignKey("PersonRole_PersonRoles_Id")]
- public virtual ICollection<Genre> Genres { get; protected set; }
-
- [ForeignKey("PersonRole_PersonRoles_Id")]
- public virtual ICollection<Artwork> Artwork { get; protected set; }
-
- [ForeignKey("PersonRole_PersonRoles_Id")]
- public virtual ICollection<Rating> Ratings { get; protected set; }
-
- [ForeignKey("PersonRole_PersonRoles_Id")]
- public virtual ICollection<MetadataProviderId> Sources { get; protected set; }
- }
-}
-
diff --git a/Jellyfin.Data/Entities/MetadataProvider.cs b/Jellyfin.Data/Entities/MetadataProvider.cs
deleted file mode 100644
index e93ea97d6..000000000
--- a/Jellyfin.Data/Entities/MetadataProvider.cs
+++ /dev/null
@@ -1,151 +0,0 @@
-using System;
-using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Jellyfin.Data.Entities
-{
- public partial class MetadataProvider
- {
- partial void Init();
-
- /// <summary>
- /// Default constructor. Protected due to required properties, but present because EF needs it.
- /// </summary>
- protected MetadataProvider()
- {
- Init();
- }
-
- /// <summary>
- /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
- /// </summary>
- public static MetadataProvider CreateMetadataProviderUnsafe()
- {
- return new MetadataProvider();
- }
-
- /// <summary>
- /// Public constructor with required data.
- /// </summary>
- /// <param name="name"></param>
- public MetadataProvider(string name)
- {
- if (string.IsNullOrEmpty(name))
- {
- throw new ArgumentNullException(nameof(name));
- }
-
- this.Name = name;
-
- Init();
- }
-
- /// <summary>
- /// Static create function (for use in LINQ queries, etc.)
- /// </summary>
- /// <param name="name"></param>
- public static MetadataProvider Create(string name)
- {
- return new MetadataProvider(name);
- }
-
- /*************************************************************************
- * Properties
- *************************************************************************/
-
- /// <summary>
- /// Backing field for Id.
- /// </summary>
- internal int _Id;
- /// <summary>
- /// When provided in a partial class, allows value of Id to be changed before setting.
- /// </summary>
- partial void SetId(int oldValue, ref int newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Id to be changed before returning.
- /// </summary>
- partial void GetId(ref int result);
-
- /// <summary>
- /// Identity, Indexed, Required.
- /// </summary>
- [Key]
- [Required]
- [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id
- {
- get
- {
- int value = _Id;
- GetId(ref value);
- return _Id = value;
- }
-
- protected set
- {
- int oldValue = _Id;
- SetId(oldValue, ref value);
- if (oldValue != value)
- {
- _Id = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for Name.
- /// </summary>
- protected string _Name;
- /// <summary>
- /// When provided in a partial class, allows value of Name to be changed before setting.
- /// </summary>
- partial void SetName(string oldValue, ref string newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Name to be changed before returning.
- /// </summary>
- partial void GetName(ref string result);
-
- /// <summary>
- /// Required, Max length = 1024
- /// </summary>
- [Required]
- [MaxLength(1024)]
- [StringLength(1024)]
- public string Name
- {
- get
- {
- string value = _Name;
- GetName(ref value);
- return _Name = value;
- }
-
- set
- {
- string oldValue = _Name;
- SetName(oldValue, ref value);
- if (oldValue != value)
- {
- _Name = value;
- }
- }
- }
-
- /// <summary>
- /// Required, ConcurrenyToken.
- /// </summary>
- [ConcurrencyCheck]
- [Required]
- public uint RowVersion { get; set; }
-
- public void OnSavingChanges()
- {
- RowVersion++;
- }
-
- /*************************************************************************
- * Navigation properties
- *************************************************************************/
- }
-}
-
diff --git a/Jellyfin.Data/Entities/MetadataProviderId.cs b/Jellyfin.Data/Entities/MetadataProviderId.cs
deleted file mode 100644
index 68f139436..000000000
--- a/Jellyfin.Data/Entities/MetadataProviderId.cs
+++ /dev/null
@@ -1,199 +0,0 @@
-using System;
-using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Jellyfin.Data.Entities
-{
- public partial class MetadataProviderId
- {
- partial void Init();
-
- /// <summary>
- /// Default constructor. Protected due to required properties, but present because EF needs it.
- /// </summary>
- protected MetadataProviderId()
- {
- // NOTE: This class has one-to-one associations with MetadataProviderId.
- // One-to-one associations are not validated in constructors since this causes a scenario where each one must be constructed before the other.
-
- Init();
- }
-
- /// <summary>
- /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
- /// </summary>
- public static MetadataProviderId CreateMetadataProviderIdUnsafe()
- {
- return new MetadataProviderId();
- }
-
- /// <summary>
- /// Public constructor with required data.
- /// </summary>
- /// <param name="providerid"></param>
- /// <param name="_metadata0"></param>
- /// <param name="_person1"></param>
- /// <param name="_personrole2"></param>
- /// <param name="_ratingsource3"></param>
- public MetadataProviderId(string providerid, Metadata _metadata0, Person _person1, PersonRole _personrole2, RatingSource _ratingsource3)
- {
- // NOTE: This class has one-to-one associations with MetadataProviderId.
- // One-to-one associations are not validated in constructors since this causes a scenario where each one must be constructed before the other.
-
- if (string.IsNullOrEmpty(providerid))
- {
- throw new ArgumentNullException(nameof(providerid));
- }
-
- this.ProviderId = providerid;
-
- if (_metadata0 == null)
- {
- throw new ArgumentNullException(nameof(_metadata0));
- }
-
- _metadata0.Sources.Add(this);
-
- if (_person1 == null)
- {
- throw new ArgumentNullException(nameof(_person1));
- }
-
- _person1.Sources.Add(this);
-
- if (_personrole2 == null)
- {
- throw new ArgumentNullException(nameof(_personrole2));
- }
-
- _personrole2.Sources.Add(this);
-
- if (_ratingsource3 == null)
- {
- throw new ArgumentNullException(nameof(_ratingsource3));
- }
-
- _ratingsource3.Source = this;
-
- Init();
- }
-
- /// <summary>
- /// Static create function (for use in LINQ queries, etc.)
- /// </summary>
- /// <param name="providerid"></param>
- /// <param name="_metadata0"></param>
- /// <param name="_person1"></param>
- /// <param name="_personrole2"></param>
- /// <param name="_ratingsource3"></param>
- public static MetadataProviderId Create(string providerid, Metadata _metadata0, Person _person1, PersonRole _personrole2, RatingSource _ratingsource3)
- {
- return new MetadataProviderId(providerid, _metadata0, _person1, _personrole2, _ratingsource3);
- }
-
- /*************************************************************************
- * Properties
- *************************************************************************/
-
- /// <summary>
- /// Backing field for Id.
- /// </summary>
- internal int _Id;
- /// <summary>
- /// When provided in a partial class, allows value of Id to be changed before setting.
- /// </summary>
- partial void SetId(int oldValue, ref int newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Id to be changed before returning.
- /// </summary>
- partial void GetId(ref int result);
-
- /// <summary>
- /// Identity, Indexed, Required.
- /// </summary>
- [Key]
- [Required]
- [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id
- {
- get
- {
- int value = _Id;
- GetId(ref value);
- return _Id = value;
- }
-
- protected set
- {
- int oldValue = _Id;
- SetId(oldValue, ref value);
- if (oldValue != value)
- {
- _Id = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for ProviderId.
- /// </summary>
- protected string _ProviderId;
- /// <summary>
- /// When provided in a partial class, allows value of ProviderId to be changed before setting.
- /// </summary>
- partial void SetProviderId(string oldValue, ref string newValue);
- /// <summary>
- /// When provided in a partial class, allows value of ProviderId to be changed before returning.
- /// </summary>
- partial void GetProviderId(ref string result);
-
- /// <summary>
- /// Required, Max length = 255
- /// </summary>
- [Required]
- [MaxLength(255)]
- [StringLength(255)]
- public string ProviderId
- {
- get
- {
- string value = _ProviderId;
- GetProviderId(ref value);
- return _ProviderId = value;
- }
-
- set
- {
- string oldValue = _ProviderId;
- SetProviderId(oldValue, ref value);
- if (oldValue != value)
- {
- _ProviderId = value;
- }
- }
- }
-
- /// <summary>
- /// Required, ConcurrenyToken.
- /// </summary>
- [ConcurrencyCheck]
- [Required]
- public uint RowVersion { get; set; }
-
- public void OnSavingChanges()
- {
- RowVersion++;
- }
-
- /*************************************************************************
- * Navigation properties
- *************************************************************************/
-
- /// <summary>
- /// Required.
- /// </summary>
- [ForeignKey("MetadataProvider_Id")]
- public virtual MetadataProvider MetadataProvider { get; set; }
- }
-}
-
diff --git a/Jellyfin.Data/Entities/Movie.cs b/Jellyfin.Data/Entities/Movie.cs
deleted file mode 100644
index 64326ca3a..000000000
--- a/Jellyfin.Data/Entities/Movie.cs
+++ /dev/null
@@ -1,68 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Jellyfin.Data.Entities
-{
- public partial class Movie : LibraryItem
- {
- partial void Init();
-
- /// <summary>
- /// Default constructor. Protected due to required properties, but present because EF needs it.
- /// </summary>
- protected Movie()
- {
- Releases = new HashSet<Release>();
- MovieMetadata = new HashSet<MovieMetadata>();
-
- Init();
- }
-
- /// <summary>
- /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
- /// </summary>
- public static Movie CreateMovieUnsafe()
- {
- return new Movie();
- }
-
- /// <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>
- public Movie(Guid urlid, DateTime dateadded)
- {
- this.UrlId = urlid;
-
- this.Releases = new HashSet<Release>();
- this.MovieMetadata = new HashSet<MovieMetadata>();
-
- Init();
- }
-
- /// <summary>
- /// Static create function (for use in LINQ queries, etc.)
- /// </summary>
- /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param>
- public static Movie Create(Guid urlid, DateTime dateadded)
- {
- return new Movie(urlid, dateadded);
- }
-
- /*************************************************************************
- * Properties
- *************************************************************************/
-
- /*************************************************************************
- * Navigation properties
- *************************************************************************/
-
- [ForeignKey("Release_Releases_Id")]
- public virtual ICollection<Release> Releases { get; protected set; }
-
- [ForeignKey("MovieMetadata_MovieMetadata_Id")]
- public virtual ICollection<MovieMetadata> MovieMetadata { get; protected set; }
- }
-}
-
diff --git a/Jellyfin.Data/Entities/MovieMetadata.cs b/Jellyfin.Data/Entities/MovieMetadata.cs
deleted file mode 100644
index cbcb78e37..000000000
--- a/Jellyfin.Data/Entities/MovieMetadata.cs
+++ /dev/null
@@ -1,238 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Jellyfin.Data.Entities
-{
- public partial class MovieMetadata : Metadata
- {
- partial void Init();
-
- /// <summary>
- /// Default constructor. Protected due to required properties, but present because EF needs it.
- /// </summary>
- protected MovieMetadata()
- {
- Studios = new HashSet<Company>();
-
- Init();
- }
-
- /// <summary>
- /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
- /// </summary>
- public static MovieMetadata CreateMovieMetadataUnsafe()
- {
- return new MovieMetadata();
- }
-
- /// <summary>
- /// Public constructor with required data.
- /// </summary>
- /// <param name="title">The title or name of the object.</param>
- /// <param name="language">ISO-639-3 3-character language codes.</param>
- /// <param name="_movie0"></param>
- public MovieMetadata(string title, string language, DateTime dateadded, DateTime datemodified, Movie _movie0)
- {
- if (string.IsNullOrEmpty(title))
- {
- throw new ArgumentNullException(nameof(title));
- }
-
- this.Title = title;
-
- if (string.IsNullOrEmpty(language))
- {
- throw new ArgumentNullException(nameof(language));
- }
-
- this.Language = language;
-
- if (_movie0 == null)
- {
- throw new ArgumentNullException(nameof(_movie0));
- }
-
- _movie0.MovieMetadata.Add(this);
-
- this.Studios = new HashSet<Company>();
-
- Init();
- }
-
- /// <summary>
- /// Static create function (for use in LINQ queries, etc.)
- /// </summary>
- /// <param name="title">The title or name of the object.</param>
- /// <param name="language">ISO-639-3 3-character language codes.</param>
- /// <param name="_movie0"></param>
- public static MovieMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, Movie _movie0)
- {
- return new MovieMetadata(title, language, dateadded, datemodified, _movie0);
- }
-
- /*************************************************************************
- * Properties
- *************************************************************************/
-
- /// <summary>
- /// Backing field for Outline.
- /// </summary>
- protected string _Outline;
- /// <summary>
- /// When provided in a partial class, allows value of Outline to be changed before setting.
- /// </summary>
- partial void SetOutline(string oldValue, ref string newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Outline to be changed before returning.
- /// </summary>
- partial void GetOutline(ref string result);
-
- /// <summary>
- /// Max length = 1024
- /// </summary>
- [MaxLength(1024)]
- [StringLength(1024)]
- public string Outline
- {
- get
- {
- string value = _Outline;
- GetOutline(ref value);
- return _Outline = value;
- }
-
- set
- {
- string oldValue = _Outline;
- SetOutline(oldValue, ref value);
- if (oldValue != value)
- {
- _Outline = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for Plot.
- /// </summary>
- protected string _Plot;
- /// <summary>
- /// When provided in a partial class, allows value of Plot to be changed before setting.
- /// </summary>
- partial void SetPlot(string oldValue, ref string newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Plot to be changed before returning.
- /// </summary>
- partial void GetPlot(ref string result);
-
- /// <summary>
- /// Max length = 65535
- /// </summary>
- [MaxLength(65535)]
- [StringLength(65535)]
- public string Plot
- {
- get
- {
- string value = _Plot;
- GetPlot(ref value);
- return _Plot = value;
- }
-
- set
- {
- string oldValue = _Plot;
- SetPlot(oldValue, ref value);
- if (oldValue != value)
- {
- _Plot = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for Tagline.
- /// </summary>
- protected string _Tagline;
- /// <summary>
- /// When provided in a partial class, allows value of Tagline to be changed before setting.
- /// </summary>
- partial void SetTagline(string oldValue, ref string newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Tagline to be changed before returning.
- /// </summary>
- partial void GetTagline(ref string result);
-
- /// <summary>
- /// Max length = 1024
- /// </summary>
- [MaxLength(1024)]
- [StringLength(1024)]
- public string Tagline
- {
- get
- {
- string value = _Tagline;
- GetTagline(ref value);
- return _Tagline = value;
- }
-
- set
- {
- string oldValue = _Tagline;
- SetTagline(oldValue, ref value);
- if (oldValue != value)
- {
- _Tagline = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for Country.
- /// </summary>
- protected string _Country;
- /// <summary>
- /// When provided in a partial class, allows value of Country to be changed before setting.
- /// </summary>
- partial void SetCountry(string oldValue, ref string newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Country to be changed before returning.
- /// </summary>
- partial void GetCountry(ref string result);
-
- /// <summary>
- /// Max length = 2
- /// </summary>
- [MaxLength(2)]
- [StringLength(2)]
- public string Country
- {
- get
- {
- string value = _Country;
- GetCountry(ref value);
- return _Country = value;
- }
-
- set
- {
- string oldValue = _Country;
- SetCountry(oldValue, ref value);
- if (oldValue != value)
- {
- _Country = value;
- }
- }
- }
-
- /*************************************************************************
- * Navigation properties
- *************************************************************************/
- [ForeignKey("Company_Studios_Id")]
- public virtual ICollection<Company> Studios { get; protected set; }
- }
-}
-
diff --git a/Jellyfin.Data/Entities/MusicAlbum.cs b/Jellyfin.Data/Entities/MusicAlbum.cs
deleted file mode 100644
index 9afea1fb6..000000000
--- a/Jellyfin.Data/Entities/MusicAlbum.cs
+++ /dev/null
@@ -1,67 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Jellyfin.Data.Entities
-{
- public partial class MusicAlbum : LibraryItem
- {
- partial void Init();
-
- /// <summary>
- /// Default constructor. Protected due to required properties, but present because EF needs it.
- /// </summary>
- protected MusicAlbum()
- {
- MusicAlbumMetadata = new HashSet<MusicAlbumMetadata>();
- Tracks = new HashSet<Track>();
-
- Init();
- }
-
- /// <summary>
- /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
- /// </summary>
- public static MusicAlbum CreateMusicAlbumUnsafe()
- {
- return new MusicAlbum();
- }
-
- /// <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>
- public MusicAlbum(Guid urlid, DateTime dateadded)
- {
- this.UrlId = urlid;
-
- this.MusicAlbumMetadata = new HashSet<MusicAlbumMetadata>();
- this.Tracks = new HashSet<Track>();
-
- Init();
- }
-
- /// <summary>
- /// Static create function (for use in LINQ queries, etc.)
- /// </summary>
- /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param>
- public static MusicAlbum Create(Guid urlid, DateTime dateadded)
- {
- return new MusicAlbum(urlid, dateadded);
- }
-
- /*************************************************************************
- * Properties
- *************************************************************************/
-
- /*************************************************************************
- * Navigation properties
- *************************************************************************/
- [ForeignKey("MusicAlbumMetadata_MusicAlbumMetadata_Id")]
- public virtual ICollection<MusicAlbumMetadata> MusicAlbumMetadata { get; protected set; }
-
- [ForeignKey("Track_Tracks_Id")]
- public virtual ICollection<Track> Tracks { get; protected set; }
- }
-}
-
diff --git a/Jellyfin.Data/Entities/MusicAlbumMetadata.cs b/Jellyfin.Data/Entities/MusicAlbumMetadata.cs
deleted file mode 100644
index bfcbebbe8..000000000
--- a/Jellyfin.Data/Entities/MusicAlbumMetadata.cs
+++ /dev/null
@@ -1,201 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Jellyfin.Data.Entities
-{
- public partial class MusicAlbumMetadata : Metadata
- {
- partial void Init();
-
- /// <summary>
- /// Default constructor. Protected due to required properties, but present because EF needs it.
- /// </summary>
- protected MusicAlbumMetadata()
- {
- Labels = new HashSet<Company>();
-
- Init();
- }
-
- /// <summary>
- /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
- /// </summary>
- public static MusicAlbumMetadata CreateMusicAlbumMetadataUnsafe()
- {
- return new MusicAlbumMetadata();
- }
-
- /// <summary>
- /// Public constructor with required data.
- /// </summary>
- /// <param name="title">The title or name of the object.</param>
- /// <param name="language">ISO-639-3 3-character language codes.</param>
- /// <param name="_musicalbum0"></param>
- public MusicAlbumMetadata(string title, string language, DateTime dateadded, DateTime datemodified, MusicAlbum _musicalbum0)
- {
- if (string.IsNullOrEmpty(title))
- {
- throw new ArgumentNullException(nameof(title));
- }
-
- this.Title = title;
-
- if (string.IsNullOrEmpty(language))
- {
- throw new ArgumentNullException(nameof(language));
- }
-
- this.Language = language;
-
- if (_musicalbum0 == null)
- {
- throw new ArgumentNullException(nameof(_musicalbum0));
- }
-
- _musicalbum0.MusicAlbumMetadata.Add(this);
-
- this.Labels = new HashSet<Company>();
-
- Init();
- }
-
- /// <summary>
- /// Static create function (for use in LINQ queries, etc.)
- /// </summary>
- /// <param name="title">The title or name of the object.</param>
- /// <param name="language">ISO-639-3 3-character language codes.</param>
- /// <param name="_musicalbum0"></param>
- public static MusicAlbumMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, MusicAlbum _musicalbum0)
- {
- return new MusicAlbumMetadata(title, language, dateadded, datemodified, _musicalbum0);
- }
-
- /*************************************************************************
- * Properties
- *************************************************************************/
-
- /// <summary>
- /// Backing field for Barcode.
- /// </summary>
- protected string _Barcode;
- /// <summary>
- /// When provided in a partial class, allows value of Barcode to be changed before setting.
- /// </summary>
- partial void SetBarcode(string oldValue, ref string newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Barcode to be changed before returning.
- /// </summary>
- partial void GetBarcode(ref string result);
-
- /// <summary>
- /// Max length = 255
- /// </summary>
- [MaxLength(255)]
- [StringLength(255)]
- public string Barcode
- {
- get
- {
- string value = _Barcode;
- GetBarcode(ref value);
- return _Barcode = value;
- }
-
- set
- {
- string oldValue = _Barcode;
- SetBarcode(oldValue, ref value);
- if (oldValue != value)
- {
- _Barcode = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for LabelNumber.
- /// </summary>
- protected string _LabelNumber;
- /// <summary>
- /// When provided in a partial class, allows value of LabelNumber to be changed before setting.
- /// </summary>
- partial void SetLabelNumber(string oldValue, ref string newValue);
- /// <summary>
- /// When provided in a partial class, allows value of LabelNumber to be changed before returning.
- /// </summary>
- partial void GetLabelNumber(ref string result);
-
- /// <summary>
- /// Max length = 255
- /// </summary>
- [MaxLength(255)]
- [StringLength(255)]
- public string LabelNumber
- {
- get
- {
- string value = _LabelNumber;
- GetLabelNumber(ref value);
- return _LabelNumber = value;
- }
-
- set
- {
- string oldValue = _LabelNumber;
- SetLabelNumber(oldValue, ref value);
- if (oldValue != value)
- {
- _LabelNumber = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for Country.
- /// </summary>
- protected string _Country;
- /// <summary>
- /// When provided in a partial class, allows value of Country to be changed before setting.
- /// </summary>
- partial void SetCountry(string oldValue, ref string newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Country to be changed before returning.
- /// </summary>
- partial void GetCountry(ref string result);
-
- /// <summary>
- /// Max length = 2
- /// </summary>
- [MaxLength(2)]
- [StringLength(2)]
- public string Country
- {
- get
- {
- string value = _Country;
- GetCountry(ref value);
- return _Country = value;
- }
-
- set
- {
- string oldValue = _Country;
- SetCountry(oldValue, ref value);
- if (oldValue != value)
- {
- _Country = value;
- }
- }
- }
-
- /*************************************************************************
- * Navigation properties
- *************************************************************************/
-
- [ForeignKey("Company_Labels_Id")]
- public virtual ICollection<Company> Labels { get; protected set; }
- }
-}
-
diff --git a/Jellyfin.Data/Entities/Permission.cs b/Jellyfin.Data/Entities/Permission.cs
index b675e911d..c0f67f836 100644
--- a/Jellyfin.Data/Entities/Permission.cs
+++ b/Jellyfin.Data/Entities/Permission.cs
@@ -1,13 +1,16 @@
+#pragma warning disable CS1591
+
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Jellyfin.Data.Enums;
+using Jellyfin.Data.Interfaces;
namespace Jellyfin.Data.Entities
{
/// <summary>
/// An entity representing whether the associated user has a specific permission.
/// </summary>
- public partial class Permission : ISavingChanges
+ public partial class Permission : IHasConcurrencyToken
{
/// <summary>
/// Initializes a new instance of the <see cref="Permission"/> class.
diff --git a/Jellyfin.Data/Entities/Person.cs b/Jellyfin.Data/Entities/Person.cs
deleted file mode 100644
index b6d91ea86..000000000
--- a/Jellyfin.Data/Entities/Person.cs
+++ /dev/null
@@ -1,311 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Jellyfin.Data.Entities
-{
- public partial class Person
- {
- partial void Init();
-
- /// <summary>
- /// Default constructor. Protected due to required properties, but present because EF needs it.
- /// </summary>
- protected Person()
- {
- Sources = new HashSet<MetadataProviderId>();
-
- Init();
- }
-
- /// <summary>
- /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
- /// </summary>
- public static Person CreatePersonUnsafe()
- {
- return new Person();
- }
-
- /// <summary>
- /// Public constructor with required data.
- /// </summary>
- /// <param name="urlid"></param>
- /// <param name="name"></param>
- public Person(Guid urlid, string name, DateTime dateadded, DateTime datemodified)
- {
- this.UrlId = urlid;
-
- if (string.IsNullOrEmpty(name))
- {
- throw new ArgumentNullException(nameof(name));
- }
-
- this.Name = name;
-
- this.Sources = new HashSet<MetadataProviderId>();
-
- Init();
- }
-
- /// <summary>
- /// Static create function (for use in LINQ queries, etc.)
- /// </summary>
- /// <param name="urlid"></param>
- /// <param name="name"></param>
- public static Person Create(Guid urlid, string name, DateTime dateadded, DateTime datemodified)
- {
- return new Person(urlid, name, dateadded, datemodified);
- }
-
- /*************************************************************************
- * Properties
- *************************************************************************/
-
- /// <summary>
- /// Backing field for Id.
- /// </summary>
- internal int _Id;
- /// <summary>
- /// When provided in a partial class, allows value of Id to be changed before setting.
- /// </summary>
- partial void SetId(int oldValue, ref int newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Id to be changed before returning.
- /// </summary>
- partial void GetId(ref int result);
-
- /// <summary>
- /// Identity, Indexed, Required.
- /// </summary>
- [Key]
- [Required]
- [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id
- {
- get
- {
- int value = _Id;
- GetId(ref value);
- return _Id = value;
- }
-
- protected set
- {
- int oldValue = _Id;
- SetId(oldValue, ref value);
- if (oldValue != value)
- {
- _Id = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for UrlId.
- /// </summary>
- protected Guid _UrlId;
- /// <summary>
- /// When provided in a partial class, allows value of UrlId to be changed before setting.
- /// </summary>
- partial void SetUrlId(Guid oldValue, ref Guid newValue);
- /// <summary>
- /// When provided in a partial class, allows value of UrlId to be changed before returning.
- /// </summary>
- partial void GetUrlId(ref Guid result);
-
- /// <summary>
- /// Required.
- /// </summary>
- [Required]
- public Guid UrlId
- {
- get
- {
- Guid value = _UrlId;
- GetUrlId(ref value);
- return _UrlId = value;
- }
-
- set
- {
- Guid oldValue = _UrlId;
- SetUrlId(oldValue, ref value);
- if (oldValue != value)
- {
- _UrlId = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for Name.
- /// </summary>
- protected string _Name;
- /// <summary>
- /// When provided in a partial class, allows value of Name to be changed before setting.
- /// </summary>
- partial void SetName(string oldValue, ref string newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Name to be changed before returning.
- /// </summary>
- partial void GetName(ref string result);
-
- /// <summary>
- /// Required, Max length = 1024
- /// </summary>
- [Required]
- [MaxLength(1024)]
- [StringLength(1024)]
- public string Name
- {
- get
- {
- string value = _Name;
- GetName(ref value);
- return _Name = value;
- }
-
- set
- {
- string oldValue = _Name;
- SetName(oldValue, ref value);
- if (oldValue != value)
- {
- _Name = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for SourceId.
- /// </summary>
- protected string _SourceId;
- /// <summary>
- /// When provided in a partial class, allows value of SourceId to be changed before setting.
- /// </summary>
- partial void SetSourceId(string oldValue, ref string newValue);
- /// <summary>
- /// When provided in a partial class, allows value of SourceId to be changed before returning.
- /// </summary>
- partial void GetSourceId(ref string result);
-
- /// <summary>
- /// Max length = 255
- /// </summary>
- [MaxLength(255)]
- [StringLength(255)]
- public string SourceId
- {
- get
- {
- string value = _SourceId;
- GetSourceId(ref value);
- return _SourceId = value;
- }
-
- set
- {
- string oldValue = _SourceId;
- SetSourceId(oldValue, ref value);
- if (oldValue != value)
- {
- _SourceId = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for DateAdded.
- /// </summary>
- protected DateTime _DateAdded;
- /// <summary>
- /// When provided in a partial class, allows value of DateAdded to be changed before setting.
- /// </summary>
- partial void SetDateAdded(DateTime oldValue, ref DateTime newValue);
- /// <summary>
- /// When provided in a partial class, allows value of DateAdded to be changed before returning.
- /// </summary>
- partial void GetDateAdded(ref DateTime result);
-
- /// <summary>
- /// Required.
- /// </summary>
- [Required]
- public DateTime DateAdded
- {
- get
- {
- DateTime value = _DateAdded;
- GetDateAdded(ref value);
- return _DateAdded = value;
- }
-
- internal set
- {
- DateTime oldValue = _DateAdded;
- SetDateAdded(oldValue, ref value);
- if (oldValue != value)
- {
- _DateAdded = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for DateModified.
- /// </summary>
- protected DateTime _DateModified;
- /// <summary>
- /// When provided in a partial class, allows value of DateModified to be changed before setting.
- /// </summary>
- partial void SetDateModified(DateTime oldValue, ref DateTime newValue);
- /// <summary>
- /// When provided in a partial class, allows value of DateModified to be changed before returning.
- /// </summary>
- partial void GetDateModified(ref DateTime result);
-
- /// <summary>
- /// Required.
- /// </summary>
- [Required]
- public DateTime DateModified
- {
- get
- {
- DateTime value = _DateModified;
- GetDateModified(ref value);
- return _DateModified = value;
- }
-
- internal set
- {
- DateTime oldValue = _DateModified;
- SetDateModified(oldValue, ref value);
- if (oldValue != value)
- {
- _DateModified = value;
- }
- }
- }
-
- /// <summary>
- /// Required, ConcurrenyToken.
- /// </summary>
- [ConcurrencyCheck]
- [Required]
- public uint RowVersion { get; set; }
-
- public void OnSavingChanges()
- {
- RowVersion++;
- }
-
- /*************************************************************************
- * Navigation properties
- *************************************************************************/
- [ForeignKey("MetadataProviderId_Sources_Id")]
- public virtual ICollection<MetadataProviderId> Sources { get; protected set; }
- }
-}
-
diff --git a/Jellyfin.Data/Entities/PersonRole.cs b/Jellyfin.Data/Entities/PersonRole.cs
deleted file mode 100644
index 2dd5f116f..000000000
--- a/Jellyfin.Data/Entities/PersonRole.cs
+++ /dev/null
@@ -1,215 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Jellyfin.Data.Entities
-{
- public partial class PersonRole
- {
- partial void Init();
-
- /// <summary>
- /// Default constructor. Protected due to required properties, but present because EF needs it.
- /// </summary>
- protected PersonRole()
- {
- // NOTE: This class has one-to-one associations with PersonRole.
- // One-to-one associations are not validated in constructors since this causes a scenario where each one must be constructed before the other.
-
- Sources = new HashSet<MetadataProviderId>();
-
- Init();
- }
-
- /// <summary>
- /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
- /// </summary>
- public static PersonRole CreatePersonRoleUnsafe()
- {
- return new PersonRole();
- }
-
- /// <summary>
- /// Public constructor with required data.
- /// </summary>
- /// <param name="type"></param>
- /// <param name="_metadata0"></param>
- public PersonRole(Enums.PersonRoleType type, Metadata _metadata0)
- {
- // NOTE: This class has one-to-one associations with PersonRole.
- // One-to-one associations are not validated in constructors since this causes a scenario where each one must be constructed before the other.
-
- this.Type = type;
-
- if (_metadata0 == null)
- {
- throw new ArgumentNullException(nameof(_metadata0));
- }
-
- _metadata0.PersonRoles.Add(this);
-
- this.Sources = new HashSet<MetadataProviderId>();
-
- Init();
- }
-
- /// <summary>
- /// Static create function (for use in LINQ queries, etc.)
- /// </summary>
- /// <param name="type"></param>
- /// <param name="_metadata0"></param>
- public static PersonRole Create(Enums.PersonRoleType type, Metadata _metadata0)
- {
- return new PersonRole(type, _metadata0);
- }
-
- /*************************************************************************
- * Properties
- *************************************************************************/
-
- /// <summary>
- /// Backing field for Id.
- /// </summary>
- internal int _Id;
- /// <summary>
- /// When provided in a partial class, allows value of Id to be changed before setting.
- /// </summary>
- partial void SetId(int oldValue, ref int newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Id to be changed before returning.
- /// </summary>
- partial void GetId(ref int result);
-
- /// <summary>
- /// Identity, Indexed, Required.
- /// </summary>
- [Key]
- [Required]
- [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id
- {
- get
- {
- int value = _Id;
- GetId(ref value);
- return _Id = value;
- }
-
- protected set
- {
- int oldValue = _Id;
- SetId(oldValue, ref value);
- if (oldValue != value)
- {
- _Id = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for Role.
- /// </summary>
- protected string _Role;
- /// <summary>
- /// When provided in a partial class, allows value of Role to be changed before setting.
- /// </summary>
- partial void SetRole(string oldValue, ref string newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Role to be changed before returning.
- /// </summary>
- partial void GetRole(ref string result);
-
- /// <summary>
- /// Max length = 1024
- /// </summary>
- [MaxLength(1024)]
- [StringLength(1024)]
- public string Role
- {
- get
- {
- string value = _Role;
- GetRole(ref value);
- return _Role = value;
- }
-
- set
- {
- string oldValue = _Role;
- SetRole(oldValue, ref value);
- if (oldValue != value)
- {
- _Role = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for Type.
- /// </summary>
- protected Enums.PersonRoleType _Type;
- /// <summary>
- /// When provided in a partial class, allows value of Type to be changed before setting.
- /// </summary>
- partial void SetType(Enums.PersonRoleType oldValue, ref Enums.PersonRoleType newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Type to be changed before returning.
- /// </summary>
- partial void GetType(ref Enums.PersonRoleType result);
-
- /// <summary>
- /// Required.
- /// </summary>
- [Required]
- public Enums.PersonRoleType Type
- {
- get
- {
- Enums.PersonRoleType value = _Type;
- GetType(ref value);
- return _Type = value;
- }
-
- set
- {
- Enums.PersonRoleType oldValue = _Type;
- SetType(oldValue, ref value);
- if (oldValue != value)
- {
- _Type = value;
- }
- }
- }
-
- /// <summary>
- /// Required, ConcurrenyToken.
- /// </summary>
- [ConcurrencyCheck]
- [Required]
- public uint RowVersion { get; set; }
-
- public void OnSavingChanges()
- {
- RowVersion++;
- }
-
- /*************************************************************************
- * Navigation properties
- *************************************************************************/
-
- /// <summary>
- /// Required.
- /// </summary>
- [ForeignKey("Person_Id")]
-
- public virtual Person Person { get; set; }
-
- [ForeignKey("Artwork_Artwork_Id")]
- public virtual Artwork Artwork { get; set; }
-
- [ForeignKey("MetadataProviderId_Sources_Id")]
- public virtual ICollection<MetadataProviderId> Sources { get; protected set; }
- }
-}
-
diff --git a/Jellyfin.Data/Entities/Photo.cs b/Jellyfin.Data/Entities/Photo.cs
deleted file mode 100644
index 9da55fe43..000000000
--- a/Jellyfin.Data/Entities/Photo.cs
+++ /dev/null
@@ -1,67 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Jellyfin.Data.Entities
-{
- public partial class Photo : LibraryItem
- {
- partial void Init();
-
- /// <summary>
- /// Default constructor. Protected due to required properties, but present because EF needs it.
- /// </summary>
- protected Photo()
- {
- PhotoMetadata = new HashSet<PhotoMetadata>();
- Releases = new HashSet<Release>();
-
- Init();
- }
-
- /// <summary>
- /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
- /// </summary>
- public static Photo CreatePhotoUnsafe()
- {
- return new Photo();
- }
-
- /// <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>
- public Photo(Guid urlid, DateTime dateadded)
- {
- this.UrlId = urlid;
-
- this.PhotoMetadata = new HashSet<PhotoMetadata>();
- this.Releases = new HashSet<Release>();
-
- Init();
- }
-
- /// <summary>
- /// Static create function (for use in LINQ queries, etc.)
- /// </summary>
- /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param>
- public static Photo Create(Guid urlid, DateTime dateadded)
- {
- return new Photo(urlid, dateadded);
- }
-
- /*************************************************************************
- * Properties
- *************************************************************************/
-
- /*************************************************************************
- * Navigation properties
- *************************************************************************/
- [ForeignKey("PhotoMetadata_PhotoMetadata_Id")]
- public virtual ICollection<PhotoMetadata> PhotoMetadata { get; protected set; }
-
- [ForeignKey("Release_Releases_Id")]
- public virtual ICollection<Release> Releases { get; protected set; }
- }
-}
-
diff --git a/Jellyfin.Data/Entities/PhotoMetadata.cs b/Jellyfin.Data/Entities/PhotoMetadata.cs
deleted file mode 100644
index b5aec7229..000000000
--- a/Jellyfin.Data/Entities/PhotoMetadata.cs
+++ /dev/null
@@ -1,78 +0,0 @@
-using System;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Jellyfin.Data.Entities
-{
- public partial class PhotoMetadata : Metadata
- {
- partial void Init();
-
- /// <summary>
- /// Default constructor. Protected due to required properties, but present because EF needs it.
- /// </summary>
- protected PhotoMetadata()
- {
- Init();
- }
-
- /// <summary>
- /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
- /// </summary>
- public static PhotoMetadata CreatePhotoMetadataUnsafe()
- {
- return new PhotoMetadata();
- }
-
- /// <summary>
- /// Public constructor with required data.
- /// </summary>
- /// <param name="title">The title or name of the object.</param>
- /// <param name="language">ISO-639-3 3-character language codes.</param>
- /// <param name="_photo0"></param>
- public PhotoMetadata(string title, string language, DateTime dateadded, DateTime datemodified, Photo _photo0)
- {
- if (string.IsNullOrEmpty(title))
- {
- throw new ArgumentNullException(nameof(title));
- }
-
- this.Title = title;
-
- if (string.IsNullOrEmpty(language))
- {
- throw new ArgumentNullException(nameof(language));
- }
-
- this.Language = language;
-
- if (_photo0 == null)
- {
- throw new ArgumentNullException(nameof(_photo0));
- }
-
- _photo0.PhotoMetadata.Add(this);
-
- Init();
- }
-
- /// <summary>
- /// Static create function (for use in LINQ queries, etc.)
- /// </summary>
- /// <param name="title">The title or name of the object.</param>
- /// <param name="language">ISO-639-3 3-character language codes.</param>
- /// <param name="_photo0"></param>
- public static PhotoMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, Photo _photo0)
- {
- return new PhotoMetadata(title, language, dateadded, datemodified, _photo0);
- }
-
- /*************************************************************************
- * Properties
- *************************************************************************/
-
- /*************************************************************************
- * Navigation properties
- *************************************************************************/
- }
-}
-
diff --git a/Jellyfin.Data/Entities/Preference.cs b/Jellyfin.Data/Entities/Preference.cs
index 0ca9d7eff..1797f0a40 100644
--- a/Jellyfin.Data/Entities/Preference.cs
+++ b/Jellyfin.Data/Entities/Preference.cs
@@ -2,13 +2,14 @@ using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Jellyfin.Data.Enums;
+using Jellyfin.Data.Interfaces;
namespace Jellyfin.Data.Entities
{
/// <summary>
/// An entity representing a preference attached to a user or group.
/// </summary>
- public class Preference : ISavingChanges
+ public class Preference : IHasConcurrencyToken
{
/// <summary>
/// Initializes a new instance of the <see cref="Preference"/> class.
diff --git a/Jellyfin.Data/Entities/ProviderMapping.cs b/Jellyfin.Data/Entities/ProviderMapping.cs
index c53e3bf40..44ebfba76 100644
--- a/Jellyfin.Data/Entities/ProviderMapping.cs
+++ b/Jellyfin.Data/Entities/ProviderMapping.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
diff --git a/Jellyfin.Data/Entities/Rating.cs b/Jellyfin.Data/Entities/Rating.cs
deleted file mode 100644
index 49a0d502d..000000000
--- a/Jellyfin.Data/Entities/Rating.cs
+++ /dev/null
@@ -1,192 +0,0 @@
-using System;
-using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Jellyfin.Data.Entities
-{
- public partial class Rating
- {
- partial void Init();
-
- /// <summary>
- /// Default constructor. Protected due to required properties, but present because EF needs it.
- /// </summary>
- protected Rating()
- {
- Init();
- }
-
- /// <summary>
- /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
- /// </summary>
- public static Rating CreateRatingUnsafe()
- {
- return new Rating();
- }
-
- /// <summary>
- /// Public constructor with required data.
- /// </summary>
- /// <param name="value"></param>
- /// <param name="_metadata0"></param>
- public Rating(double value, Metadata _metadata0)
- {
- this.Value = value;
-
- if (_metadata0 == null)
- {
- throw new ArgumentNullException(nameof(_metadata0));
- }
-
- _metadata0.Ratings.Add(this);
-
- Init();
- }
-
- /// <summary>
- /// Static create function (for use in LINQ queries, etc.)
- /// </summary>
- /// <param name="value"></param>
- /// <param name="_metadata0"></param>
- public static Rating Create(double value, Metadata _metadata0)
- {
- return new Rating(value, _metadata0);
- }
-
- /*************************************************************************
- * Properties
- *************************************************************************/
-
- /// <summary>
- /// Backing field for Id.
- /// </summary>
- internal int _Id;
- /// <summary>
- /// When provided in a partial class, allows value of Id to be changed before setting.
- /// </summary>
- partial void SetId(int oldValue, ref int newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Id to be changed before returning.
- /// </summary>
- partial void GetId(ref int result);
-
- /// <summary>
- /// Identity, Indexed, Required.
- /// </summary>
- [Key]
- [Required]
- [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id
- {
- get
- {
- int value = _Id;
- GetId(ref value);
- return _Id = value;
- }
-
- protected set
- {
- int oldValue = _Id;
- SetId(oldValue, ref value);
- if (oldValue != value)
- {
- _Id = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for Value.
- /// </summary>
- protected double _Value;
- /// <summary>
- /// When provided in a partial class, allows value of Value to be changed before setting.
- /// </summary>
- partial void SetValue(double oldValue, ref double newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Value to be changed before returning.
- /// </summary>
- partial void GetValue(ref double result);
-
- /// <summary>
- /// Required.
- /// </summary>
- [Required]
- public double Value
- {
- get
- {
- double value = _Value;
- GetValue(ref value);
- return _Value = value;
- }
-
- set
- {
- double oldValue = _Value;
- SetValue(oldValue, ref value);
- if (oldValue != value)
- {
- _Value = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for Votes.
- /// </summary>
- protected int? _Votes;
- /// <summary>
- /// When provided in a partial class, allows value of Votes to be changed before setting.
- /// </summary>
- partial void SetVotes(int? oldValue, ref int? newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Votes to be changed before returning.
- /// </summary>
- partial void GetVotes(ref int? result);
-
- public int? Votes
- {
- get
- {
- int? value = _Votes;
- GetVotes(ref value);
- return _Votes = value;
- }
-
- set
- {
- int? oldValue = _Votes;
- SetVotes(oldValue, ref value);
- if (oldValue != value)
- {
- _Votes = value;
- }
- }
- }
-
- /// <summary>
- /// Required, ConcurrenyToken.
- /// </summary>
- [ConcurrencyCheck]
- [Required]
- public uint RowVersion { get; set; }
-
- public void OnSavingChanges()
- {
- RowVersion++;
- }
-
- /*************************************************************************
- * Navigation properties
- *************************************************************************/
-
- /// <summary>
- /// If this is NULL it&apos;s the internal user rating.
- /// </summary>
- [ForeignKey("RatingSource_RatingType_Id")]
- public virtual RatingSource RatingType { get; set; }
- }
-}
-
diff --git a/Jellyfin.Data/Entities/RatingSource.cs b/Jellyfin.Data/Entities/RatingSource.cs
deleted file mode 100644
index b62d8b444..000000000
--- a/Jellyfin.Data/Entities/RatingSource.cs
+++ /dev/null
@@ -1,237 +0,0 @@
-using System;
-using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Jellyfin.Data.Entities
-{
- /// <summary>
- /// This is the entity to store review ratings, not age ratings.
- /// </summary>
- public partial class RatingSource
- {
- partial void Init();
-
- /// <summary>
- /// Default constructor. Protected due to required properties, but present because EF needs it.
- /// </summary>
- protected RatingSource()
- {
- Init();
- }
-
- /// <summary>
- /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
- /// </summary>
- public static RatingSource CreateRatingSourceUnsafe()
- {
- return new RatingSource();
- }
-
- /// <summary>
- /// Public constructor with required data.
- /// </summary>
- /// <param name="maximumvalue"></param>
- /// <param name="minimumvalue"></param>
- /// <param name="_rating0"></param>
- public RatingSource(double maximumvalue, double minimumvalue, Rating _rating0)
- {
- this.MaximumValue = maximumvalue;
-
- this.MinimumValue = minimumvalue;
-
- if (_rating0 == null)
- {
- throw new ArgumentNullException(nameof(_rating0));
- }
-
- _rating0.RatingType = this;
-
- Init();
- }
-
- /// <summary>
- /// Static create function (for use in LINQ queries, etc.)
- /// </summary>
- /// <param name="maximumvalue"></param>
- /// <param name="minimumvalue"></param>
- /// <param name="_rating0"></param>
- public static RatingSource Create(double maximumvalue, double minimumvalue, Rating _rating0)
- {
- return new RatingSource(maximumvalue, minimumvalue, _rating0);
- }
-
- /*************************************************************************
- * Properties
- *************************************************************************/
-
- /// <summary>
- /// Backing field for Id.
- /// </summary>
- internal int _Id;
- /// <summary>
- /// When provided in a partial class, allows value of Id to be changed before setting.
- /// </summary>
- partial void SetId(int oldValue, ref int newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Id to be changed before returning.
- /// </summary>
- partial void GetId(ref int result);
-
- /// <summary>
- /// Identity, Indexed, Required.
- /// </summary>
- [Key]
- [Required]
- [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id
- {
- get
- {
- int value = _Id;
- GetId(ref value);
- return _Id = value;
- }
-
- protected set
- {
- int oldValue = _Id;
- SetId(oldValue, ref value);
- if (oldValue != value)
- {
- _Id = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for Name.
- /// </summary>
- protected string _Name;
- /// <summary>
- /// When provided in a partial class, allows value of Name to be changed before setting.
- /// </summary>
- partial void SetName(string oldValue, ref string newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Name to be changed before returning.
- /// </summary>
- partial void GetName(ref string result);
-
- /// <summary>
- /// Max length = 1024
- /// </summary>
- [MaxLength(1024)]
- [StringLength(1024)]
- public string Name
- {
- get
- {
- string value = _Name;
- GetName(ref value);
- return _Name = value;
- }
-
- set
- {
- string oldValue = _Name;
- SetName(oldValue, ref value);
- if (oldValue != value)
- {
- _Name = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for MaximumValue.
- /// </summary>
- protected double _MaximumValue;
- /// <summary>
- /// When provided in a partial class, allows value of MaximumValue to be changed before setting.
- /// </summary>
- partial void SetMaximumValue(double oldValue, ref double newValue);
- /// <summary>
- /// When provided in a partial class, allows value of MaximumValue to be changed before returning.
- /// </summary>
- partial void GetMaximumValue(ref double result);
-
- /// <summary>
- /// Required.
- /// </summary>
- [Required]
- public double MaximumValue
- {
- get
- {
- double value = _MaximumValue;
- GetMaximumValue(ref value);
- return _MaximumValue = value;
- }
-
- set
- {
- double oldValue = _MaximumValue;
- SetMaximumValue(oldValue, ref value);
- if (oldValue != value)
- {
- _MaximumValue = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for MinimumValue.
- /// </summary>
- protected double _MinimumValue;
- /// <summary>
- /// When provided in a partial class, allows value of MinimumValue to be changed before setting.
- /// </summary>
- partial void SetMinimumValue(double oldValue, ref double newValue);
- /// <summary>
- /// When provided in a partial class, allows value of MinimumValue to be changed before returning.
- /// </summary>
- partial void GetMinimumValue(ref double result);
-
- /// <summary>
- /// Required.
- /// </summary>
- [Required]
- public double MinimumValue
- {
- get
- {
- double value = _MinimumValue;
- GetMinimumValue(ref value);
- return _MinimumValue = value;
- }
-
- set
- {
- double oldValue = _MinimumValue;
- SetMinimumValue(oldValue, ref value);
- if (oldValue != value)
- {
- _MinimumValue = value;
- }
- }
- }
-
- /// <summary>
- /// Required, ConcurrenyToken.
- /// </summary>
- [ConcurrencyCheck]
- [Required]
- public uint RowVersion { get; set; }
-
- public void OnSavingChanges()
- {
- RowVersion++;
- }
-
- /*************************************************************************
- * Navigation properties
- *************************************************************************/
- [ForeignKey("MetadataProviderId_Source_Id")]
- public virtual MetadataProviderId Source { get; set; }
- }
-}
-
diff --git a/Jellyfin.Data/Entities/Release.cs b/Jellyfin.Data/Entities/Release.cs
deleted file mode 100644
index 1e9faa5a1..000000000
--- a/Jellyfin.Data/Entities/Release.cs
+++ /dev/null
@@ -1,217 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Jellyfin.Data.Entities
-{
- public partial class Release
- {
- partial void Init();
-
- /// <summary>
- /// Default constructor. Protected due to required properties, but present because EF needs it.
- /// </summary>
- protected Release()
- {
- MediaFiles = new HashSet<MediaFile>();
- Chapters = new HashSet<Chapter>();
-
- Init();
- }
-
- /// <summary>
- /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
- /// </summary>
- public static Release CreateReleaseUnsafe()
- {
- return new Release();
- }
-
- /// <summary>
- /// Public constructor with required data.
- /// </summary>
- /// <param name="name"></param>
- /// <param name="_movie0"></param>
- /// <param name="_episode1"></param>
- /// <param name="_track2"></param>
- /// <param name="_customitem3"></param>
- /// <param name="_book4"></param>
- /// <param name="_photo5"></param>
- public Release(string name, Movie _movie0, Episode _episode1, Track _track2, CustomItem _customitem3, Book _book4, Photo _photo5)
- {
- if (string.IsNullOrEmpty(name))
- {
- throw new ArgumentNullException(nameof(name));
- }
-
- this.Name = name;
-
- if (_movie0 == null)
- {
- throw new ArgumentNullException(nameof(_movie0));
- }
-
- _movie0.Releases.Add(this);
-
- if (_episode1 == null)
- {
- throw new ArgumentNullException(nameof(_episode1));
- }
-
- _episode1.Releases.Add(this);
-
- if (_track2 == null)
- {
- throw new ArgumentNullException(nameof(_track2));
- }
-
- _track2.Releases.Add(this);
-
- if (_customitem3 == null)
- {
- throw new ArgumentNullException(nameof(_customitem3));
- }
-
- _customitem3.Releases.Add(this);
-
- if (_book4 == null)
- {
- throw new ArgumentNullException(nameof(_book4));
- }
-
- _book4.Releases.Add(this);
-
- if (_photo5 == null)
- {
- throw new ArgumentNullException(nameof(_photo5));
- }
-
- _photo5.Releases.Add(this);
-
- this.MediaFiles = new HashSet<MediaFile>();
- this.Chapters = new HashSet<Chapter>();
-
- Init();
- }
-
- /// <summary>
- /// Static create function (for use in LINQ queries, etc.)
- /// </summary>
- /// <param name="name"></param>
- /// <param name="_movie0"></param>
- /// <param name="_episode1"></param>
- /// <param name="_track2"></param>
- /// <param name="_customitem3"></param>
- /// <param name="_book4"></param>
- /// <param name="_photo5"></param>
- public static Release Create(string name, Movie _movie0, Episode _episode1, Track _track2, CustomItem _customitem3, Book _book4, Photo _photo5)
- {
- return new Release(name, _movie0, _episode1, _track2, _customitem3, _book4, _photo5);
- }
-
- /*************************************************************************
- * Properties
- *************************************************************************/
-
- /// <summary>
- /// Backing field for Id.
- /// </summary>
- internal int _Id;
- /// <summary>
- /// When provided in a partial class, allows value of Id to be changed before setting.
- /// </summary>
- partial void SetId(int oldValue, ref int newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Id to be changed before returning.
- /// </summary>
- partial void GetId(ref int result);
-
- /// <summary>
- /// Identity, Indexed, Required.
- /// </summary>
- [Key]
- [Required]
- [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id
- {
- get
- {
- int value = _Id;
- GetId(ref value);
- return _Id = value;
- }
-
- protected set
- {
- int oldValue = _Id;
- SetId(oldValue, ref value);
- if (oldValue != value)
- {
- _Id = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for Name.
- /// </summary>
- protected string _Name;
- /// <summary>
- /// When provided in a partial class, allows value of Name to be changed before setting.
- /// </summary>
- partial void SetName(string oldValue, ref string newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Name to be changed before returning.
- /// </summary>
- partial void GetName(ref string result);
-
- /// <summary>
- /// Required, Max length = 1024
- /// </summary>
- [Required]
- [MaxLength(1024)]
- [StringLength(1024)]
- public string Name
- {
- get
- {
- string value = _Name;
- GetName(ref value);
- return _Name = value;
- }
-
- set
- {
- string oldValue = _Name;
- SetName(oldValue, ref value);
- if (oldValue != value)
- {
- _Name = value;
- }
- }
- }
-
- /// <summary>
- /// Required, ConcurrenyToken.
- /// </summary>
- [ConcurrencyCheck]
- [Required]
- public uint RowVersion { get; set; }
-
- public void OnSavingChanges()
- {
- RowVersion++;
- }
-
- /*************************************************************************
- * Navigation properties
- *************************************************************************/
- [ForeignKey("MediaFile_MediaFiles_Id")]
- public virtual ICollection<MediaFile> MediaFiles { get; protected set; }
-
- [ForeignKey("Chapter_Chapters_Id")]
- public virtual ICollection<Chapter> Chapters { get; protected set; }
- }
-}
-
diff --git a/Jellyfin.Data/Entities/Season.cs b/Jellyfin.Data/Entities/Season.cs
deleted file mode 100644
index 4b1e78575..000000000
--- a/Jellyfin.Data/Entities/Season.cs
+++ /dev/null
@@ -1,115 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Jellyfin.Data.Entities
-{
- public partial class Season : LibraryItem
- {
- partial void Init();
-
- /// <summary>
- /// Default constructor. Protected due to required properties, but present because EF needs it.
- /// </summary>
- protected Season()
- {
- // NOTE: This class has one-to-one associations with LibraryRoot, LibraryItem and CollectionItem.
- // One-to-one associations are not validated in constructors since this causes a scenario where each one must be constructed before the other.
-
- SeasonMetadata = new HashSet<SeasonMetadata>();
- Episodes = new HashSet<Episode>();
-
- Init();
- }
-
- /// <summary>
- /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
- /// </summary>
- public static Season CreateSeasonUnsafe()
- {
- return new Season();
- }
-
- /// <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>
- /// <param name="_series0"></param>
- public Season(Guid urlid, DateTime dateadded, Series _series0)
- {
- // NOTE: This class has one-to-one associations with LibraryRoot, LibraryItem and CollectionItem.
- // One-to-one associations are not validated in constructors since this causes a scenario where each one must be constructed before the other.
-
- this.UrlId = urlid;
-
- if (_series0 == null)
- {
- throw new ArgumentNullException(nameof(_series0));
- }
-
- _series0.Seasons.Add(this);
-
- this.SeasonMetadata = new HashSet<SeasonMetadata>();
- this.Episodes = new HashSet<Episode>();
-
- Init();
- }
-
- /// <summary>
- /// Static create function (for use in LINQ queries, etc.)
- /// </summary>
- /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param>
- /// <param name="_series0"></param>
- public static Season Create(Guid urlid, DateTime dateadded, Series _series0)
- {
- return new Season(urlid, dateadded, _series0);
- }
-
- /*************************************************************************
- * Properties
- *************************************************************************/
-
- /// <summary>
- /// Backing field for SeasonNumber.
- /// </summary>
- protected int? _SeasonNumber;
- /// <summary>
- /// When provided in a partial class, allows value of SeasonNumber to be changed before setting.
- /// </summary>
- partial void SetSeasonNumber(int? oldValue, ref int? newValue);
- /// <summary>
- /// When provided in a partial class, allows value of SeasonNumber to be changed before returning.
- /// </summary>
- partial void GetSeasonNumber(ref int? result);
-
- public int? SeasonNumber
- {
- get
- {
- int? value = _SeasonNumber;
- GetSeasonNumber(ref value);
- return _SeasonNumber = value;
- }
-
- set
- {
- int? oldValue = _SeasonNumber;
- SetSeasonNumber(oldValue, ref value);
- if (oldValue != value)
- {
- _SeasonNumber = value;
- }
- }
- }
-
- /*************************************************************************
- * Navigation properties
- *************************************************************************/
- [ForeignKey("SeasonMetadata_SeasonMetadata_Id")]
- public virtual ICollection<SeasonMetadata> SeasonMetadata { get; protected set; }
-
- [ForeignKey("Episode_Episodes_Id")]
- public virtual ICollection<Episode> Episodes { get; protected set; }
- }
-}
-
diff --git a/Jellyfin.Data/Entities/SeasonMetadata.cs b/Jellyfin.Data/Entities/SeasonMetadata.cs
deleted file mode 100644
index 10d19875e..000000000
--- a/Jellyfin.Data/Entities/SeasonMetadata.cs
+++ /dev/null
@@ -1,117 +0,0 @@
-using System;
-using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Jellyfin.Data.Entities
-{
- public partial class SeasonMetadata : Metadata
- {
- partial void Init();
-
- /// <summary>
- /// Default constructor. Protected due to required properties, but present because EF needs it.
- /// </summary>
- protected SeasonMetadata()
- {
- Init();
- }
-
- /// <summary>
- /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
- /// </summary>
- public static SeasonMetadata CreateSeasonMetadataUnsafe()
- {
- return new SeasonMetadata();
- }
-
- /// <summary>
- /// Public constructor with required data.
- /// </summary>
- /// <param name="title">The title or name of the object.</param>
- /// <param name="language">ISO-639-3 3-character language codes.</param>
- /// <param name="_season0"></param>
- public SeasonMetadata(string title, string language, DateTime dateadded, DateTime datemodified, Season _season0)
- {
- if (string.IsNullOrEmpty(title))
- {
- throw new ArgumentNullException(nameof(title));
- }
-
- this.Title = title;
-
- if (string.IsNullOrEmpty(language))
- {
- throw new ArgumentNullException(nameof(language));
- }
-
- this.Language = language;
-
- if (_season0 == null)
- {
- throw new ArgumentNullException(nameof(_season0));
- }
-
- _season0.SeasonMetadata.Add(this);
-
- Init();
- }
-
- /// <summary>
- /// Static create function (for use in LINQ queries, etc.)
- /// </summary>
- /// <param name="title">The title or name of the object.</param>
- /// <param name="language">ISO-639-3 3-character language codes.</param>
- /// <param name="_season0"></param>
- public static SeasonMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, Season _season0)
- {
- return new SeasonMetadata(title, language, dateadded, datemodified, _season0);
- }
-
- /*************************************************************************
- * Properties
- *************************************************************************/
-
- /// <summary>
- /// Backing field for Outline.
- /// </summary>
- protected string _Outline;
- /// <summary>
- /// When provided in a partial class, allows value of Outline to be changed before setting.
- /// </summary>
- partial void SetOutline(string oldValue, ref string newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Outline to be changed before returning.
- /// </summary>
- partial void GetOutline(ref string result);
-
- /// <summary>
- /// Max length = 1024
- /// </summary>
- [MaxLength(1024)]
- [StringLength(1024)]
- public string Outline
- {
- get
- {
- string value = _Outline;
- GetOutline(ref value);
- return _Outline = value;
- }
-
- set
- {
- string oldValue = _Outline;
- SetOutline(oldValue, ref value);
- if (oldValue != value)
- {
- _Outline = value;
- }
- }
- }
-
- /*************************************************************************
- * Navigation properties
- *************************************************************************/
- }
-}
-
diff --git a/Jellyfin.Data/Entities/Series.cs b/Jellyfin.Data/Entities/Series.cs
deleted file mode 100644
index bede14acf..000000000
--- a/Jellyfin.Data/Entities/Series.cs
+++ /dev/null
@@ -1,161 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Jellyfin.Data.Entities
-{
- public partial class Series : LibraryItem
- {
- partial void Init();
-
- /// <summary>
- /// Default constructor. Protected due to required properties, but present because EF needs it.
- /// </summary>
- protected Series()
- {
- SeriesMetadata = new HashSet<SeriesMetadata>();
- Seasons = new HashSet<Season>();
-
- Init();
- }
-
- /// <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>
- public Series(Guid urlid, DateTime dateadded)
- {
- this.UrlId = urlid;
-
- this.SeriesMetadata = new HashSet<SeriesMetadata>();
- this.Seasons = new HashSet<Season>();
-
- Init();
- }
-
- /// <summary>
- /// Static create function (for use in LINQ queries, etc.)
- /// </summary>
- /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param>
- public static Series Create(Guid urlid, DateTime dateadded)
- {
- return new Series(urlid, dateadded);
- }
-
- /*************************************************************************
- * Properties
- *************************************************************************/
-
- /// <summary>
- /// Backing field for AirsDayOfWeek.
- /// </summary>
- protected DayOfWeek? _AirsDayOfWeek;
- /// <summary>
- /// When provided in a partial class, allows value of AirsDayOfWeek to be changed before setting.
- /// </summary>
- 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 DayOfWeek? result);
-
- public DayOfWeek? AirsDayOfWeek
- {
- get
- {
- DayOfWeek? value = _AirsDayOfWeek;
- GetAirsDayOfWeek(ref value);
- return _AirsDayOfWeek = value;
- }
-
- set
- {
- DayOfWeek? oldValue = _AirsDayOfWeek;
- SetAirsDayOfWeek(oldValue, ref value);
- if (oldValue != value)
- {
- _AirsDayOfWeek = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for AirsTime.
- /// </summary>
- protected DateTimeOffset? _AirsTime;
- /// <summary>
- /// When provided in a partial class, allows value of AirsTime to be changed before setting.
- /// </summary>
- partial void SetAirsTime(DateTimeOffset? oldValue, ref DateTimeOffset? newValue);
- /// <summary>
- /// When provided in a partial class, allows value of AirsTime to be changed before returning.
- /// </summary>
- partial void GetAirsTime(ref DateTimeOffset? result);
-
- /// <summary>
- /// The time the show airs, ignore the date portion.
- /// </summary>
- public DateTimeOffset? AirsTime
- {
- get
- {
- DateTimeOffset? value = _AirsTime;
- GetAirsTime(ref value);
- return _AirsTime = value;
- }
-
- set
- {
- DateTimeOffset? oldValue = _AirsTime;
- SetAirsTime(oldValue, ref value);
- if (oldValue != value)
- {
- _AirsTime = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for FirstAired.
- /// </summary>
- protected DateTimeOffset? _FirstAired;
- /// <summary>
- /// When provided in a partial class, allows value of FirstAired to be changed before setting.
- /// </summary>
- partial void SetFirstAired(DateTimeOffset? oldValue, ref DateTimeOffset? newValue);
- /// <summary>
- /// When provided in a partial class, allows value of FirstAired to be changed before returning.
- /// </summary>
- partial void GetFirstAired(ref DateTimeOffset? result);
-
- public DateTimeOffset? FirstAired
- {
- get
- {
- DateTimeOffset? value = _FirstAired;
- GetFirstAired(ref value);
- return _FirstAired = value;
- }
-
- set
- {
- DateTimeOffset? oldValue = _FirstAired;
- SetFirstAired(oldValue, ref value);
- if (oldValue != value)
- {
- _FirstAired = value;
- }
- }
- }
-
- /*************************************************************************
- * Navigation properties
- *************************************************************************/
- [ForeignKey("SeriesMetadata_SeriesMetadata_Id")]
- public virtual ICollection<SeriesMetadata> SeriesMetadata { get; protected set; }
-
- [ForeignKey("Season_Seasons_Id")]
- public virtual ICollection<Season> Seasons { get; protected set; }
- }
-}
-
diff --git a/Jellyfin.Data/Entities/SeriesMetadata.cs b/Jellyfin.Data/Entities/SeriesMetadata.cs
deleted file mode 100644
index 16eb59315..000000000
--- a/Jellyfin.Data/Entities/SeriesMetadata.cs
+++ /dev/null
@@ -1,238 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Jellyfin.Data.Entities
-{
- public partial class SeriesMetadata : Metadata
- {
- partial void Init();
-
- /// <summary>
- /// Default constructor. Protected due to required properties, but present because EF needs it.
- /// </summary>
- protected SeriesMetadata()
- {
- Networks = new HashSet<Company>();
-
- Init();
- }
-
- /// <summary>
- /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
- /// </summary>
- public static SeriesMetadata CreateSeriesMetadataUnsafe()
- {
- return new SeriesMetadata();
- }
-
- /// <summary>
- /// Public constructor with required data.
- /// </summary>
- /// <param name="title">The title or name of the object.</param>
- /// <param name="language">ISO-639-3 3-character language codes.</param>
- /// <param name="_series0"></param>
- public SeriesMetadata(string title, string language, DateTime dateadded, DateTime datemodified, Series _series0)
- {
- if (string.IsNullOrEmpty(title))
- {
- throw new ArgumentNullException(nameof(title));
- }
-
- this.Title = title;
-
- if (string.IsNullOrEmpty(language))
- {
- throw new ArgumentNullException(nameof(language));
- }
-
- this.Language = language;
-
- if (_series0 == null)
- {
- throw new ArgumentNullException(nameof(_series0));
- }
-
- _series0.SeriesMetadata.Add(this);
-
- this.Networks = new HashSet<Company>();
-
- Init();
- }
-
- /// <summary>
- /// Static create function (for use in LINQ queries, etc.)
- /// </summary>
- /// <param name="title">The title or name of the object.</param>
- /// <param name="language">ISO-639-3 3-character language codes.</param>
- /// <param name="_series0"></param>
- public static SeriesMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, Series _series0)
- {
- return new SeriesMetadata(title, language, dateadded, datemodified, _series0);
- }
-
- /*************************************************************************
- * Properties
- *************************************************************************/
-
- /// <summary>
- /// Backing field for Outline.
- /// </summary>
- protected string _Outline;
- /// <summary>
- /// When provided in a partial class, allows value of Outline to be changed before setting.
- /// </summary>
- partial void SetOutline(string oldValue, ref string newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Outline to be changed before returning.
- /// </summary>
- partial void GetOutline(ref string result);
-
- /// <summary>
- /// Max length = 1024
- /// </summary>
- [MaxLength(1024)]
- [StringLength(1024)]
- public string Outline
- {
- get
- {
- string value = _Outline;
- GetOutline(ref value);
- return _Outline = value;
- }
-
- set
- {
- string oldValue = _Outline;
- SetOutline(oldValue, ref value);
- if (oldValue != value)
- {
- _Outline = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for Plot.
- /// </summary>
- protected string _Plot;
- /// <summary>
- /// When provided in a partial class, allows value of Plot to be changed before setting.
- /// </summary>
- partial void SetPlot(string oldValue, ref string newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Plot to be changed before returning.
- /// </summary>
- partial void GetPlot(ref string result);
-
- /// <summary>
- /// Max length = 65535
- /// </summary>
- [MaxLength(65535)]
- [StringLength(65535)]
- public string Plot
- {
- get
- {
- string value = _Plot;
- GetPlot(ref value);
- return _Plot = value;
- }
-
- set
- {
- string oldValue = _Plot;
- SetPlot(oldValue, ref value);
- if (oldValue != value)
- {
- _Plot = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for Tagline.
- /// </summary>
- protected string _Tagline;
- /// <summary>
- /// When provided in a partial class, allows value of Tagline to be changed before setting.
- /// </summary>
- partial void SetTagline(string oldValue, ref string newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Tagline to be changed before returning.
- /// </summary>
- partial void GetTagline(ref string result);
-
- /// <summary>
- /// Max length = 1024
- /// </summary>
- [MaxLength(1024)]
- [StringLength(1024)]
- public string Tagline
- {
- get
- {
- string value = _Tagline;
- GetTagline(ref value);
- return _Tagline = value;
- }
-
- set
- {
- string oldValue = _Tagline;
- SetTagline(oldValue, ref value);
- if (oldValue != value)
- {
- _Tagline = value;
- }
- }
- }
-
- /// <summary>
- /// Backing field for Country.
- /// </summary>
- protected string _Country;
- /// <summary>
- /// When provided in a partial class, allows value of Country to be changed before setting.
- /// </summary>
- partial void SetCountry(string oldValue, ref string newValue);
- /// <summary>
- /// When provided in a partial class, allows value of Country to be changed before returning.
- /// </summary>
- partial void GetCountry(ref string result);
-
- /// <summary>
- /// Max length = 2
- /// </summary>
- [MaxLength(2)]
- [StringLength(2)]
- public string Country
- {
- get
- {
- string value = _Country;
- GetCountry(ref value);
- return _Country = value;
- }
-
- set
- {
- string oldValue = _Country;
- SetCountry(oldValue, ref value);
- if (oldValue != value)
- {
- _Country = value;
- }
- }
- }
-
- /*************************************************************************
- * Navigation properties
- *************************************************************************/
- [ForeignKey("Company_Networks_Id")]
- public virtual ICollection<Company> Networks { get; protected set; }
- }
-}
-
diff --git a/Jellyfin.Data/Entities/Track.cs b/Jellyfin.Data/Entities/Track.cs
deleted file mode 100644
index b7d7b5873..000000000
--- a/Jellyfin.Data/Entities/Track.cs
+++ /dev/null
@@ -1,116 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Jellyfin.Data.Entities
-{
- public partial class Track : LibraryItem
- {
- partial void Init();
-
- /// <summary>
- /// Default constructor. Protected due to required properties, but present because EF needs it.
- /// </summary>
- protected Track()
- {
- // NOTE: This class has one-to-one associations with LibraryRoot, LibraryItem and CollectionItem.
- // One-to-one associations are not validated in constructors since this causes a scenario where each one must be constructed before the other.
-
- Releases = new HashSet<Release>();
- TrackMetadata = new HashSet<TrackMetadata>();
-
- Init();
- }
-
- /// <summary>
- /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
- /// </summary>
- public static Track CreateTrackUnsafe()
- {
- return new Track();
- }
-
- /// <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>
- /// <param name="_musicalbum0"></param>
- public Track(Guid urlid, DateTime dateadded, MusicAlbum _musicalbum0)
- {
- // NOTE: This class has one-to-one associations with LibraryRoot, LibraryItem and CollectionItem.
- // One-to-one associations are not validated in constructors since this causes a scenario where each one must be constructed before the other.
-
- this.UrlId = urlid;
-
- if (_musicalbum0 == null)
- {
- throw new ArgumentNullException(nameof(_musicalbum0));
- }
-
- _musicalbum0.Tracks.Add(this);
-
- this.Releases = new HashSet<Release>();
- this.TrackMetadata = new HashSet<TrackMetadata>();
-
- Init();
- }
-
- /// <summary>
- /// Static create function (for use in LINQ queries, etc.)
- /// </summary>
- /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param>
- /// <param name="_musicalbum0"></param>
- public static Track Create(Guid urlid, DateTime dateadded, MusicAlbum _musicalbum0)
- {
- return new Track(urlid, dateadded, _musicalbum0);
- }
-
- /*************************************************************************
- * Properties
- *************************************************************************/
-
- /// <summary>
- /// Backing field for TrackNumber.
- /// </summary>
- protected int? _TrackNumber;
- /// <summary>
- /// When provided in a partial class, allows value of TrackNumber to be changed before setting.
- /// </summary>
- partial void SetTrackNumber(int? oldValue, ref int? newValue);
- /// <summary>
- /// When provided in a partial class, allows value of TrackNumber to be changed before returning.
- /// </summary>
- partial void GetTrackNumber(ref int? result);
-
- public int? TrackNumber
- {
- get
- {
- int? value = _TrackNumber;
- GetTrackNumber(ref value);
- return _TrackNumber = value;
- }
-
- set
- {
- int? oldValue = _TrackNumber;
- SetTrackNumber(oldValue, ref value);
- if (oldValue != value)
- {
- _TrackNumber = value;
- }
- }
- }
-
- /*************************************************************************
- * Navigation properties
- *************************************************************************/
-
- [ForeignKey("Release_Releases_Id")]
- public virtual ICollection<Release> Releases { get; protected set; }
-
- [ForeignKey("TrackMetadata_TrackMetadata_Id")]
- public virtual ICollection<TrackMetadata> TrackMetadata { get; protected set; }
- }
-}
-
diff --git a/Jellyfin.Data/Entities/TrackMetadata.cs b/Jellyfin.Data/Entities/TrackMetadata.cs
deleted file mode 100644
index 23e1219aa..000000000
--- a/Jellyfin.Data/Entities/TrackMetadata.cs
+++ /dev/null
@@ -1,78 +0,0 @@
-using System;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Jellyfin.Data.Entities
-{
- public partial class TrackMetadata : Metadata
- {
- partial void Init();
-
- /// <summary>
- /// Default constructor. Protected due to required properties, but present because EF needs it.
- /// </summary>
- protected TrackMetadata()
- {
- Init();
- }
-
- /// <summary>
- /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
- /// </summary>
- public static TrackMetadata CreateTrackMetadataUnsafe()
- {
- return new TrackMetadata();
- }
-
- /// <summary>
- /// Public constructor with required data.
- /// </summary>
- /// <param name="title">The title or name of the object.</param>
- /// <param name="language">ISO-639-3 3-character language codes.</param>
- /// <param name="_track0"></param>
- public TrackMetadata(string title, string language, DateTime dateadded, DateTime datemodified, Track _track0)
- {
- if (string.IsNullOrEmpty(title))
- {
- throw new ArgumentNullException(nameof(title));
- }
-
- this.Title = title;
-
- if (string.IsNullOrEmpty(language))
- {
- throw new ArgumentNullException(nameof(language));
- }
-
- this.Language = language;
-
- if (_track0 == null)
- {
- throw new ArgumentNullException(nameof(_track0));
- }
-
- _track0.TrackMetadata.Add(this);
-
- Init();
- }
-
- /// <summary>
- /// Static create function (for use in LINQ queries, etc.)
- /// </summary>
- /// <param name="title">The title or name of the object.</param>
- /// <param name="language">ISO-639-3 3-character language codes.</param>
- /// <param name="_track0"></param>
- public static TrackMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, Track _track0)
- {
- return new TrackMetadata(title, language, dateadded, datemodified, _track0);
- }
-
- /*************************************************************************
- * Properties
- *************************************************************************/
-
- /*************************************************************************
- * Navigation properties
- *************************************************************************/
- }
-}
-
diff --git a/Jellyfin.Data/Entities/User.cs b/Jellyfin.Data/Entities/User.cs
index 50810561f..7ea1f4498 100644
--- a/Jellyfin.Data/Entities/User.cs
+++ b/Jellyfin.Data/Entities/User.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
@@ -6,13 +8,14 @@ using System.Globalization;
using System.Linq;
using System.Text.Json.Serialization;
using Jellyfin.Data.Enums;
+using Jellyfin.Data.Interfaces;
namespace Jellyfin.Data.Entities
{
/// <summary>
/// An entity representing a user.
/// </summary>
- public partial class User : IHasPermissions, ISavingChanges
+ public partial class User : IHasPermissions, IHasConcurrencyToken
{
/// <summary>
/// The values being delimited here are Guids, so commas work as they do not appear in Guids.
diff --git a/Jellyfin.Data/Enums/ArtKind.cs b/Jellyfin.Data/Enums/ArtKind.cs
index 6b69d68b2..71b4db6f2 100644
--- a/Jellyfin.Data/Enums/ArtKind.cs
+++ b/Jellyfin.Data/Enums/ArtKind.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace Jellyfin.Data.Enums
{
public enum ArtKind
diff --git a/Jellyfin.Data/Enums/IndexingKind.cs b/Jellyfin.Data/Enums/IndexingKind.cs
index 9badc6573..fafe47e0c 100644
--- a/Jellyfin.Data/Enums/IndexingKind.cs
+++ b/Jellyfin.Data/Enums/IndexingKind.cs
@@ -1,4 +1,6 @@
-namespace Jellyfin.Data.Enums
+#pragma warning disable CS1591
+
+namespace Jellyfin.Data.Enums
{
public enum IndexingKind
{
diff --git a/Jellyfin.Data/Enums/MediaFileKind.cs b/Jellyfin.Data/Enums/MediaFileKind.cs
index 12f48c558..b03591fb8 100644
--- a/Jellyfin.Data/Enums/MediaFileKind.cs
+++ b/Jellyfin.Data/Enums/MediaFileKind.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace Jellyfin.Data.Enums
{
public enum MediaFileKind
diff --git a/Jellyfin.Data/Enums/PersonRoleType.cs b/Jellyfin.Data/Enums/PersonRoleType.cs
index 6e52f2c85..2d80eaa4c 100644
--- a/Jellyfin.Data/Enums/PersonRoleType.cs
+++ b/Jellyfin.Data/Enums/PersonRoleType.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace Jellyfin.Data.Enums
{
public enum PersonRoleType
diff --git a/MediaBrowser.Model/Events/GenericEventArgs.cs b/Jellyfin.Data/Events/GenericEventArgs.cs
index 44f60f811..7b9a5111e 100644
--- a/MediaBrowser.Model/Events/GenericEventArgs.cs
+++ b/Jellyfin.Data/Events/GenericEventArgs.cs
@@ -1,20 +1,14 @@
using System;
-namespace MediaBrowser.Model.Events
+namespace Jellyfin.Data.Events
{
/// <summary>
/// Provides a generic EventArgs subclass that can hold any kind of object.
/// </summary>
- /// <typeparam name="T"></typeparam>
+ /// <typeparam name="T">The type of this event.</typeparam>
public class GenericEventArgs<T> : EventArgs
{
/// <summary>
- /// Gets or sets the argument.
- /// </summary>
- /// <value>The argument.</value>
- public T Argument { get; set; }
-
- /// <summary>
/// Initializes a new instance of the <see cref="GenericEventArgs{T}"/> class.
/// </summary>
/// <param name="arg">The argument.</param>
@@ -22,5 +16,11 @@ namespace MediaBrowser.Model.Events
{
Argument = arg;
}
+
+ /// <summary>
+ /// Gets the argument.
+ /// </summary>
+ /// <value>The argument.</value>
+ public T Argument { get; }
}
}
diff --git a/Jellyfin.Data/Events/System/PendingRestartEventArgs.cs b/Jellyfin.Data/Events/System/PendingRestartEventArgs.cs
new file mode 100644
index 000000000..2aa40a946
--- /dev/null
+++ b/Jellyfin.Data/Events/System/PendingRestartEventArgs.cs
@@ -0,0 +1,11 @@
+using System;
+
+namespace Jellyfin.Data.Events.System
+{
+ /// <summary>
+ /// An event that occurs when there is a pending restart.
+ /// </summary>
+ public class PendingRestartEventArgs : EventArgs
+ {
+ }
+}
diff --git a/Jellyfin.Data/Events/Users/UserCreatedEventArgs.cs b/Jellyfin.Data/Events/Users/UserCreatedEventArgs.cs
new file mode 100644
index 000000000..66f7c8d4f
--- /dev/null
+++ b/Jellyfin.Data/Events/Users/UserCreatedEventArgs.cs
@@ -0,0 +1,18 @@
+using Jellyfin.Data.Entities;
+
+namespace Jellyfin.Data.Events.Users
+{
+ /// <summary>
+ /// An event that occurs when a user is created.
+ /// </summary>
+ public class UserCreatedEventArgs : GenericEventArgs<User>
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UserCreatedEventArgs"/> class.
+ /// </summary>
+ /// <param name="arg">The user.</param>
+ public UserCreatedEventArgs(User arg) : base(arg)
+ {
+ }
+ }
+}
diff --git a/Jellyfin.Data/Events/Users/UserDeletedEventArgs.cs b/Jellyfin.Data/Events/Users/UserDeletedEventArgs.cs
new file mode 100644
index 000000000..0b9493375
--- /dev/null
+++ b/Jellyfin.Data/Events/Users/UserDeletedEventArgs.cs
@@ -0,0 +1,18 @@
+using Jellyfin.Data.Entities;
+
+namespace Jellyfin.Data.Events.Users
+{
+ /// <summary>
+ /// An event that occurs when a user is deleted.
+ /// </summary>
+ public class UserDeletedEventArgs : GenericEventArgs<User>
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UserDeletedEventArgs"/> class.
+ /// </summary>
+ /// <param name="arg">The user.</param>
+ public UserDeletedEventArgs(User arg) : base(arg)
+ {
+ }
+ }
+}
diff --git a/Jellyfin.Data/Events/Users/UserLockedOutEventArgs.cs b/Jellyfin.Data/Events/Users/UserLockedOutEventArgs.cs
new file mode 100644
index 000000000..cca3726dc
--- /dev/null
+++ b/Jellyfin.Data/Events/Users/UserLockedOutEventArgs.cs
@@ -0,0 +1,18 @@
+using Jellyfin.Data.Entities;
+
+namespace Jellyfin.Data.Events.Users
+{
+ /// <summary>
+ /// An event that occurs when a user is locked out.
+ /// </summary>
+ public class UserLockedOutEventArgs : GenericEventArgs<User>
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UserLockedOutEventArgs"/> class.
+ /// </summary>
+ /// <param name="arg">The user.</param>
+ public UserLockedOutEventArgs(User arg) : base(arg)
+ {
+ }
+ }
+}
diff --git a/Jellyfin.Data/Events/Users/UserPasswordChangedEventArgs.cs b/Jellyfin.Data/Events/Users/UserPasswordChangedEventArgs.cs
new file mode 100644
index 000000000..087ec9ab6
--- /dev/null
+++ b/Jellyfin.Data/Events/Users/UserPasswordChangedEventArgs.cs
@@ -0,0 +1,18 @@
+using Jellyfin.Data.Entities;
+
+namespace Jellyfin.Data.Events.Users
+{
+ /// <summary>
+ /// An event that occurs when a user's password has changed.
+ /// </summary>
+ public class UserPasswordChangedEventArgs : GenericEventArgs<User>
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UserPasswordChangedEventArgs"/> class.
+ /// </summary>
+ /// <param name="arg">The user.</param>
+ public UserPasswordChangedEventArgs(User arg) : base(arg)
+ {
+ }
+ }
+}
diff --git a/Jellyfin.Data/Events/Users/UserUpdatedEventArgs.cs b/Jellyfin.Data/Events/Users/UserUpdatedEventArgs.cs
new file mode 100644
index 000000000..2b4e49cdf
--- /dev/null
+++ b/Jellyfin.Data/Events/Users/UserUpdatedEventArgs.cs
@@ -0,0 +1,18 @@
+using Jellyfin.Data.Entities;
+
+namespace Jellyfin.Data.Events.Users
+{
+ /// <summary>
+ /// An event that occurs when a user is updated.
+ /// </summary>
+ public class UserUpdatedEventArgs : GenericEventArgs<User>
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UserUpdatedEventArgs"/> class.
+ /// </summary>
+ /// <param name="arg">The user.</param>
+ public UserUpdatedEventArgs(User arg) : base(arg)
+ {
+ }
+ }
+}
diff --git a/Jellyfin.Data/ISavingChanges.cs b/Jellyfin.Data/ISavingChanges.cs
deleted file mode 100644
index f392dae6a..000000000
--- a/Jellyfin.Data/ISavingChanges.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-#pragma warning disable CS1591
-
-namespace Jellyfin.Data
-{
- public interface ISavingChanges
- {
- void OnSavingChanges();
- }
-}
diff --git a/Jellyfin.Data/Interfaces/IHasArtwork.cs b/Jellyfin.Data/Interfaces/IHasArtwork.cs
new file mode 100644
index 000000000..a4d9c54af
--- /dev/null
+++ b/Jellyfin.Data/Interfaces/IHasArtwork.cs
@@ -0,0 +1,16 @@
+using System.Collections.Generic;
+using Jellyfin.Data.Entities.Libraries;
+
+namespace Jellyfin.Data.Interfaces
+{
+ /// <summary>
+ /// An interface abstracting an entity that has artwork.
+ /// </summary>
+ public interface IHasArtwork
+ {
+ /// <summary>
+ /// Gets a collection containing this entity's artwork.
+ /// </summary>
+ ICollection<Artwork> Artwork { get; }
+ }
+}
diff --git a/Jellyfin.Data/Interfaces/IHasCompanies.cs b/Jellyfin.Data/Interfaces/IHasCompanies.cs
new file mode 100644
index 000000000..8f19ce04f
--- /dev/null
+++ b/Jellyfin.Data/Interfaces/IHasCompanies.cs
@@ -0,0 +1,16 @@
+using System.Collections.Generic;
+using Jellyfin.Data.Entities.Libraries;
+
+namespace Jellyfin.Data.Interfaces
+{
+ /// <summary>
+ /// An abstraction representing an entity that has companies.
+ /// </summary>
+ public interface IHasCompanies
+ {
+ /// <summary>
+ /// Gets a collection containing this entity's companies.
+ /// </summary>
+ ICollection<Company> Companies { get; }
+ }
+}
diff --git a/Jellyfin.Data/Interfaces/IHasConcurrencyToken.cs b/Jellyfin.Data/Interfaces/IHasConcurrencyToken.cs
new file mode 100644
index 000000000..2c4091493
--- /dev/null
+++ b/Jellyfin.Data/Interfaces/IHasConcurrencyToken.cs
@@ -0,0 +1,18 @@
+namespace Jellyfin.Data.Interfaces
+{
+ /// <summary>
+ /// An interface abstracting an entity that has a concurrency token.
+ /// </summary>
+ public interface IHasConcurrencyToken
+ {
+ /// <summary>
+ /// Gets the version of this row. Acts as a concurrency token.
+ /// </summary>
+ uint RowVersion { get; }
+
+ /// <summary>
+ /// Called when saving changes to this entity.
+ /// </summary>
+ void OnSavingChanges();
+ }
+}
diff --git a/Jellyfin.Data/IHasPermissions.cs b/Jellyfin.Data/Interfaces/IHasPermissions.cs
index 3be72259a..3be72259a 100644
--- a/Jellyfin.Data/IHasPermissions.cs
+++ b/Jellyfin.Data/Interfaces/IHasPermissions.cs
diff --git a/Jellyfin.Data/Interfaces/IHasReleases.cs b/Jellyfin.Data/Interfaces/IHasReleases.cs
new file mode 100644
index 000000000..3b615893e
--- /dev/null
+++ b/Jellyfin.Data/Interfaces/IHasReleases.cs
@@ -0,0 +1,16 @@
+using System.Collections.Generic;
+using Jellyfin.Data.Entities.Libraries;
+
+namespace Jellyfin.Data.Interfaces
+{
+ /// <summary>
+ /// An abstraction representing an entity that has releases.
+ /// </summary>
+ public interface IHasReleases
+ {
+ /// <summary>
+ /// Gets a collection containing this entity's releases.
+ /// </summary>
+ ICollection<Release> Releases { get; }
+ }
+}
diff --git a/Jellyfin.Data/Jellyfin.Data.csproj b/Jellyfin.Data/Jellyfin.Data.csproj
index 8ce0f3848..e8065419d 100644
--- a/Jellyfin.Data/Jellyfin.Data.csproj
+++ b/Jellyfin.Data/Jellyfin.Data.csproj
@@ -4,6 +4,15 @@
<TargetFrameworks>netstandard2.0;netstandard2.1</TargetFrameworks>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
+ <TreatWarningsAsErrors Condition=" '$(Configuration)' == 'Release' ">true</TreatWarningsAsErrors>
+ </PropertyGroup>
+
+ <PropertyGroup>
+ <Authors>Jellyfin Contributors</Authors>
+ <PackageId>Jellyfin.Data</PackageId>
+ <VersionPrefix>10.7.0</VersionPrefix>
+ <RepositoryUrl>https://github.com/jellyfin/jellyfin</RepositoryUrl>
+ <PackageLicenseExpression>GPL-3.0-only</PackageLicenseExpression>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
diff --git a/Jellyfin.Server.Implementations/Activity/ActivityManager.cs b/Jellyfin.Server.Implementations/Activity/ActivityManager.cs
index 2deefbe81..abdd290d4 100644
--- a/Jellyfin.Server.Implementations/Activity/ActivityManager.cs
+++ b/Jellyfin.Server.Implementations/Activity/ActivityManager.cs
@@ -2,8 +2,8 @@ using System;
using System.Linq;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
+using Jellyfin.Data.Events;
using MediaBrowser.Model.Activity;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.Querying;
namespace Jellyfin.Server.Implementations.Activity
@@ -28,16 +28,6 @@ namespace Jellyfin.Server.Implementations.Activity
public event EventHandler<GenericEventArgs<ActivityLogEntry>> EntryCreated;
/// <inheritdoc/>
- public void Create(ActivityLog entry)
- {
- using var dbContext = _provider.CreateContext();
- dbContext.ActivityLogs.Add(entry);
- dbContext.SaveChanges();
-
- EntryCreated?.Invoke(this, new GenericEventArgs<ActivityLogEntry>(ConvertToOldModel(entry)));
- }
-
- /// <inheritdoc/>
public async Task CreateAsync(ActivityLog entry)
{
await using var dbContext = _provider.CreateContext();
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Library/SubtitleDownloadFailureLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Library/SubtitleDownloadFailureLogger.cs
new file mode 100644
index 000000000..449f27be2
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Library/SubtitleDownloadFailureLogger.cs
@@ -0,0 +1,102 @@
+using System;
+using System.Globalization;
+using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Controller.Subtitles;
+using MediaBrowser.Model.Activity;
+using MediaBrowser.Model.Globalization;
+using Episode = MediaBrowser.Controller.Entities.TV.Episode;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.Library
+{
+ /// <summary>
+ /// Creates an entry in the activity log whenever a subtitle download fails.
+ /// </summary>
+ public class SubtitleDownloadFailureLogger : IEventConsumer<SubtitleDownloadFailureEventArgs>
+ {
+ private readonly ILocalizationManager _localizationManager;
+ private readonly IActivityManager _activityManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SubtitleDownloadFailureLogger"/> class.
+ /// </summary>
+ /// <param name="localizationManager">The localization manager.</param>
+ /// <param name="activityManager">The activity manager.</param>
+ public SubtitleDownloadFailureLogger(ILocalizationManager localizationManager, IActivityManager activityManager)
+ {
+ _localizationManager = localizationManager;
+ _activityManager = activityManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(SubtitleDownloadFailureEventArgs eventArgs)
+ {
+ await _activityManager.CreateAsync(new ActivityLog(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("SubtitleDownloadFailureFromForItem"),
+ eventArgs.Provider,
+ GetItemName(eventArgs.Item)),
+ "SubtitleDownloadFailure",
+ Guid.Empty)
+ {
+ ItemId = eventArgs.Item.Id.ToString("N", CultureInfo.InvariantCulture),
+ ShortOverview = eventArgs.Exception.Message
+ }).ConfigureAwait(false);
+ }
+
+ private static string GetItemName(BaseItem item)
+ {
+ var name = item.Name;
+ if (item is Episode episode)
+ {
+ if (episode.IndexNumber.HasValue)
+ {
+ name = string.Format(
+ CultureInfo.InvariantCulture,
+ "Ep{0} - {1}",
+ episode.IndexNumber.Value,
+ name);
+ }
+
+ if (episode.ParentIndexNumber.HasValue)
+ {
+ name = string.Format(
+ CultureInfo.InvariantCulture,
+ "S{0}, {1}",
+ episode.ParentIndexNumber.Value,
+ name);
+ }
+ }
+
+ if (item is IHasSeries hasSeries)
+ {
+ name = hasSeries.SeriesName + " - " + name;
+ }
+
+ if (item is IHasAlbumArtist hasAlbumArtist)
+ {
+ var artists = hasAlbumArtist.AlbumArtists;
+
+ if (artists.Count > 0)
+ {
+ name = artists[0] + " - " + name;
+ }
+ }
+ else if (item is IHasArtist hasArtist)
+ {
+ var artists = hasArtist.Artists;
+
+ if (artists.Count > 0)
+ {
+ name = artists[0] + " - " + name;
+ }
+ }
+
+ return name;
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationFailedLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationFailedLogger.cs
new file mode 100644
index 000000000..f899b4497
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationFailedLogger.cs
@@ -0,0 +1,52 @@
+using System;
+using System.Globalization;
+using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Events;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Controller.Session;
+using MediaBrowser.Model.Activity;
+using MediaBrowser.Model.Globalization;
+using Microsoft.Extensions.Logging;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.Security
+{
+ /// <summary>
+ /// Creates an entry in the activity log when there is a failed login attempt.
+ /// </summary>
+ public class AuthenticationFailedLogger : IEventConsumer<GenericEventArgs<AuthenticationRequest>>
+ {
+ private readonly ILocalizationManager _localizationManager;
+ private readonly IActivityManager _activityManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="AuthenticationFailedLogger"/> class.
+ /// </summary>
+ /// <param name="localizationManager">The localization manager.</param>
+ /// <param name="activityManager">The activity manager.</param>
+ public AuthenticationFailedLogger(ILocalizationManager localizationManager, IActivityManager activityManager)
+ {
+ _localizationManager = localizationManager;
+ _activityManager = activityManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(GenericEventArgs<AuthenticationRequest> eventArgs)
+ {
+ await _activityManager.CreateAsync(new ActivityLog(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("FailedLoginAttemptWithUserName"),
+ eventArgs.Argument.Username),
+ "AuthenticationFailed",
+ Guid.Empty)
+ {
+ LogSeverity = LogLevel.Error,
+ ShortOverview = string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("LabelIpAddressValue"),
+ eventArgs.Argument.RemoteEndPoint),
+ }).ConfigureAwait(false);
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationSucceededLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationSucceededLogger.cs
new file mode 100644
index 000000000..2f9f44ed6
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationSucceededLogger.cs
@@ -0,0 +1,49 @@
+using System.Globalization;
+using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Events;
+using MediaBrowser.Controller.Authentication;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Model.Activity;
+using MediaBrowser.Model.Globalization;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.Security
+{
+ /// <summary>
+ /// Creates an entry in the activity log when there is a successful login attempt.
+ /// </summary>
+ public class AuthenticationSucceededLogger : IEventConsumer<GenericEventArgs<AuthenticationResult>>
+ {
+ private readonly ILocalizationManager _localizationManager;
+ private readonly IActivityManager _activityManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="AuthenticationSucceededLogger"/> class.
+ /// </summary>
+ /// <param name="localizationManager">The localization manager.</param>
+ /// <param name="activityManager">The activity manager.</param>
+ public AuthenticationSucceededLogger(ILocalizationManager localizationManager, IActivityManager activityManager)
+ {
+ _localizationManager = localizationManager;
+ _activityManager = activityManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(GenericEventArgs<AuthenticationResult> e)
+ {
+ await _activityManager.CreateAsync(new ActivityLog(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("AuthenticationSucceededWithUserName"),
+ e.Argument.User.Name),
+ "AuthenticationSucceeded",
+ e.Argument.User.Id)
+ {
+ ShortOverview = string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("LabelIpAddressValue"),
+ e.Argument.SessionInfo.RemoteEndPoint),
+ }).ConfigureAwait(false);
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStartLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStartLogger.cs
new file mode 100644
index 000000000..ec4a76e7f
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStartLogger.cs
@@ -0,0 +1,104 @@
+using System;
+using System.Globalization;
+using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Activity;
+using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Globalization;
+using MediaBrowser.Model.Notifications;
+using Microsoft.Extensions.Logging;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.Session
+{
+ /// <summary>
+ /// Creates an entry in the activity log whenever a user starts playback.
+ /// </summary>
+ public class PlaybackStartLogger : IEventConsumer<PlaybackStartEventArgs>
+ {
+ private readonly ILogger<PlaybackStartLogger> _logger;
+ private readonly ILocalizationManager _localizationManager;
+ private readonly IActivityManager _activityManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PlaybackStartLogger"/> class.
+ /// </summary>
+ /// <param name="logger">The logger.</param>
+ /// <param name="localizationManager">The localization manager.</param>
+ /// <param name="activityManager">The activity manager.</param>
+ public PlaybackStartLogger(ILogger<PlaybackStartLogger> logger, ILocalizationManager localizationManager, IActivityManager activityManager)
+ {
+ _logger = logger;
+ _localizationManager = localizationManager;
+ _activityManager = activityManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(PlaybackStartEventArgs eventArgs)
+ {
+ if (eventArgs.MediaInfo == null)
+ {
+ _logger.LogWarning("PlaybackStart reported with null media info.");
+ return;
+ }
+
+ if (eventArgs.Item != null && eventArgs.Item.IsThemeMedia)
+ {
+ // Don't report theme song or local trailer playback
+ return;
+ }
+
+ if (eventArgs.Users.Count == 0)
+ {
+ return;
+ }
+
+ var user = eventArgs.Users[0];
+
+ await _activityManager.CreateAsync(new ActivityLog(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("UserStartedPlayingItemWithValues"),
+ user.Username,
+ GetItemName(eventArgs.MediaInfo),
+ eventArgs.DeviceName),
+ GetPlaybackNotificationType(eventArgs.MediaInfo.MediaType),
+ user.Id))
+ .ConfigureAwait(false);
+ }
+
+ private static string GetItemName(BaseItemDto item)
+ {
+ var name = item.Name;
+
+ if (!string.IsNullOrEmpty(item.SeriesName))
+ {
+ name = item.SeriesName + " - " + name;
+ }
+
+ if (item.Artists != null && item.Artists.Count > 0)
+ {
+ name = item.Artists[0] + " - " + name;
+ }
+
+ return name;
+ }
+
+ private static string GetPlaybackNotificationType(string mediaType)
+ {
+ if (string.Equals(mediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase))
+ {
+ return NotificationType.AudioPlayback.ToString();
+ }
+
+ if (string.Equals(mediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
+ {
+ return NotificationType.VideoPlayback.ToString();
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStopLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStopLogger.cs
new file mode 100644
index 000000000..51a882c14
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStopLogger.cs
@@ -0,0 +1,106 @@
+using System;
+using System.Globalization;
+using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Activity;
+using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Globalization;
+using MediaBrowser.Model.Notifications;
+using Microsoft.Extensions.Logging;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.Session
+{
+ /// <summary>
+ /// Creates an activity log entry whenever a user stops playback.
+ /// </summary>
+ public class PlaybackStopLogger : IEventConsumer<PlaybackStopEventArgs>
+ {
+ private readonly ILogger<PlaybackStopLogger> _logger;
+ private readonly ILocalizationManager _localizationManager;
+ private readonly IActivityManager _activityManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PlaybackStopLogger"/> class.
+ /// </summary>
+ /// <param name="logger">The logger.</param>
+ /// <param name="localizationManager">The localization manager.</param>
+ /// <param name="activityManager">The activity manager.</param>
+ public PlaybackStopLogger(ILogger<PlaybackStopLogger> logger, ILocalizationManager localizationManager, IActivityManager activityManager)
+ {
+ _logger = logger;
+ _localizationManager = localizationManager;
+ _activityManager = activityManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(PlaybackStopEventArgs eventArgs)
+ {
+ var item = eventArgs.MediaInfo;
+
+ if (item == null)
+ {
+ _logger.LogWarning("PlaybackStopped reported with null media info.");
+ return;
+ }
+
+ if (eventArgs.Item != null && eventArgs.Item.IsThemeMedia)
+ {
+ // Don't report theme song or local trailer playback
+ return;
+ }
+
+ if (eventArgs.Users.Count == 0)
+ {
+ return;
+ }
+
+ var user = eventArgs.Users[0];
+
+ await _activityManager.CreateAsync(new ActivityLog(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("UserStoppedPlayingItemWithValues"),
+ user.Username,
+ GetItemName(item),
+ eventArgs.DeviceName),
+ GetPlaybackStoppedNotificationType(item.MediaType),
+ user.Id))
+ .ConfigureAwait(false);
+ }
+
+ private static string GetItemName(BaseItemDto item)
+ {
+ var name = item.Name;
+
+ if (!string.IsNullOrEmpty(item.SeriesName))
+ {
+ name = item.SeriesName + " - " + name;
+ }
+
+ if (item.Artists != null && item.Artists.Count > 0)
+ {
+ name = item.Artists[0] + " - " + name;
+ }
+
+ return name;
+ }
+
+ private static string GetPlaybackStoppedNotificationType(string mediaType)
+ {
+ if (string.Equals(mediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase))
+ {
+ return NotificationType.AudioPlaybackStopped.ToString();
+ }
+
+ if (string.Equals(mediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
+ {
+ return NotificationType.VideoPlaybackStopped.ToString();
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Session/SessionEndedLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Session/SessionEndedLogger.cs
new file mode 100644
index 000000000..cf20946ec
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Session/SessionEndedLogger.cs
@@ -0,0 +1,54 @@
+using System.Globalization;
+using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Controller.Events.Session;
+using MediaBrowser.Model.Activity;
+using MediaBrowser.Model.Globalization;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.Session
+{
+ /// <summary>
+ /// Creates an entry in the activity log whenever a session ends.
+ /// </summary>
+ public class SessionEndedLogger : IEventConsumer<SessionEndedEventArgs>
+ {
+ private readonly ILocalizationManager _localizationManager;
+ private readonly IActivityManager _activityManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SessionEndedLogger"/> class.
+ /// </summary>
+ /// <param name="localizationManager">The localization manager.</param>
+ /// <param name="activityManager">The activity manager.</param>
+ public SessionEndedLogger(ILocalizationManager localizationManager, IActivityManager activityManager)
+ {
+ _localizationManager = localizationManager;
+ _activityManager = activityManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(SessionEndedEventArgs eventArgs)
+ {
+ if (string.IsNullOrEmpty(eventArgs.Argument.UserName))
+ {
+ return;
+ }
+
+ await _activityManager.CreateAsync(new ActivityLog(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("UserOfflineFromDevice"),
+ eventArgs.Argument.UserName,
+ eventArgs.Argument.DeviceName),
+ "SessionEnded",
+ eventArgs.Argument.UserId)
+ {
+ ShortOverview = string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("LabelIpAddressValue"),
+ eventArgs.Argument.RemoteEndPoint),
+ }).ConfigureAwait(false);
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Session/SessionStartedLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Session/SessionStartedLogger.cs
new file mode 100644
index 000000000..6a0f29b09
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Session/SessionStartedLogger.cs
@@ -0,0 +1,54 @@
+using System.Globalization;
+using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Controller.Events.Session;
+using MediaBrowser.Model.Activity;
+using MediaBrowser.Model.Globalization;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.Session
+{
+ /// <summary>
+ /// Creates an entry in the activity log when a session is started.
+ /// </summary>
+ public class SessionStartedLogger : IEventConsumer<SessionStartedEventArgs>
+ {
+ private readonly ILocalizationManager _localizationManager;
+ private readonly IActivityManager _activityManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SessionStartedLogger"/> class.
+ /// </summary>
+ /// <param name="localizationManager">The localization manager.</param>
+ /// <param name="activityManager">The activity manager.</param>
+ public SessionStartedLogger(ILocalizationManager localizationManager, IActivityManager activityManager)
+ {
+ _localizationManager = localizationManager;
+ _activityManager = activityManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(SessionStartedEventArgs eventArgs)
+ {
+ if (string.IsNullOrEmpty(eventArgs.Argument.UserName))
+ {
+ return;
+ }
+
+ await _activityManager.CreateAsync(new ActivityLog(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("UserOnlineFromDevice"),
+ eventArgs.Argument.UserName,
+ eventArgs.Argument.DeviceName),
+ "SessionStarted",
+ eventArgs.Argument.UserId)
+ {
+ ShortOverview = string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("LabelIpAddressValue"),
+ eventArgs.Argument.RemoteEndPoint)
+ }).ConfigureAwait(false);
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/System/PendingRestartNotifier.cs b/Jellyfin.Server.Implementations/Events/Consumers/System/PendingRestartNotifier.cs
new file mode 100644
index 000000000..2fa38dd71
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/System/PendingRestartNotifier.cs
@@ -0,0 +1,31 @@
+using System.Threading;
+using System.Threading.Tasks;
+using Jellyfin.Data.Events.System;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Controller.Session;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.System
+{
+ /// <summary>
+ /// Notifies users when there is a pending restart.
+ /// </summary>
+ public class PendingRestartNotifier : IEventConsumer<PendingRestartEventArgs>
+ {
+ private readonly ISessionManager _sessionManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PendingRestartNotifier"/> class.
+ /// </summary>
+ /// <param name="sessionManager">The session manager.</param>
+ public PendingRestartNotifier(ISessionManager sessionManager)
+ {
+ _sessionManager = sessionManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(PendingRestartEventArgs eventArgs)
+ {
+ await _sessionManager.SendRestartRequiredNotification(CancellationToken.None).ConfigureAwait(false);
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/System/TaskCompletedLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/System/TaskCompletedLogger.cs
new file mode 100644
index 000000000..05201a346
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/System/TaskCompletedLogger.cs
@@ -0,0 +1,158 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Text;
+using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Model.Activity;
+using MediaBrowser.Model.Globalization;
+using MediaBrowser.Model.Notifications;
+using MediaBrowser.Model.Tasks;
+using Microsoft.Extensions.Logging;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.System
+{
+ /// <summary>
+ /// Creates an activity log entry whenever a task is completed.
+ /// </summary>
+ public class TaskCompletedLogger : IEventConsumer<TaskCompletionEventArgs>
+ {
+ private readonly ILocalizationManager _localizationManager;
+ private readonly IActivityManager _activityManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TaskCompletedLogger"/> class.
+ /// </summary>
+ /// <param name="localizationManager">The localization manager.</param>
+ /// <param name="activityManager">The activity manager.</param>
+ public TaskCompletedLogger(ILocalizationManager localizationManager, IActivityManager activityManager)
+ {
+ _localizationManager = localizationManager;
+ _activityManager = activityManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(TaskCompletionEventArgs e)
+ {
+ var result = e.Result;
+ var task = e.Task;
+
+ if (task.ScheduledTask is IConfigurableScheduledTask activityTask
+ && !activityTask.IsLogged)
+ {
+ return;
+ }
+
+ var time = result.EndTimeUtc - result.StartTimeUtc;
+ var runningTime = string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("LabelRunningTimeValue"),
+ ToUserFriendlyString(time));
+
+ if (result.Status == TaskCompletionStatus.Failed)
+ {
+ var vals = new List<string>();
+
+ if (!string.IsNullOrEmpty(e.Result.ErrorMessage))
+ {
+ vals.Add(e.Result.ErrorMessage);
+ }
+
+ if (!string.IsNullOrEmpty(e.Result.LongErrorMessage))
+ {
+ vals.Add(e.Result.LongErrorMessage);
+ }
+
+ await _activityManager.CreateAsync(new ActivityLog(
+ string.Format(CultureInfo.InvariantCulture, _localizationManager.GetLocalizedString("ScheduledTaskFailedWithName"), task.Name),
+ NotificationType.TaskFailed.ToString(),
+ Guid.Empty)
+ {
+ LogSeverity = LogLevel.Error,
+ Overview = string.Join(Environment.NewLine, vals),
+ ShortOverview = runningTime
+ }).ConfigureAwait(false);
+ }
+ }
+
+ private static string ToUserFriendlyString(TimeSpan span)
+ {
+ const int DaysInYear = 365;
+ const int DaysInMonth = 30;
+
+ // Get each non-zero value from TimeSpan component
+ var values = new List<string>();
+
+ // Number of years
+ int days = span.Days;
+ if (days >= DaysInYear)
+ {
+ int years = days / DaysInYear;
+ values.Add(CreateValueString(years, "year"));
+ days %= DaysInYear;
+ }
+
+ // Number of months
+ if (days >= DaysInMonth)
+ {
+ int months = days / DaysInMonth;
+ values.Add(CreateValueString(months, "month"));
+ days = days % DaysInMonth;
+ }
+
+ // Number of days
+ if (days >= 1)
+ {
+ values.Add(CreateValueString(days, "day"));
+ }
+
+ // Number of hours
+ if (span.Hours >= 1)
+ {
+ values.Add(CreateValueString(span.Hours, "hour"));
+ }
+
+ // Number of minutes
+ if (span.Minutes >= 1)
+ {
+ values.Add(CreateValueString(span.Minutes, "minute"));
+ }
+
+ // Number of seconds (include when 0 if no other components included)
+ if (span.Seconds >= 1 || values.Count == 0)
+ {
+ values.Add(CreateValueString(span.Seconds, "second"));
+ }
+
+ // Combine values into string
+ var builder = new StringBuilder();
+ for (int i = 0; i < values.Count; i++)
+ {
+ if (builder.Length > 0)
+ {
+ builder.Append(i == values.Count - 1 ? " and " : ", ");
+ }
+
+ builder.Append(values[i]);
+ }
+
+ // Return result
+ return builder.ToString();
+ }
+
+ /// <summary>
+ /// Constructs a string description of a time-span value.
+ /// </summary>
+ /// <param name="value">The value of this item.</param>
+ /// <param name="description">The name of this item (singular form).</param>
+ private static string CreateValueString(int value, string description)
+ {
+ return string.Format(
+ CultureInfo.InvariantCulture,
+ "{0:#,##0} {1}",
+ value,
+ value == 1 ? description : string.Format(CultureInfo.InvariantCulture, "{0}s", description));
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/System/TaskCompletedNotifier.cs b/Jellyfin.Server.Implementations/Events/Consumers/System/TaskCompletedNotifier.cs
new file mode 100644
index 000000000..80ed56cd8
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/System/TaskCompletedNotifier.cs
@@ -0,0 +1,31 @@
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Controller.Session;
+using MediaBrowser.Model.Tasks;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.System
+{
+ /// <summary>
+ /// Notifies admin users when a task is completed.
+ /// </summary>
+ public class TaskCompletedNotifier : IEventConsumer<TaskCompletionEventArgs>
+ {
+ private readonly ISessionManager _sessionManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TaskCompletedNotifier"/> class.
+ /// </summary>
+ /// <param name="sessionManager">The session manager.</param>
+ public TaskCompletedNotifier(ISessionManager sessionManager)
+ {
+ _sessionManager = sessionManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(TaskCompletionEventArgs eventArgs)
+ {
+ await _sessionManager.SendMessageToAdminSessions("ScheduledTaskEnded", eventArgs.Result, CancellationToken.None).ConfigureAwait(false);
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationCancelledNotifier.cs b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationCancelledNotifier.cs
new file mode 100644
index 000000000..1c600683a
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationCancelledNotifier.cs
@@ -0,0 +1,31 @@
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Controller.Events.Updates;
+using MediaBrowser.Controller.Session;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.Updates
+{
+ /// <summary>
+ /// Notifies admin users when a plugin installation is cancelled.
+ /// </summary>
+ public class PluginInstallationCancelledNotifier : IEventConsumer<PluginInstallationCancelledEventArgs>
+ {
+ private readonly ISessionManager _sessionManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PluginInstallationCancelledNotifier"/> class.
+ /// </summary>
+ /// <param name="sessionManager">The session manager.</param>
+ public PluginInstallationCancelledNotifier(ISessionManager sessionManager)
+ {
+ _sessionManager = sessionManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(PluginInstallationCancelledEventArgs eventArgs)
+ {
+ await _sessionManager.SendMessageToAdminSessions("PackageInstallationCancelled", eventArgs.Argument, CancellationToken.None).ConfigureAwait(false);
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationFailedLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationFailedLogger.cs
new file mode 100644
index 000000000..d71c298c5
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationFailedLogger.cs
@@ -0,0 +1,51 @@
+using System;
+using System.Globalization;
+using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using MediaBrowser.Common.Updates;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Model.Activity;
+using MediaBrowser.Model.Globalization;
+using MediaBrowser.Model.Notifications;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.Updates
+{
+ /// <summary>
+ /// Creates an entry in the activity log when a package installation fails.
+ /// </summary>
+ public class PluginInstallationFailedLogger : IEventConsumer<InstallationFailedEventArgs>
+ {
+ private readonly ILocalizationManager _localizationManager;
+ private readonly IActivityManager _activityManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PluginInstallationFailedLogger"/> class.
+ /// </summary>
+ /// <param name="localizationManager">The localization manager.</param>
+ /// <param name="activityManager">The activity manager.</param>
+ public PluginInstallationFailedLogger(ILocalizationManager localizationManager, IActivityManager activityManager)
+ {
+ _localizationManager = localizationManager;
+ _activityManager = activityManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(InstallationFailedEventArgs eventArgs)
+ {
+ await _activityManager.CreateAsync(new ActivityLog(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("NameInstallFailed"),
+ eventArgs.InstallationInfo.Name),
+ NotificationType.InstallationFailed.ToString(),
+ Guid.Empty)
+ {
+ ShortOverview = string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("VersionNumber"),
+ eventArgs.InstallationInfo.Version),
+ Overview = eventArgs.Exception.Message
+ }).ConfigureAwait(false);
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationFailedNotifier.cs b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationFailedNotifier.cs
new file mode 100644
index 000000000..ea0c878d4
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationFailedNotifier.cs
@@ -0,0 +1,31 @@
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Common.Updates;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Controller.Session;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.Updates
+{
+ /// <summary>
+ /// Notifies admin users when a plugin installation fails.
+ /// </summary>
+ public class PluginInstallationFailedNotifier : IEventConsumer<InstallationFailedEventArgs>
+ {
+ private readonly ISessionManager _sessionManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PluginInstallationFailedNotifier"/> class.
+ /// </summary>
+ /// <param name="sessionManager">The session manager.</param>
+ public PluginInstallationFailedNotifier(ISessionManager sessionManager)
+ {
+ _sessionManager = sessionManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(InstallationFailedEventArgs eventArgs)
+ {
+ await _sessionManager.SendMessageToAdminSessions("PackageInstallationFailed", eventArgs.InstallationInfo, CancellationToken.None).ConfigureAwait(false);
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstalledLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstalledLogger.cs
new file mode 100644
index 000000000..8837172db
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstalledLogger.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Globalization;
+using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Controller.Events.Updates;
+using MediaBrowser.Model.Activity;
+using MediaBrowser.Model.Globalization;
+using MediaBrowser.Model.Notifications;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.Updates
+{
+ /// <summary>
+ /// Creates an entry in the activity log when a plugin is installed.
+ /// </summary>
+ public class PluginInstalledLogger : IEventConsumer<PluginInstalledEventArgs>
+ {
+ private readonly ILocalizationManager _localizationManager;
+ private readonly IActivityManager _activityManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PluginInstalledLogger"/> class.
+ /// </summary>
+ /// <param name="localizationManager">The localization manager.</param>
+ /// <param name="activityManager">The activity manager.</param>
+ public PluginInstalledLogger(ILocalizationManager localizationManager, IActivityManager activityManager)
+ {
+ _localizationManager = localizationManager;
+ _activityManager = activityManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(PluginInstalledEventArgs eventArgs)
+ {
+ await _activityManager.CreateAsync(new ActivityLog(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("PluginInstalledWithName"),
+ eventArgs.Argument.Name),
+ NotificationType.PluginInstalled.ToString(),
+ Guid.Empty)
+ {
+ ShortOverview = string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("VersionNumber"),
+ eventArgs.Argument.Version)
+ }).ConfigureAwait(false);
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstalledNotifier.cs b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstalledNotifier.cs
new file mode 100644
index 000000000..3dda5a04c
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstalledNotifier.cs
@@ -0,0 +1,31 @@
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Controller.Events.Updates;
+using MediaBrowser.Controller.Session;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.Updates
+{
+ /// <summary>
+ /// Notifies admin users when a plugin is installed.
+ /// </summary>
+ public class PluginInstalledNotifier : IEventConsumer<PluginInstalledEventArgs>
+ {
+ private readonly ISessionManager _sessionManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PluginInstalledNotifier"/> class.
+ /// </summary>
+ /// <param name="sessionManager">The session manager.</param>
+ public PluginInstalledNotifier(ISessionManager sessionManager)
+ {
+ _sessionManager = sessionManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(PluginInstalledEventArgs eventArgs)
+ {
+ await _sessionManager.SendMessageToAdminSessions("PackageInstallationCompleted", eventArgs.Argument, CancellationToken.None).ConfigureAwait(false);
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallingNotifier.cs b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallingNotifier.cs
new file mode 100644
index 000000000..f691d11a7
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallingNotifier.cs
@@ -0,0 +1,31 @@
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Controller.Events.Updates;
+using MediaBrowser.Controller.Session;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.Updates
+{
+ /// <summary>
+ /// Notifies admin users when a plugin is being installed.
+ /// </summary>
+ public class PluginInstallingNotifier : IEventConsumer<PluginInstallingEventArgs>
+ {
+ private readonly ISessionManager _sessionManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PluginInstallingNotifier"/> class.
+ /// </summary>
+ /// <param name="sessionManager">The session manager.</param>
+ public PluginInstallingNotifier(ISessionManager sessionManager)
+ {
+ _sessionManager = sessionManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(PluginInstallingEventArgs eventArgs)
+ {
+ await _sessionManager.SendMessageToAdminSessions("PackageInstalling", eventArgs.Argument, CancellationToken.None).ConfigureAwait(false);
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUninstalledLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUninstalledLogger.cs
new file mode 100644
index 000000000..91a30069e
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUninstalledLogger.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Globalization;
+using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Controller.Events.Updates;
+using MediaBrowser.Model.Activity;
+using MediaBrowser.Model.Globalization;
+using MediaBrowser.Model.Notifications;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.Updates
+{
+ /// <summary>
+ /// Creates an entry in the activity log when a plugin is uninstalled.
+ /// </summary>
+ public class PluginUninstalledLogger : IEventConsumer<PluginUninstalledEventArgs>
+ {
+ private readonly ILocalizationManager _localizationManager;
+ private readonly IActivityManager _activityManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PluginUninstalledLogger"/> class.
+ /// </summary>
+ /// <param name="localizationManager">The localization manager.</param>
+ /// <param name="activityManager">The activity manager.</param>
+ public PluginUninstalledLogger(ILocalizationManager localizationManager, IActivityManager activityManager)
+ {
+ _localizationManager = localizationManager;
+ _activityManager = activityManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(PluginUninstalledEventArgs e)
+ {
+ await _activityManager.CreateAsync(new ActivityLog(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("PluginUninstalledWithName"),
+ e.Argument.Name),
+ NotificationType.PluginUninstalled.ToString(),
+ Guid.Empty))
+ .ConfigureAwait(false);
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUninstalledNotifier.cs b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUninstalledNotifier.cs
new file mode 100644
index 000000000..709692f6b
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUninstalledNotifier.cs
@@ -0,0 +1,31 @@
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Controller.Events.Updates;
+using MediaBrowser.Controller.Session;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.Updates
+{
+ /// <summary>
+ /// Notifies admin users when a plugin is uninstalled.
+ /// </summary>
+ public class PluginUninstalledNotifier : IEventConsumer<PluginUninstalledEventArgs>
+ {
+ private readonly ISessionManager _sessionManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PluginUninstalledNotifier"/> class.
+ /// </summary>
+ /// <param name="sessionManager">The session manager.</param>
+ public PluginUninstalledNotifier(ISessionManager sessionManager)
+ {
+ _sessionManager = sessionManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(PluginUninstalledEventArgs eventArgs)
+ {
+ await _sessionManager.SendMessageToAdminSessions("PluginUninstalled", eventArgs.Argument, CancellationToken.None).ConfigureAwait(false);
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUpdatedLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUpdatedLogger.cs
new file mode 100644
index 000000000..9ce16f774
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUpdatedLogger.cs
@@ -0,0 +1,51 @@
+using System;
+using System.Globalization;
+using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Controller.Events.Updates;
+using MediaBrowser.Model.Activity;
+using MediaBrowser.Model.Globalization;
+using MediaBrowser.Model.Notifications;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.Updates
+{
+ /// <summary>
+ /// Creates an entry in the activity log when a plugin is updated.
+ /// </summary>
+ public class PluginUpdatedLogger : IEventConsumer<PluginUpdatedEventArgs>
+ {
+ private readonly ILocalizationManager _localizationManager;
+ private readonly IActivityManager _activityManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PluginUpdatedLogger"/> class.
+ /// </summary>
+ /// <param name="localizationManager">The localization manager.</param>
+ /// <param name="activityManager">The activity manager.</param>
+ public PluginUpdatedLogger(ILocalizationManager localizationManager, IActivityManager activityManager)
+ {
+ _localizationManager = localizationManager;
+ _activityManager = activityManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(PluginUpdatedEventArgs eventArgs)
+ {
+ await _activityManager.CreateAsync(new ActivityLog(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("PluginUpdatedWithName"),
+ eventArgs.Argument.Name),
+ NotificationType.PluginUpdateInstalled.ToString(),
+ Guid.Empty)
+ {
+ ShortOverview = string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("VersionNumber"),
+ eventArgs.Argument.Version),
+ Overview = eventArgs.Argument.Changelog
+ }).ConfigureAwait(false);
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Users/UserCreatedLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Users/UserCreatedLogger.cs
new file mode 100644
index 000000000..dc855cc36
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Users/UserCreatedLogger.cs
@@ -0,0 +1,43 @@
+using System.Globalization;
+using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Events.Users;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Model.Activity;
+using MediaBrowser.Model.Globalization;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.Users
+{
+ /// <summary>
+ /// Creates an entry in the activity log when a user is created.
+ /// </summary>
+ public class UserCreatedLogger : IEventConsumer<UserCreatedEventArgs>
+ {
+ private readonly ILocalizationManager _localizationManager;
+ private readonly IActivityManager _activityManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UserCreatedLogger"/> class.
+ /// </summary>
+ /// <param name="localizationManager">The localization manager.</param>
+ /// <param name="activityManager">The activity manager.</param>
+ public UserCreatedLogger(ILocalizationManager localizationManager, IActivityManager activityManager)
+ {
+ _localizationManager = localizationManager;
+ _activityManager = activityManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(UserCreatedEventArgs eventArgs)
+ {
+ await _activityManager.CreateAsync(new ActivityLog(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("UserCreatedWithName"),
+ eventArgs.Argument.Username),
+ "UserCreated",
+ eventArgs.Argument.Id))
+ .ConfigureAwait(false);
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Users/UserDeletedLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Users/UserDeletedLogger.cs
new file mode 100644
index 000000000..c68a62c81
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Users/UserDeletedLogger.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Globalization;
+using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Events.Users;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Model.Activity;
+using MediaBrowser.Model.Globalization;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.Users
+{
+ /// <summary>
+ /// Adds an entry to the activity log when a user is deleted.
+ /// </summary>
+ public class UserDeletedLogger : IEventConsumer<UserDeletedEventArgs>
+ {
+ private readonly ILocalizationManager _localizationManager;
+ private readonly IActivityManager _activityManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UserDeletedLogger"/> class.
+ /// </summary>
+ /// <param name="localizationManager">The localization manager.</param>
+ /// <param name="activityManager">The activity manager.</param>
+ public UserDeletedLogger(ILocalizationManager localizationManager, IActivityManager activityManager)
+ {
+ _localizationManager = localizationManager;
+ _activityManager = activityManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(UserDeletedEventArgs eventArgs)
+ {
+ await _activityManager.CreateAsync(new ActivityLog(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("UserDeletedWithName"),
+ eventArgs.Argument.Username),
+ "UserDeleted",
+ Guid.Empty))
+ .ConfigureAwait(false);
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Users/UserDeletedNotifier.cs b/Jellyfin.Server.Implementations/Events/Consumers/Users/UserDeletedNotifier.cs
new file mode 100644
index 000000000..10367a939
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Users/UserDeletedNotifier.cs
@@ -0,0 +1,38 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Threading;
+using System.Threading.Tasks;
+using Jellyfin.Data.Events.Users;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Controller.Session;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.Users
+{
+ /// <summary>
+ /// Notifies the user's sessions when a user is deleted.
+ /// </summary>
+ public class UserDeletedNotifier : IEventConsumer<UserDeletedEventArgs>
+ {
+ private readonly ISessionManager _sessionManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UserDeletedNotifier"/> class.
+ /// </summary>
+ /// <param name="sessionManager">The session manager.</param>
+ public UserDeletedNotifier(ISessionManager sessionManager)
+ {
+ _sessionManager = sessionManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(UserDeletedEventArgs eventArgs)
+ {
+ await _sessionManager.SendMessageToUserSessions(
+ new List<Guid> { eventArgs.Argument.Id },
+ "UserDeleted",
+ eventArgs.Argument.Id.ToString("N", CultureInfo.InvariantCulture),
+ CancellationToken.None).ConfigureAwait(false);
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Users/UserLockedOutLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Users/UserLockedOutLogger.cs
new file mode 100644
index 000000000..a31f222ee
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Users/UserLockedOutLogger.cs
@@ -0,0 +1,47 @@
+using System.Globalization;
+using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Events.Users;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Model.Activity;
+using MediaBrowser.Model.Globalization;
+using MediaBrowser.Model.Notifications;
+using Microsoft.Extensions.Logging;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.Users
+{
+ /// <summary>
+ /// Creates an entry in the activity log when a user is locked out.
+ /// </summary>
+ public class UserLockedOutLogger : IEventConsumer<UserLockedOutEventArgs>
+ {
+ private readonly ILocalizationManager _localizationManager;
+ private readonly IActivityManager _activityManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UserLockedOutLogger"/> class.
+ /// </summary>
+ /// <param name="localizationManager">The localization manager.</param>
+ /// <param name="activityManager">The activity manager.</param>
+ public UserLockedOutLogger(ILocalizationManager localizationManager, IActivityManager activityManager)
+ {
+ _localizationManager = localizationManager;
+ _activityManager = activityManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(UserLockedOutEventArgs eventArgs)
+ {
+ await _activityManager.CreateAsync(new ActivityLog(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("UserLockedOutWithName"),
+ eventArgs.Argument.Username),
+ NotificationType.UserLockedOut.ToString(),
+ eventArgs.Argument.Id)
+ {
+ LogSeverity = LogLevel.Error
+ }).ConfigureAwait(false);
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Users/UserPasswordChangedLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Users/UserPasswordChangedLogger.cs
new file mode 100644
index 000000000..dc8ecbf48
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Users/UserPasswordChangedLogger.cs
@@ -0,0 +1,43 @@
+using System.Globalization;
+using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Events.Users;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Model.Activity;
+using MediaBrowser.Model.Globalization;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.Users
+{
+ /// <summary>
+ /// Creates an entry in the activity log when a user's password is changed.
+ /// </summary>
+ public class UserPasswordChangedLogger : IEventConsumer<UserPasswordChangedEventArgs>
+ {
+ private readonly ILocalizationManager _localizationManager;
+ private readonly IActivityManager _activityManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UserPasswordChangedLogger"/> class.
+ /// </summary>
+ /// <param name="localizationManager">The localization manager.</param>
+ /// <param name="activityManager">The activity manager.</param>
+ public UserPasswordChangedLogger(ILocalizationManager localizationManager, IActivityManager activityManager)
+ {
+ _localizationManager = localizationManager;
+ _activityManager = activityManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(UserPasswordChangedEventArgs eventArgs)
+ {
+ await _activityManager.CreateAsync(new ActivityLog(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("UserPasswordChangedWithName"),
+ eventArgs.Argument.Username),
+ "UserPasswordChanged",
+ eventArgs.Argument.Id))
+ .ConfigureAwait(false);
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Users/UserUpdatedNotifier.cs b/Jellyfin.Server.Implementations/Events/Consumers/Users/UserUpdatedNotifier.cs
new file mode 100644
index 000000000..6081dd044
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Users/UserUpdatedNotifier.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using Jellyfin.Data.Events.Users;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Session;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.Users
+{
+ /// <summary>
+ /// Notifies a user when their account has been updated.
+ /// </summary>
+ public class UserUpdatedNotifier : IEventConsumer<UserUpdatedEventArgs>
+ {
+ private readonly IUserManager _userManager;
+ private readonly ISessionManager _sessionManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UserUpdatedNotifier"/> class.
+ /// </summary>
+ /// <param name="userManager">The user manager.</param>
+ /// <param name="sessionManager">The session manager.</param>
+ public UserUpdatedNotifier(IUserManager userManager, ISessionManager sessionManager)
+ {
+ _userManager = userManager;
+ _sessionManager = sessionManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(UserUpdatedEventArgs e)
+ {
+ await _sessionManager.SendMessageToUserSessions(
+ new List<Guid> { e.Argument.Id },
+ "UserUpdated",
+ _userManager.GetUserDto(e.Argument),
+ CancellationToken.None).ConfigureAwait(false);
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/EventManager.cs b/Jellyfin.Server.Implementations/Events/EventManager.cs
new file mode 100644
index 000000000..707002442
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/EventManager.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Threading.Tasks;
+using MediaBrowser.Controller;
+using MediaBrowser.Controller.Events;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+
+namespace Jellyfin.Server.Implementations.Events
+{
+ /// <summary>
+ /// Handles the firing of events.
+ /// </summary>
+ public class EventManager : IEventManager
+ {
+ private readonly ILogger<EventManager> _logger;
+ private readonly IServerApplicationHost _appHost;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="EventManager"/> class.
+ /// </summary>
+ /// <param name="logger">The logger.</param>
+ /// <param name="appHost">The application host.</param>
+ public EventManager(ILogger<EventManager> logger, IServerApplicationHost appHost)
+ {
+ _logger = logger;
+ _appHost = appHost;
+ }
+
+ /// <inheritdoc />
+ public void Publish<T>(T eventArgs)
+ where T : EventArgs
+ {
+ Task.WaitAll(PublishInternal(eventArgs));
+ }
+
+ /// <inheritdoc />
+ public async Task PublishAsync<T>(T eventArgs)
+ where T : EventArgs
+ {
+ await PublishInternal(eventArgs).ConfigureAwait(false);
+ }
+
+ private async Task PublishInternal<T>(T eventArgs)
+ where T : EventArgs
+ {
+ using var scope = _appHost.ServiceProvider.CreateScope();
+ foreach (var service in scope.ServiceProvider.GetServices<IEventConsumer<T>>())
+ {
+ try
+ {
+ await service.OnEvent(eventArgs).ConfigureAwait(false);
+ }
+ catch (Exception e)
+ {
+ _logger.LogError(e, "Uncaught exception in EventConsumer {type}: ", service.GetType());
+ }
+ }
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/EventingServiceCollectionExtensions.cs b/Jellyfin.Server.Implementations/Events/EventingServiceCollectionExtensions.cs
new file mode 100644
index 000000000..5d558189b
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/EventingServiceCollectionExtensions.cs
@@ -0,0 +1,72 @@
+using Jellyfin.Data.Events;
+using Jellyfin.Data.Events.System;
+using Jellyfin.Data.Events.Users;
+using Jellyfin.Server.Implementations.Events.Consumers.Library;
+using Jellyfin.Server.Implementations.Events.Consumers.Security;
+using Jellyfin.Server.Implementations.Events.Consumers.Session;
+using Jellyfin.Server.Implementations.Events.Consumers.System;
+using Jellyfin.Server.Implementations.Events.Consumers.Updates;
+using Jellyfin.Server.Implementations.Events.Consumers.Users;
+using MediaBrowser.Common.Updates;
+using MediaBrowser.Controller.Authentication;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Controller.Events.Session;
+using MediaBrowser.Controller.Events.Updates;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Session;
+using MediaBrowser.Controller.Subtitles;
+using MediaBrowser.Model.Tasks;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Jellyfin.Server.Implementations.Events
+{
+ /// <summary>
+ /// A class containing extensions to <see cref="IServiceCollection"/> for eventing.
+ /// </summary>
+ public static class EventingServiceCollectionExtensions
+ {
+ /// <summary>
+ /// Adds the event services to the service collection.
+ /// </summary>
+ /// <param name="collection">The service collection.</param>
+ public static void AddEventServices(this IServiceCollection collection)
+ {
+ // Library consumers
+ collection.AddScoped<IEventConsumer<SubtitleDownloadFailureEventArgs>, SubtitleDownloadFailureLogger>();
+
+ // Security consumers
+ collection.AddScoped<IEventConsumer<GenericEventArgs<AuthenticationRequest>>, AuthenticationFailedLogger>();
+ collection.AddScoped<IEventConsumer<GenericEventArgs<AuthenticationResult>>, AuthenticationSucceededLogger>();
+
+ // Session consumers
+ collection.AddScoped<IEventConsumer<PlaybackStartEventArgs>, PlaybackStartLogger>();
+ collection.AddScoped<IEventConsumer<PlaybackStopEventArgs>, PlaybackStopLogger>();
+ collection.AddScoped<IEventConsumer<SessionEndedEventArgs>, SessionEndedLogger>();
+ collection.AddScoped<IEventConsumer<SessionStartedEventArgs>, SessionStartedLogger>();
+
+ // System consumers
+ collection.AddScoped<IEventConsumer<PendingRestartEventArgs>, PendingRestartNotifier>();
+ collection.AddScoped<IEventConsumer<TaskCompletionEventArgs>, TaskCompletedLogger>();
+ collection.AddScoped<IEventConsumer<TaskCompletionEventArgs>, TaskCompletedNotifier>();
+
+ // Update consumers
+ collection.AddScoped<IEventConsumer<PluginInstallationCancelledEventArgs>, PluginInstallationCancelledNotifier>();
+ collection.AddScoped<IEventConsumer<InstallationFailedEventArgs>, PluginInstallationFailedLogger>();
+ collection.AddScoped<IEventConsumer<InstallationFailedEventArgs>, PluginInstallationFailedNotifier>();
+ collection.AddScoped<IEventConsumer<PluginInstalledEventArgs>, PluginInstalledLogger>();
+ collection.AddScoped<IEventConsumer<PluginInstalledEventArgs>, PluginInstalledNotifier>();
+ collection.AddScoped<IEventConsumer<PluginInstallingEventArgs>, PluginInstallingNotifier>();
+ collection.AddScoped<IEventConsumer<PluginUninstalledEventArgs>, PluginUninstalledLogger>();
+ collection.AddScoped<IEventConsumer<PluginUninstalledEventArgs>, PluginUninstalledNotifier>();
+ collection.AddScoped<IEventConsumer<PluginUpdatedEventArgs>, PluginUpdatedLogger>();
+
+ // User consumers
+ collection.AddScoped<IEventConsumer<UserCreatedEventArgs>, UserCreatedLogger>();
+ collection.AddScoped<IEventConsumer<UserDeletedEventArgs>, UserDeletedLogger>();
+ collection.AddScoped<IEventConsumer<UserDeletedEventArgs>, UserDeletedNotifier>();
+ collection.AddScoped<IEventConsumer<UserLockedOutEventArgs>, UserLockedOutLogger>();
+ collection.AddScoped<IEventConsumer<UserPasswordChangedEventArgs>, UserPasswordChangedLogger>();
+ collection.AddScoped<IEventConsumer<UserUpdatedEventArgs>, UserUpdatedNotifier>();
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/JellyfinDb.cs b/Jellyfin.Server.Implementations/JellyfinDb.cs
index 7d864ebc6..08e4db388 100644
--- a/Jellyfin.Server.Implementations/JellyfinDb.cs
+++ b/Jellyfin.Server.Implementations/JellyfinDb.cs
@@ -2,8 +2,8 @@
using System;
using System.Linq;
-using Jellyfin.Data;
using Jellyfin.Data.Entities;
+using Jellyfin.Data.Interfaces;
using Microsoft.EntityFrameworkCore;
namespace Jellyfin.Server.Implementations
@@ -130,7 +130,7 @@ namespace Jellyfin.Server.Implementations
foreach (var saveEntity in ChangeTracker.Entries()
.Where(e => e.State == EntityState.Modified)
.Select(entry => entry.Entity)
- .OfType<ISavingChanges>())
+ .OfType<IHasConcurrencyToken>())
{
saveEntity.OnSavingChanges();
}
diff --git a/Jellyfin.Server.Implementations/Users/DeviceAccessEntryPoint.cs b/Jellyfin.Server.Implementations/Users/DeviceAccessEntryPoint.cs
index 140853e52..1fb89c4a6 100644
--- a/Jellyfin.Server.Implementations/Users/DeviceAccessEntryPoint.cs
+++ b/Jellyfin.Server.Implementations/Users/DeviceAccessEntryPoint.cs
@@ -4,12 +4,12 @@
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
+using Jellyfin.Data.Events;
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
{
diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs
index 11402ee05..8f04baa08 100644
--- a/Jellyfin.Server.Implementations/Users/UserManager.cs
+++ b/Jellyfin.Server.Implementations/Users/UserManager.cs
@@ -10,18 +10,20 @@ using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
+using Jellyfin.Data.Events;
+using Jellyfin.Data.Events.Users;
using MediaBrowser.Common;
using MediaBrowser.Common.Cryptography;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.Drawing;
+using MediaBrowser.Controller.Events;
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.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
@@ -34,6 +36,7 @@ namespace Jellyfin.Server.Implementations.Users
public class UserManager : IUserManager
{
private readonly JellyfinDbProvider _dbProvider;
+ private readonly IEventManager _eventManager;
private readonly ICryptoProvider _cryptoProvider;
private readonly INetworkManager _networkManager;
private readonly IApplicationHost _appHost;
@@ -49,6 +52,7 @@ namespace Jellyfin.Server.Implementations.Users
/// Initializes a new instance of the <see cref="UserManager"/> class.
/// </summary>
/// <param name="dbProvider">The database provider.</param>
+ /// <param name="eventManager">The event manager.</param>
/// <param name="cryptoProvider">The cryptography provider.</param>
/// <param name="networkManager">The network manager.</param>
/// <param name="appHost">The application host.</param>
@@ -56,6 +60,7 @@ namespace Jellyfin.Server.Implementations.Users
/// <param name="logger">The logger.</param>
public UserManager(
JellyfinDbProvider dbProvider,
+ IEventManager eventManager,
ICryptoProvider cryptoProvider,
INetworkManager networkManager,
IApplicationHost appHost,
@@ -63,6 +68,7 @@ namespace Jellyfin.Server.Implementations.Users
ILogger<UserManager> logger)
{
_dbProvider = dbProvider;
+ _eventManager = eventManager;
_cryptoProvider = cryptoProvider;
_networkManager = networkManager;
_appHost = appHost;
@@ -78,21 +84,9 @@ namespace Jellyfin.Server.Implementations.Users
}
/// <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
{
get
@@ -234,7 +228,7 @@ namespace Jellyfin.Server.Implementations.Users
dbContext.Users.Add(newUser);
await dbContext.SaveChangesAsync().ConfigureAwait(false);
- OnUserCreated?.Invoke(this, new GenericEventArgs<User>(newUser));
+ await _eventManager.PublishAsync(new UserCreatedEventArgs(newUser)).ConfigureAwait(false);
return newUser;
}
@@ -293,7 +287,8 @@ namespace Jellyfin.Server.Implementations.Users
dbContext.RemoveRange(user.AccessSchedules);
dbContext.Users.Remove(user);
dbContext.SaveChanges();
- OnUserDeleted?.Invoke(this, new GenericEventArgs<User>(user));
+
+ _eventManager.Publish(new UserDeletedEventArgs(user));
}
/// <inheritdoc/>
@@ -319,7 +314,7 @@ namespace Jellyfin.Server.Implementations.Users
await GetAuthenticationProvider(user).ChangePassword(user, newPassword).ConfigureAwait(false);
await UpdateUserAsync(user).ConfigureAwait(false);
- OnUserPasswordChanged?.Invoke(this, new GenericEventArgs<User>(user));
+ await _eventManager.PublishAsync(new UserPasswordChangedEventArgs(user)).ConfigureAwait(false);
}
/// <inheritdoc/>
@@ -338,7 +333,7 @@ namespace Jellyfin.Server.Implementations.Users
user.EasyPassword = newPasswordSha1;
UpdateUser(user);
- OnUserPasswordChanged?.Invoke(this, new GenericEventArgs<User>(user));
+ _eventManager.Publish(new UserPasswordChangedEventArgs(user));
}
/// <inheritdoc/>
@@ -407,13 +402,13 @@ namespace Jellyfin.Server.Implementations.Users
EnablePublicSharing = user.HasPermission(PermissionKind.EnablePublicSharing),
AccessSchedules = user.AccessSchedules.ToArray(),
BlockedTags = user.GetPreference(PreferenceKind.BlockedTags),
- EnabledChannels = user.GetPreference(PreferenceKind.EnabledChannels),
+ EnabledChannels = user.GetPreference(PreferenceKind.EnabledChannels)?.Select(Guid.Parse).ToArray(),
EnabledDevices = user.GetPreference(PreferenceKind.EnabledDevices),
- EnabledFolders = user.GetPreference(PreferenceKind.EnabledFolders),
+ EnabledFolders = user.GetPreference(PreferenceKind.EnabledFolders)?.Select(Guid.Parse).ToArray(),
EnableContentDeletionFromFolders = user.GetPreference(PreferenceKind.EnableContentDeletionFromFolders),
SyncPlayAccess = user.SyncPlayAccess,
- BlockedChannels = user.GetPreference(PreferenceKind.BlockedChannels),
- BlockedMediaFolders = user.GetPreference(PreferenceKind.BlockedMediaFolders),
+ BlockedChannels = user.GetPreference(PreferenceKind.BlockedChannels)?.Select(Guid.Parse).ToArray(),
+ BlockedMediaFolders = user.GetPreference(PreferenceKind.BlockedMediaFolders)?.Select(Guid.Parse).ToArray(),
BlockUnratedItems = user.GetPreference(PreferenceKind.BlockUnratedItems).Select(Enum.Parse<UnratedItem>).ToArray()
}
};
@@ -740,9 +735,9 @@ namespace Jellyfin.Server.Implementations.Users
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.EnabledChannels, policy.EnabledChannels?.Select(i => i.ToString("N", CultureInfo.InvariantCulture)).ToArray());
user.SetPreference(PreferenceKind.EnabledDevices, policy.EnabledDevices);
- user.SetPreference(PreferenceKind.EnabledFolders, policy.EnabledFolders);
+ user.SetPreference(PreferenceKind.EnabledFolders, policy.EnabledFolders?.Select(i => i.ToString("N", CultureInfo.InvariantCulture)).ToArray());
user.SetPreference(PreferenceKind.EnableContentDeletionFromFolders, policy.EnableContentDeletionFromFolders);
dbContext.Update(user);
@@ -901,7 +896,7 @@ namespace Jellyfin.Server.Implementations.Users
if (maxInvalidLogins.HasValue && user.InvalidLoginAttemptCount >= maxInvalidLogins)
{
user.SetPermission(PermissionKind.IsDisabled, true);
- OnUserLockedOut?.Invoke(this, new GenericEventArgs<User>(user));
+ await _eventManager.PublishAsync(new UserLockedOutEventArgs(user)).ConfigureAwait(false);
_logger.LogWarning(
"Disabling user {Username} due to {Attempts} unsuccessful login attempts.",
user.Username,
diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs
index 29a59e1c8..755844dd9 100644
--- a/Jellyfin.Server/CoreAppHost.cs
+++ b/Jellyfin.Server/CoreAppHost.cs
@@ -1,20 +1,20 @@
using System;
using System.Collections.Generic;
-using System.IO;
using System.Reflection;
using Emby.Drawing;
using Emby.Server.Implementations;
using Jellyfin.Drawing.Skia;
using Jellyfin.Server.Implementations;
using Jellyfin.Server.Implementations.Activity;
+using Jellyfin.Server.Implementations.Events;
using Jellyfin.Server.Implementations.Users;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Drawing;
+using MediaBrowser.Controller.Events;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Activity;
using MediaBrowser.Model.IO;
-using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
@@ -33,30 +33,33 @@ namespace Jellyfin.Server
/// <param name="options">The <see cref="StartupOptions" /> to be used by the <see cref="CoreAppHost" />.</param>
/// <param name="fileSystem">The <see cref="IFileSystem" /> to be used by the <see cref="CoreAppHost" />.</param>
/// <param name="networkManager">The <see cref="INetworkManager" /> to be used by the <see cref="CoreAppHost" />.</param>
+ /// <param name="collection">The <see cref="IServiceCollection"/> to be used by the <see cref="CoreAppHost"/>.</param>
public CoreAppHost(
IServerApplicationPaths applicationPaths,
ILoggerFactory loggerFactory,
IStartupOptions options,
IFileSystem fileSystem,
- INetworkManager networkManager)
+ INetworkManager networkManager,
+ IServiceCollection collection)
: base(
applicationPaths,
loggerFactory,
options,
fileSystem,
- networkManager)
+ networkManager,
+ collection)
{
}
/// <inheritdoc/>
- protected override void RegisterServices(IServiceCollection serviceCollection)
+ protected override void RegisterServices()
{
// Register an image encoder
bool useSkiaEncoder = SkiaEncoder.IsNativeLibAvailable();
Type imageEncoderType = useSkiaEncoder
? typeof(SkiaEncoder)
: typeof(NullImageEncoder);
- serviceCollection.AddSingleton(typeof(IImageEncoder), imageEncoderType);
+ ServiceCollection.AddSingleton(typeof(IImageEncoder), imageEncoderType);
// Log a warning if the Skia encoder could not be used
if (!useSkiaEncoder)
@@ -71,13 +74,15 @@ namespace Jellyfin.Server
// .UseSqlite($"Filename={Path.Combine(ApplicationPaths.DataPath, "jellyfin.db")}"),
// ServiceLifetime.Transient);
- serviceCollection.AddSingleton<JellyfinDbProvider>();
+ ServiceCollection.AddEventServices();
+ ServiceCollection.AddSingleton<IEventManager, EventManager>();
+ ServiceCollection.AddSingleton<JellyfinDbProvider>();
- serviceCollection.AddSingleton<IActivityManager, ActivityManager>();
- serviceCollection.AddSingleton<IUserManager, UserManager>();
- serviceCollection.AddSingleton<IDisplayPreferencesManager, DisplayPreferencesManager>();
+ ServiceCollection.AddSingleton<IActivityManager, ActivityManager>();
+ ServiceCollection.AddSingleton<IUserManager, UserManager>();
+ ServiceCollection.AddSingleton<IDisplayPreferencesManager, DisplayPreferencesManager>();
- base.RegisterServices(serviceCollection);
+ base.RegisterServices();
}
/// <inheritdoc />
diff --git a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs
index 2e2bfea68..0160a05f9 100644
--- a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs
+++ b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs
@@ -18,6 +18,7 @@ using Jellyfin.Api.Constants;
using Jellyfin.Api.Controllers;
using Jellyfin.Server.Formatters;
using Jellyfin.Server.Models;
+using MediaBrowser.Common;
using MediaBrowser.Common.Json;
using MediaBrowser.Model.Entities;
using Microsoft.AspNetCore.Authentication;
@@ -135,10 +136,11 @@ namespace Jellyfin.Server.Extensions
/// </summary>
/// <param name="serviceCollection">The service collection.</param>
/// <param name="baseUrl">The base url for the API.</param>
+ /// <param name="pluginAssemblies">An IEnumberable containing all plugin assemblies with API controllers.</param>
/// <returns>The MVC builder.</returns>
- public static IMvcBuilder AddJellyfinApi(this IServiceCollection serviceCollection, string baseUrl)
+ public static IMvcBuilder AddJellyfinApi(this IServiceCollection serviceCollection, string baseUrl, IEnumerable<Assembly> pluginAssemblies)
{
- return serviceCollection
+ IMvcBuilder mvcBuilder = serviceCollection
.AddCors(options =>
{
options.AddPolicy(ServerCorsPolicy.DefaultPolicyName, ServerCorsPolicy.DefaultPolicy);
@@ -168,6 +170,9 @@ namespace Jellyfin.Server.Extensions
// From JsonDefaults
options.JsonSerializerOptions.ReadCommentHandling = jsonOptions.ReadCommentHandling;
options.JsonSerializerOptions.WriteIndented = jsonOptions.WriteIndented;
+ options.JsonSerializerOptions.DefaultIgnoreCondition = jsonOptions.DefaultIgnoreCondition;
+ options.JsonSerializerOptions.NumberHandling = jsonOptions.NumberHandling;
+
options.JsonSerializerOptions.Converters.Clear();
foreach (var converter in jsonOptions.Converters)
{
@@ -176,8 +181,14 @@ namespace Jellyfin.Server.Extensions
// From JsonDefaults.PascalCase
options.JsonSerializerOptions.PropertyNamingPolicy = jsonOptions.PropertyNamingPolicy;
- })
- .AddControllersAsServices();
+ });
+
+ foreach (Assembly pluginAssembly in pluginAssemblies)
+ {
+ mvcBuilder.AddApplicationPart(pluginAssembly);
+ }
+
+ return mvcBuilder.AddControllersAsServices();
}
/// <summary>
@@ -194,7 +205,7 @@ namespace Jellyfin.Server.Extensions
{
Type = SecuritySchemeType.ApiKey,
In = ParameterLocation.Header,
- Name = "X-Emby-Token",
+ Name = "X-Emby-Authorization",
Description = "API key header parameter"
});
diff --git a/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs
index b15ccf01e..7f57358ec 100644
--- a/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs
+++ b/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs
@@ -81,6 +81,11 @@ namespace Jellyfin.Server.Migrations.Routines
foreach (var result in results)
{
var dto = JsonSerializer.Deserialize<DisplayPreferencesDto>(result[3].ToString(), _jsonOptions);
+ if (dto == null)
+ {
+ continue;
+ }
+
var chromecastVersion = dto.CustomPrefs.TryGetValue("chromecastVersion", out var version)
? chromecastDict[version]
: ChromecastVersion.Stable;
diff --git a/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs
index 274e6ab73..74c550331 100644
--- a/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs
+++ b/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs
@@ -1,5 +1,7 @@
using System;
+using System.Globalization;
using System.IO;
+using System.Linq;
using Emby.Server.Implementations.Data;
using Emby.Server.Implementations.Serialization;
using Jellyfin.Data.Entities;
@@ -74,7 +76,12 @@ namespace Jellyfin.Server.Migrations.Routines
foreach (var entry in queryResult)
{
- UserMockup mockup = JsonSerializer.Deserialize<UserMockup>(entry[2].ToBlob(), JsonDefaults.GetOptions());
+ UserMockup? mockup = JsonSerializer.Deserialize<UserMockup>(entry[2].ToBlob(), JsonDefaults.GetOptions());
+ if (mockup == null)
+ {
+ continue;
+ }
+
var userDataDir = Path.Combine(_paths.UserConfigurationDirectoryPath, mockup.Name);
var config = File.Exists(Path.Combine(userDataDir, "config.xml"))
@@ -161,9 +168,9 @@ namespace Jellyfin.Server.Migrations.Routines
}
user.SetPreference(PreferenceKind.BlockedTags, policy.BlockedTags);
- user.SetPreference(PreferenceKind.EnabledChannels, policy.EnabledChannels);
+ user.SetPreference(PreferenceKind.EnabledChannels, policy.EnabledChannels?.Select(i => i.ToString("N", CultureInfo.InvariantCulture)).ToArray());
user.SetPreference(PreferenceKind.EnabledDevices, policy.EnabledDevices);
- user.SetPreference(PreferenceKind.EnabledFolders, policy.EnabledFolders);
+ user.SetPreference(PreferenceKind.EnabledFolders, policy.EnabledFolders?.Select(i => i.ToString("N", CultureInfo.InvariantCulture)).ToArray());
user.SetPreference(PreferenceKind.EnableContentDeletionFromFolders, policy.EnableContentDeletionFromFolders);
user.SetPreference(PreferenceKind.OrderedViews, config.OrderedViews);
user.SetPreference(PreferenceKind.GroupedFolders, config.GroupedFolders);
diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs
index f6ac4e2a3..14cc5f4c2 100644
--- a/Jellyfin.Server/Program.cs
+++ b/Jellyfin.Server/Program.cs
@@ -154,13 +154,15 @@ namespace Jellyfin.Server
ApplicationHost.LogEnvironmentInfo(_logger, appPaths);
PerformStaticInitialization();
+ var serviceCollection = new ServiceCollection();
var appHost = new CoreAppHost(
appPaths,
_loggerFactory,
options,
new ManagedFileSystem(_loggerFactory.CreateLogger<ManagedFileSystem>(), appPaths),
- new NetworkManager(_loggerFactory.CreateLogger<NetworkManager>()));
+ new NetworkManager(_loggerFactory.CreateLogger<NetworkManager>()),
+ serviceCollection);
try
{
@@ -178,8 +180,7 @@ namespace Jellyfin.Server
}
}
- ServiceCollection serviceCollection = new ServiceCollection();
- appHost.Init(serviceCollection);
+ appHost.Init();
var webHost = new WebHostBuilder().ConfigureWebHostBuilder(appHost, serviceCollection, options, startupConfig, appPaths).Build();
diff --git a/Jellyfin.Server/Startup.cs b/Jellyfin.Server/Startup.cs
index f50e5716d..cbc1c040c 100644
--- a/Jellyfin.Server/Startup.cs
+++ b/Jellyfin.Server/Startup.cs
@@ -44,7 +44,7 @@ namespace Jellyfin.Server
{
services.AddResponseCompression();
services.AddHttpContextAccessor();
- services.AddJellyfinApi(_serverConfigurationManager.Configuration.BaseUrl.TrimStart('/'));
+ services.AddJellyfinApi(_serverConfigurationManager.Configuration.BaseUrl.TrimStart('/'), _applicationHost.GetApiPluginAssemblies());
services.AddJellyfinApiSwagger();
diff --git a/MediaBrowser.Common/IApplicationHost.cs b/MediaBrowser.Common/IApplicationHost.cs
index e8d9282e4..849037ac4 100644
--- a/MediaBrowser.Common/IApplicationHost.cs
+++ b/MediaBrowser.Common/IApplicationHost.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Reflection;
using System.Threading.Tasks;
using MediaBrowser.Common.Plugins;
using Microsoft.Extensions.DependencyInjection;
@@ -77,6 +78,12 @@ namespace MediaBrowser.Common
IReadOnlyList<IPlugin> Plugins { get; }
/// <summary>
+ /// Gets all plugin assemblies which implement a custom rest api.
+ /// </summary>
+ /// <returns>An <see cref="IEnumerable{Assembly}"/> containing the plugin assemblies.</returns>
+ IEnumerable<Assembly> GetApiPluginAssemblies();
+
+ /// <summary>
/// Notifies the pending restart.
/// </summary>
void NotifyPendingRestart();
@@ -116,8 +123,7 @@ namespace MediaBrowser.Common
/// <summary>
/// Initializes this instance.
/// </summary>
- /// <param name="serviceCollection">The service collection.</param>
- void Init(IServiceCollection serviceCollection);
+ void Init();
/// <summary>
/// Creates the instance.
diff --git a/MediaBrowser.Common/Json/Converters/JsonDoubleConverter.cs b/MediaBrowser.Common/Json/Converters/JsonDoubleConverter.cs
deleted file mode 100644
index 56c0ecbe9..000000000
--- a/MediaBrowser.Common/Json/Converters/JsonDoubleConverter.cs
+++ /dev/null
@@ -1,56 +0,0 @@
-using System;
-using System.Buffers;
-using System.Buffers.Text;
-using System.Globalization;
-using System.Text.Json;
-using System.Text.Json.Serialization;
-
-namespace MediaBrowser.Common.Json.Converters
-{
- /// <summary>
- /// Double to String JSON converter.
- /// Web client send quoted doubles.
- /// </summary>
- public class JsonDoubleConverter : JsonConverter<double>
- {
- /// <summary>
- /// Read JSON string as double.
- /// </summary>
- /// <param name="reader"><see cref="Utf8JsonReader"/>.</param>
- /// <param name="typeToConvert">Type.</param>
- /// <param name="options">Options.</param>
- /// <returns>Parsed value.</returns>
- public override double Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
- {
- if (reader.TokenType == JsonTokenType.String)
- {
- // try to parse number directly from bytes
- var span = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan;
- if (Utf8Parser.TryParse(span, out double number, out var bytesConsumed) && span.Length == bytesConsumed)
- {
- return number;
- }
-
- // try to parse from a string if the above failed, this covers cases with other escaped/UTF characters
- if (double.TryParse(reader.GetString(), out number))
- {
- return number;
- }
- }
-
- // fallback to default handling
- return reader.GetDouble();
- }
-
- /// <summary>
- /// Write double to JSON string.
- /// </summary>
- /// <param name="writer"><see cref="Utf8JsonWriter"/>.</param>
- /// <param name="value">Value to write.</param>
- /// <param name="options">Options.</param>
- public override void Write(Utf8JsonWriter writer, double value, JsonSerializerOptions options)
- {
- writer.WriteNumberValue(value);
- }
- }
-}
diff --git a/MediaBrowser.Common/Json/Converters/JsonInt32Converter.cs b/MediaBrowser.Common/Json/Converters/JsonInt32Converter.cs
deleted file mode 100644
index 70c375b8c..000000000
--- a/MediaBrowser.Common/Json/Converters/JsonInt32Converter.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-using System;
-using System.Buffers;
-using System.Buffers.Text;
-using System.Text.Json;
-using System.Text.Json.Serialization;
-
-namespace MediaBrowser.Common.Json.Converters
-{
- /// <summary>
- /// Converts a GUID object or value to/from JSON.
- /// </summary>
- public class JsonInt32Converter : JsonConverter<int>
- {
- /// <inheritdoc />
- public override int Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
- {
- if (reader.TokenType == JsonTokenType.String)
- {
- ReadOnlySpan<byte> span = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan;
- if (Utf8Parser.TryParse(span, out int number, out int bytesConsumed) && span.Length == bytesConsumed)
- {
- return number;
- }
-
- if (int.TryParse(reader.GetString(), out number))
- {
- return number;
- }
- }
-
- return reader.GetInt32();
- }
-
- /// <inheritdoc />
- public override void Write(Utf8JsonWriter writer, int value, JsonSerializerOptions options)
- {
- writer.WriteNumberValue(value);
- }
- }
-}
diff --git a/MediaBrowser.Common/Json/Converters/JsonInt64Converter.cs b/MediaBrowser.Common/Json/Converters/JsonInt64Converter.cs
deleted file mode 100644
index 427f1fa7e..000000000
--- a/MediaBrowser.Common/Json/Converters/JsonInt64Converter.cs
+++ /dev/null
@@ -1,56 +0,0 @@
-using System;
-using System.Buffers;
-using System.Buffers.Text;
-using System.Globalization;
-using System.Text.Json;
-using System.Text.Json.Serialization;
-
-namespace MediaBrowser.Common.Json.Converters
-{
- /// <summary>
- /// Parse JSON string as long.
- /// Javascript does not support 64-bit integers.
- /// </summary>
- public class JsonInt64Converter : JsonConverter<long>
- {
- /// <summary>
- /// Read JSON string as int64.
- /// </summary>
- /// <param name="reader"><see cref="Utf8JsonReader"/>.</param>
- /// <param name="type">Type.</param>
- /// <param name="options">Options.</param>
- /// <returns>Parsed value.</returns>
- public override long Read(ref Utf8JsonReader reader, Type type, JsonSerializerOptions options)
- {
- if (reader.TokenType == JsonTokenType.String)
- {
- // try to parse number directly from bytes
- var span = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan;
- if (Utf8Parser.TryParse(span, out long number, out var bytesConsumed) && span.Length == bytesConsumed)
- {
- return number;
- }
-
- // try to parse from a string if the above failed, this covers cases with other escaped/UTF characters
- if (long.TryParse(reader.GetString(), out number))
- {
- return number;
- }
- }
-
- // fallback to default handling
- return reader.GetInt64();
- }
-
- /// <summary>
- /// Write long to JSON long.
- /// </summary>
- /// <param name="writer"><see cref="Utf8JsonWriter"/>.</param>
- /// <param name="value">Value to write.</param>
- /// <param name="options">Options.</param>
- public override void Write(Utf8JsonWriter writer, long value, JsonSerializerOptions options)
- {
- writer.WriteNumberValue(value);
- }
- }
-}
diff --git a/MediaBrowser.Common/Json/Converters/JsonNonStringKeyDictionaryConverter.cs b/MediaBrowser.Common/Json/Converters/JsonNonStringKeyDictionaryConverter.cs
deleted file mode 100644
index 8053461f0..000000000
--- a/MediaBrowser.Common/Json/Converters/JsonNonStringKeyDictionaryConverter.cs
+++ /dev/null
@@ -1,82 +0,0 @@
-#nullable enable
-
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Reflection;
-using System.Text.Json;
-using System.Text.Json.Serialization;
-
-namespace MediaBrowser.Common.Json.Converters
-{
- /// <summary>
- /// Converter for Dictionaries without string key.
- /// TODO This can be removed when System.Text.Json supports Dictionaries with non-string keys.
- /// </summary>
- /// <typeparam name="TKey">Type of key.</typeparam>
- /// <typeparam name="TValue">Type of value.</typeparam>
- internal sealed class JsonNonStringKeyDictionaryConverter<TKey, TValue> : JsonConverter<IDictionary<TKey, TValue>>
- {
- /// <summary>
- /// Read JSON.
- /// </summary>
- /// <param name="reader">The Utf8JsonReader.</param>
- /// <param name="typeToConvert">The type to convert.</param>
- /// <param name="options">The json serializer options.</param>
- /// <returns>Typed dictionary.</returns>
- /// <exception cref="NotSupportedException">Dictionary key type not supported.</exception>
- public override IDictionary<TKey, TValue> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
- {
- var convertedType = typeof(Dictionary<,>).MakeGenericType(typeof(string), typeToConvert.GenericTypeArguments[1]);
- var value = JsonSerializer.Deserialize(ref reader, convertedType, options);
- var instance = (Dictionary<TKey, TValue>)Activator.CreateInstance(
- typeToConvert,
- BindingFlags.Instance | BindingFlags.Public,
- null,
- null,
- CultureInfo.CurrentCulture);
- var enumerator = (IEnumerator)convertedType.GetMethod("GetEnumerator")!.Invoke(value, null);
- var parse = typeof(TKey).GetMethod(
- "Parse",
- 0,
- BindingFlags.Public | BindingFlags.Static,
- null,
- CallingConventions.Any,
- new[] { typeof(string) },
- null);
- if (parse == null)
- {
- throw new NotSupportedException($"{typeof(TKey)} as TKey in IDictionary<TKey, TValue> is not supported.");
- }
-
- while (enumerator.MoveNext())
- {
- var element = (KeyValuePair<string?, TValue>)enumerator.Current;
- instance.Add((TKey)parse.Invoke(null, new[] { (object?)element.Key }), element.Value);
- }
-
- return instance;
- }
-
- /// <summary>
- /// Write dictionary as Json.
- /// </summary>
- /// <param name="writer">The Utf8JsonWriter.</param>
- /// <param name="value">The dictionary value.</param>
- /// <param name="options">The Json serializer options.</param>
- public override void Write(Utf8JsonWriter writer, IDictionary<TKey, TValue> value, JsonSerializerOptions options)
- {
- var convertedDictionary = new Dictionary<string?, TValue>(value.Count);
- foreach (var (k, v) in value)
- {
- if (k != null)
- {
- convertedDictionary[k.ToString()] = v;
- }
- }
-
- JsonSerializer.Serialize(writer, convertedDictionary, options);
- }
- }
-}
diff --git a/MediaBrowser.Common/Json/Converters/JsonNonStringKeyDictionaryConverterFactory.cs b/MediaBrowser.Common/Json/Converters/JsonNonStringKeyDictionaryConverterFactory.cs
deleted file mode 100644
index 52f360740..000000000
--- a/MediaBrowser.Common/Json/Converters/JsonNonStringKeyDictionaryConverterFactory.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-#nullable enable
-
-using System;
-using System.Collections;
-using System.Globalization;
-using System.Reflection;
-using System.Text.Json;
-using System.Text.Json.Serialization;
-
-namespace MediaBrowser.Common.Json.Converters
-{
- /// <summary>
- /// https://github.com/dotnet/runtime/issues/30524#issuecomment-524619972.
- /// TODO This can be removed when System.Text.Json supports Dictionaries with non-string keys.
- /// </summary>
- internal sealed class JsonNonStringKeyDictionaryConverterFactory : JsonConverterFactory
- {
- /// <summary>
- /// Only convert objects that implement IDictionary and do not have string keys.
- /// </summary>
- /// <param name="typeToConvert">Type convert.</param>
- /// <returns>Conversion ability.</returns>
- public override bool CanConvert(Type typeToConvert)
- {
- if (!typeToConvert.IsGenericType)
- {
- return false;
- }
-
- // Let built in converter handle string keys
- if (typeToConvert.GenericTypeArguments[0] == typeof(string))
- {
- return false;
- }
-
- // Only support objects that implement IDictionary
- return typeToConvert.GetInterface(nameof(IDictionary)) != null;
- }
-
- /// <summary>
- /// Create converter for generic dictionary type.
- /// </summary>
- /// <param name="typeToConvert">Type to convert.</param>
- /// <param name="options">Json serializer options.</param>
- /// <returns>JsonConverter for given type.</returns>
- public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
- {
- var converterType = typeof(JsonNonStringKeyDictionaryConverter<,>)
- .MakeGenericType(typeToConvert.GenericTypeArguments[0], typeToConvert.GenericTypeArguments[1]);
- var converter = (JsonConverter)Activator.CreateInstance(
- converterType,
- BindingFlags.Instance | BindingFlags.Public,
- null,
- null,
- CultureInfo.CurrentCulture);
- return converter;
- }
- }
-}
diff --git a/MediaBrowser.Common/Json/JsonDefaults.cs b/MediaBrowser.Common/Json/JsonDefaults.cs
index 36ab6d900..9d30927db 100644
--- a/MediaBrowser.Common/Json/JsonDefaults.cs
+++ b/MediaBrowser.Common/Json/JsonDefaults.cs
@@ -24,15 +24,13 @@ namespace MediaBrowser.Common.Json
var options = new JsonSerializerOptions
{
ReadCommentHandling = JsonCommentHandling.Disallow,
- WriteIndented = false
+ WriteIndented = false,
+ DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
+ NumberHandling = JsonNumberHandling.AllowReadingFromString
};
options.Converters.Add(new JsonGuidConverter());
- options.Converters.Add(new JsonInt32Converter());
options.Converters.Add(new JsonStringEnumConverter());
- options.Converters.Add(new JsonNonStringKeyDictionaryConverterFactory());
- options.Converters.Add(new JsonInt64Converter());
- options.Converters.Add(new JsonDoubleConverter());
return options;
}
diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj
index 7380f39fd..deb674e45 100644
--- a/MediaBrowser.Common/MediaBrowser.Common.csproj
+++ b/MediaBrowser.Common/MediaBrowser.Common.csproj
@@ -8,8 +8,9 @@
<PropertyGroup>
<Authors>Jellyfin Contributors</Authors>
<PackageId>Jellyfin.Common</PackageId>
- <PackageLicenseUrl>https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt</PackageLicenseUrl>
+ <VersionPrefix>10.7.0</VersionPrefix>
<RepositoryUrl>https://github.com/jellyfin/jellyfin</RepositoryUrl>
+ <PackageLicenseExpression>GPL-3.0-only</PackageLicenseExpression>
</PropertyGroup>
<ItemGroup>
diff --git a/MediaBrowser.Common/Plugins/BasePlugin.cs b/MediaBrowser.Common/Plugins/BasePlugin.cs
index f10a1918f..4b2918d08 100644
--- a/MediaBrowser.Common/Plugins/BasePlugin.cs
+++ b/MediaBrowser.Common/Plugins/BasePlugin.cs
@@ -6,6 +6,7 @@ using System.Reflection;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.Plugins;
using MediaBrowser.Model.Serialization;
+using Microsoft.Extensions.DependencyInjection;
namespace MediaBrowser.Common.Plugins
{
@@ -82,6 +83,16 @@ namespace MediaBrowser.Common.Plugins
}
/// <inheritdoc />
+ public virtual void RegisterServices(IServiceCollection serviceCollection)
+ {
+ }
+
+ /// <inheritdoc />
+ public virtual void UnregisterServices(IServiceCollection serviceCollection)
+ {
+ }
+
+ /// <inheritdoc />
public void SetAttributes(string assemblyFilePath, string dataFolderPath, Version assemblyVersion)
{
AssemblyFilePath = assemblyFilePath;
diff --git a/MediaBrowser.Common/Plugins/IPlugin.cs b/MediaBrowser.Common/Plugins/IPlugin.cs
index 7bd37d210..1844eb124 100644
--- a/MediaBrowser.Common/Plugins/IPlugin.cs
+++ b/MediaBrowser.Common/Plugins/IPlugin.cs
@@ -2,6 +2,7 @@
using System;
using MediaBrowser.Model.Plugins;
+using Microsoft.Extensions.DependencyInjection;
namespace MediaBrowser.Common.Plugins
{
@@ -61,6 +62,18 @@ namespace MediaBrowser.Common.Plugins
/// Called when just before the plugin is uninstalled from the server.
/// </summary>
void OnUninstalling();
+
+ /// <summary>
+ /// Registers the plugin's services to the service collection.
+ /// </summary>
+ /// <param name="serviceCollection">The service collection.</param>
+ void RegisterServices(IServiceCollection serviceCollection);
+
+ /// <summary>
+ /// Unregisters the plugin's services from the service collection.
+ /// </summary>
+ /// <param name="serviceCollection">The service collection.</param>
+ void UnregisterServices(IServiceCollection serviceCollection);
}
public interface IHasPluginConfiguration
diff --git a/MediaBrowser.Common/Updates/InstallationEventArgs.cs b/MediaBrowser.Common/Updates/InstallationEventArgs.cs
index 11eb2ad34..61178f631 100644
--- a/MediaBrowser.Common/Updates/InstallationEventArgs.cs
+++ b/MediaBrowser.Common/Updates/InstallationEventArgs.cs
@@ -1,10 +1,11 @@
#pragma warning disable CS1591
+using System;
using MediaBrowser.Model.Updates;
namespace MediaBrowser.Common.Updates
{
- public class InstallationEventArgs
+ public class InstallationEventArgs : EventArgs
{
public InstallationInfo InstallationInfo { get; set; }
diff --git a/MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs b/MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs
index 15c902777..ecdffa2eb 100644
--- a/MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs
+++ b/MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
using MediaBrowser.Model.Users;
@@ -11,7 +13,9 @@ namespace MediaBrowser.Controller.Authentication
bool IsEnabled { get; }
Task<ProviderAuthenticationResult> Authenticate(string username, string password);
+
bool HasPassword(User user);
+
Task ChangePassword(User user, string newPassword);
}
diff --git a/MediaBrowser.Controller/Authentication/IPasswordResetProvider.cs b/MediaBrowser.Controller/Authentication/IPasswordResetProvider.cs
index 693df80ac..6729b9115 100644
--- a/MediaBrowser.Controller/Authentication/IPasswordResetProvider.cs
+++ b/MediaBrowser.Controller/Authentication/IPasswordResetProvider.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
@@ -12,6 +14,7 @@ namespace MediaBrowser.Controller.Authentication
bool IsEnabled { get; }
Task<ForgotPasswordResult> StartForgotPasswordProcess(User user, bool isInNetwork);
+
Task<PinRedeemResult> RedeemPasswordResetPin(string pin);
}
diff --git a/MediaBrowser.Controller/Channels/Channel.cs b/MediaBrowser.Controller/Channels/Channel.cs
index dbb047804..129cdb519 100644
--- a/MediaBrowser.Controller/Channels/Channel.cs
+++ b/MediaBrowser.Controller/Channels/Channel.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Globalization;
using System.Linq;
diff --git a/MediaBrowser.Controller/Channels/ChannelItemInfo.cs b/MediaBrowser.Controller/Channels/ChannelItemInfo.cs
index 00d4d9cb3..476992cbd 100644
--- a/MediaBrowser.Controller/Channels/ChannelItemInfo.cs
+++ b/MediaBrowser.Controller/Channels/ChannelItemInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using MediaBrowser.Controller.Entities;
diff --git a/MediaBrowser.Controller/Channels/ChannelItemResult.cs b/MediaBrowser.Controller/Channels/ChannelItemResult.cs
index c194c8e48..cee7b2003 100644
--- a/MediaBrowser.Controller/Channels/ChannelItemResult.cs
+++ b/MediaBrowser.Controller/Channels/ChannelItemResult.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
namespace MediaBrowser.Controller.Channels
diff --git a/MediaBrowser.Controller/Channels/ChannelItemType.cs b/MediaBrowser.Controller/Channels/ChannelItemType.cs
index 833ae75fe..3ce920e23 100644
--- a/MediaBrowser.Controller/Channels/ChannelItemType.cs
+++ b/MediaBrowser.Controller/Channels/ChannelItemType.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Controller.Channels
{
public enum ChannelItemType
diff --git a/MediaBrowser.Controller/Channels/ChannelParentalRating.cs b/MediaBrowser.Controller/Channels/ChannelParentalRating.cs
index 1d189446d..f77d81c16 100644
--- a/MediaBrowser.Controller/Channels/ChannelParentalRating.cs
+++ b/MediaBrowser.Controller/Channels/ChannelParentalRating.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Controller.Channels
{
public enum ChannelParentalRating
diff --git a/MediaBrowser.Controller/Channels/ChannelSearchInfo.cs b/MediaBrowser.Controller/Channels/ChannelSearchInfo.cs
index c02055b90..32469d4d7 100644
--- a/MediaBrowser.Controller/Channels/ChannelSearchInfo.cs
+++ b/MediaBrowser.Controller/Channels/ChannelSearchInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Controller.Channels
{
public class ChannelSearchInfo
diff --git a/MediaBrowser.Controller/Channels/IChannel.cs b/MediaBrowser.Controller/Channels/IChannel.cs
index c44e20d1a..2c0eadf95 100644
--- a/MediaBrowser.Controller/Channels/IChannel.cs
+++ b/MediaBrowser.Controller/Channels/IChannel.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
diff --git a/MediaBrowser.Controller/Channels/IChannelManager.cs b/MediaBrowser.Controller/Channels/IChannelManager.cs
index df508fbe2..9a9d22d33 100644
--- a/MediaBrowser.Controller/Channels/IChannelManager.cs
+++ b/MediaBrowser.Controller/Channels/IChannelManager.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Threading;
@@ -31,6 +33,7 @@ namespace MediaBrowser.Controller.Channels
ChannelFeatures[] GetAllChannelFeatures();
bool EnableMediaSourceDisplay(BaseItem item);
+
bool CanDelete(BaseItem item);
Task DeleteItem(BaseItem item);
diff --git a/MediaBrowser.Controller/Channels/IHasCacheKey.cs b/MediaBrowser.Controller/Channels/IHasCacheKey.cs
index d86ad0dba..bf895a0ec 100644
--- a/MediaBrowser.Controller/Channels/IHasCacheKey.cs
+++ b/MediaBrowser.Controller/Channels/IHasCacheKey.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Controller.Channels
{
public interface IHasCacheKey
diff --git a/MediaBrowser.Controller/Channels/IRequiresMediaInfoCallback.cs b/MediaBrowser.Controller/Channels/IRequiresMediaInfoCallback.cs
index deee46ff7..589295543 100644
--- a/MediaBrowser.Controller/Channels/IRequiresMediaInfoCallback.cs
+++ b/MediaBrowser.Controller/Channels/IRequiresMediaInfoCallback.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
diff --git a/MediaBrowser.Controller/Channels/ISearchableChannel.cs b/MediaBrowser.Controller/Channels/ISearchableChannel.cs
index 48043ad7a..b627ca1c2 100644
--- a/MediaBrowser.Controller/Channels/ISearchableChannel.cs
+++ b/MediaBrowser.Controller/Channels/ISearchableChannel.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
@@ -30,6 +32,7 @@ namespace MediaBrowser.Controller.Channels
public interface ISupportsDelete
{
bool CanDelete(BaseItem item);
+
Task DeleteItem(string id, CancellationToken cancellationToken);
}
diff --git a/MediaBrowser.Controller/Channels/InternalChannelFeatures.cs b/MediaBrowser.Controller/Channels/InternalChannelFeatures.cs
index 1f4a26064..1074ce435 100644
--- a/MediaBrowser.Controller/Channels/InternalChannelFeatures.cs
+++ b/MediaBrowser.Controller/Channels/InternalChannelFeatures.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
using MediaBrowser.Model.Channels;
@@ -5,6 +7,14 @@ namespace MediaBrowser.Controller.Channels
{
public class InternalChannelFeatures
{
+ public InternalChannelFeatures()
+ {
+ MediaTypes = new List<ChannelMediaType>();
+ ContentTypes = new List<ChannelMediaContentType>();
+
+ DefaultSortFields = new List<ChannelItemSortField>();
+ }
+
/// <summary>
/// Gets or sets the media types.
/// </summary>
@@ -48,13 +58,5 @@ namespace MediaBrowser.Controller.Channels
/// </summary>
/// <value><c>true</c> if [supports downloading]; otherwise, <c>false</c>.</value>
public bool SupportsContentDownloading { get; set; }
-
- public InternalChannelFeatures()
- {
- MediaTypes = new List<ChannelMediaType>();
- ContentTypes = new List<ChannelMediaContentType>();
-
- DefaultSortFields = new List<ChannelItemSortField>();
- }
}
}
diff --git a/MediaBrowser.Controller/Channels/InternalChannelItemQuery.cs b/MediaBrowser.Controller/Channels/InternalChannelItemQuery.cs
index 6d5ca75af..7e9bb28ed 100644
--- a/MediaBrowser.Controller/Channels/InternalChannelItemQuery.cs
+++ b/MediaBrowser.Controller/Channels/InternalChannelItemQuery.cs
@@ -1,7 +1,8 @@
+#pragma warning disable CS1591
+
using System;
using MediaBrowser.Model.Channels;
-
namespace MediaBrowser.Controller.Channels
{
public class InternalChannelItemQuery
diff --git a/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs b/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs
index 1e7549d2b..f6037d05e 100644
--- a/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs
+++ b/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using MediaBrowser.Model.Entities;
@@ -6,6 +8,13 @@ namespace MediaBrowser.Controller.Collections
{
public class CollectionCreationOptions : IHasProviderIds
{
+ public CollectionCreationOptions()
+ {
+ ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+ ItemIdList = Array.Empty<string>();
+ UserIds = Array.Empty<Guid>();
+ }
+
public string Name { get; set; }
public Guid? ParentId { get; set; }
@@ -17,12 +26,5 @@ namespace MediaBrowser.Controller.Collections
public string[] ItemIdList { get; set; }
public Guid[] UserIds { get; set; }
-
- public CollectionCreationOptions()
- {
- ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- ItemIdList = Array.Empty<string>();
- UserIds = Array.Empty<Guid>();
- }
}
}
diff --git a/MediaBrowser.Controller/Collections/CollectionEvents.cs b/MediaBrowser.Controller/Collections/CollectionEvents.cs
index bfc6af463..ce59b4ada 100644
--- a/MediaBrowser.Controller/Collections/CollectionEvents.cs
+++ b/MediaBrowser.Controller/Collections/CollectionEvents.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using MediaBrowser.Controller.Entities;
diff --git a/MediaBrowser.Controller/Collections/ICollectionManager.cs b/MediaBrowser.Controller/Collections/ICollectionManager.cs
index 701423c0f..a6991e2ea 100644
--- a/MediaBrowser.Controller/Collections/ICollectionManager.cs
+++ b/MediaBrowser.Controller/Collections/ICollectionManager.cs
@@ -1,5 +1,8 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
+using System.Threading.Tasks;
using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
@@ -27,24 +30,23 @@ namespace MediaBrowser.Controller.Collections
/// Creates the collection.
/// </summary>
/// <param name="options">The options.</param>
- BoxSet CreateCollection(CollectionCreationOptions options);
+ Task<BoxSet> CreateCollectionAsync(CollectionCreationOptions options);
/// <summary>
/// Adds to collection.
/// </summary>
/// <param name="collectionId">The collection identifier.</param>
/// <param name="itemIds">The item ids.</param>
- void AddToCollection(Guid collectionId, IEnumerable<string> itemIds);
+ /// <returns><see cref="Task"/> representing the asynchronous operation.</returns>
+ Task AddToCollectionAsync(Guid collectionId, IEnumerable<Guid> itemIds);
/// <summary>
/// Removes from collection.
/// </summary>
/// <param name="collectionId">The collection identifier.</param>
/// <param name="itemIds">The item ids.</param>
- void RemoveFromCollection(Guid collectionId, IEnumerable<string> itemIds);
-
- void AddToCollection(Guid collectionId, IEnumerable<Guid> itemIds);
- void RemoveFromCollection(Guid collectionId, IEnumerable<Guid> itemIds);
+ /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
+ Task RemoveFromCollectionAsync(Guid collectionId, IEnumerable<Guid> itemIds);
/// <summary>
/// Collapses the items within box sets.
diff --git a/MediaBrowser.Controller/Devices/IDeviceManager.cs b/MediaBrowser.Controller/Devices/IDeviceManager.cs
index 7d279230b..8f0872dba 100644
--- a/MediaBrowser.Controller/Devices/IDeviceManager.cs
+++ b/MediaBrowser.Controller/Devices/IDeviceManager.cs
@@ -1,7 +1,9 @@
+#pragma warning disable CS1591
+
using System;
using Jellyfin.Data.Entities;
+using Jellyfin.Data.Events;
using MediaBrowser.Model.Devices;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Session;
@@ -9,6 +11,8 @@ namespace MediaBrowser.Controller.Devices
{
public interface IDeviceManager
{
+ event EventHandler<GenericEventArgs<Tuple<string, DeviceOptions>>> DeviceOptionsUpdated;
+
/// <summary>
/// Saves the capabilities.
/// </summary>
@@ -44,7 +48,7 @@ namespace MediaBrowser.Controller.Devices
bool CanAccessDevice(User user, string deviceId);
void UpdateDeviceOptions(string deviceId, DeviceOptions options);
+
DeviceOptions GetDeviceOptions(string deviceId);
- event EventHandler<GenericEventArgs<Tuple<string, DeviceOptions>>> DeviceOptionsUpdated;
}
}
diff --git a/MediaBrowser.Controller/Dlna/IDlnaManager.cs b/MediaBrowser.Controller/Dlna/IDlnaManager.cs
index 41a7686a3..dc2d5a356 100644
--- a/MediaBrowser.Controller/Dlna/IDlnaManager.cs
+++ b/MediaBrowser.Controller/Dlna/IDlnaManager.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Model.Dlna;
diff --git a/MediaBrowser.Controller/Drawing/IImageEncoder.cs b/MediaBrowser.Controller/Drawing/IImageEncoder.cs
index e09ccd204..f9b2e6fef 100644
--- a/MediaBrowser.Controller/Drawing/IImageEncoder.cs
+++ b/MediaBrowser.Controller/Drawing/IImageEncoder.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using MediaBrowser.Model.Drawing;
diff --git a/MediaBrowser.Controller/Drawing/IImageProcessor.cs b/MediaBrowser.Controller/Drawing/IImageProcessor.cs
index 69d799165..b7edb1052 100644
--- a/MediaBrowser.Controller/Drawing/IImageProcessor.cs
+++ b/MediaBrowser.Controller/Drawing/IImageProcessor.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.IO;
diff --git a/MediaBrowser.Controller/Drawing/ImageCollageOptions.cs b/MediaBrowser.Controller/Drawing/ImageCollageOptions.cs
index 3f762fad0..fe0465d0d 100644
--- a/MediaBrowser.Controller/Drawing/ImageCollageOptions.cs
+++ b/MediaBrowser.Controller/Drawing/ImageCollageOptions.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Controller.Drawing
{
public class ImageCollageOptions
@@ -7,16 +9,19 @@ namespace MediaBrowser.Controller.Drawing
/// </summary>
/// <value>The input paths.</value>
public string[] InputPaths { get; set; }
+
/// <summary>
/// Gets or sets the output path.
/// </summary>
/// <value>The output path.</value>
public string OutputPath { get; set; }
+
/// <summary>
/// Gets or sets the width.
/// </summary>
/// <value>The width.</value>
public int Width { get; set; }
+
/// <summary>
/// Gets or sets the height.
/// </summary>
diff --git a/MediaBrowser.Controller/Drawing/ImageHelper.cs b/MediaBrowser.Controller/Drawing/ImageHelper.cs
index e1273fe7f..87c28d577 100644
--- a/MediaBrowser.Controller/Drawing/ImageHelper.cs
+++ b/MediaBrowser.Controller/Drawing/ImageHelper.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Drawing;
diff --git a/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs b/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs
index 31d2c1bd4..22105b7d7 100644
--- a/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs
+++ b/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.IO;
diff --git a/MediaBrowser.Controller/Drawing/ImageProcessorExtensions.cs b/MediaBrowser.Controller/Drawing/ImageProcessorExtensions.cs
index df9050de5..d3a2b4dbf 100644
--- a/MediaBrowser.Controller/Drawing/ImageProcessorExtensions.cs
+++ b/MediaBrowser.Controller/Drawing/ImageProcessorExtensions.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Entities;
diff --git a/MediaBrowser.Controller/Drawing/ImageStream.cs b/MediaBrowser.Controller/Drawing/ImageStream.cs
index 74ac24ec9..46f58ec15 100644
--- a/MediaBrowser.Controller/Drawing/ImageStream.cs
+++ b/MediaBrowser.Controller/Drawing/ImageStream.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.IO;
using MediaBrowser.Model.Drawing;
@@ -11,6 +13,7 @@ namespace MediaBrowser.Controller.Drawing
/// </summary>
/// <value>The stream.</value>
public Stream Stream { get; set; }
+
/// <summary>
/// Gets or sets the format.
/// </summary>
diff --git a/MediaBrowser.Controller/Dto/DtoOptions.cs b/MediaBrowser.Controller/Dto/DtoOptions.cs
index cf301f1e4..76f20ace2 100644
--- a/MediaBrowser.Controller/Dto/DtoOptions.cs
+++ b/MediaBrowser.Controller/Dto/DtoOptions.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Linq;
using MediaBrowser.Model.Entities;
diff --git a/MediaBrowser.Controller/Entities/AggregateFolder.cs b/MediaBrowser.Controller/Entities/AggregateFolder.cs
index e1c800e61..6ebea5f44 100644
--- a/MediaBrowser.Controller/Entities/AggregateFolder.cs
+++ b/MediaBrowser.Controller/Entities/AggregateFolder.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@@ -57,6 +59,7 @@ namespace MediaBrowser.Controller.Entities
private Guid[] _childrenIds = null;
private readonly object _childIdsLock = new object();
+
protected override List<BaseItem> LoadChildren()
{
lock (_childIdsLock)
diff --git a/MediaBrowser.Controller/Entities/Audio/Audio.cs b/MediaBrowser.Controller/Entities/Audio/Audio.cs
index 98f802b5d..2c6dea02c 100644
--- a/MediaBrowser.Controller/Entities/Audio/Audio.cs
+++ b/MediaBrowser.Controller/Entities/Audio/Audio.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Linq;
diff --git a/MediaBrowser.Controller/Entities/Audio/IHasAlbumArtist.cs b/MediaBrowser.Controller/Entities/Audio/IHasAlbumArtist.cs
index 056f31f78..f6d3cd6cc 100644
--- a/MediaBrowser.Controller/Entities/Audio/IHasAlbumArtist.cs
+++ b/MediaBrowser.Controller/Entities/Audio/IHasAlbumArtist.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
namespace MediaBrowser.Controller.Entities.Audio
diff --git a/MediaBrowser.Controller/Entities/Audio/IHasMusicGenres.cs b/MediaBrowser.Controller/Entities/Audio/IHasMusicGenres.cs
index 5ae056050..ac4dd1688 100644
--- a/MediaBrowser.Controller/Entities/Audio/IHasMusicGenres.cs
+++ b/MediaBrowser.Controller/Entities/Audio/IHasMusicGenres.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Controller.Entities.Audio
{
public interface IHasMusicGenres
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
index 5a1ddeece..48cd9371a 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Linq;
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
index ea5c41caa..397a68ff7 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Linq;
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs
index 4f6aa0776..5a117a6b1 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
diff --git a/MediaBrowser.Controller/Entities/AudioBook.cs b/MediaBrowser.Controller/Entities/AudioBook.cs
index 11ff8a257..f4bd851e1 100644
--- a/MediaBrowser.Controller/Entities/AudioBook.cs
+++ b/MediaBrowser.Controller/Entities/AudioBook.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Text.Json.Serialization;
using Jellyfin.Data.Enums;
@@ -15,8 +17,10 @@ namespace MediaBrowser.Controller.Entities
[JsonIgnore]
public string SeriesPresentationUniqueKey { get; set; }
+
[JsonIgnore]
public string SeriesName { get; set; }
+
[JsonIgnore]
public Guid SeriesId { get; set; }
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs
index f34309c40..24978d8dd 100644
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ b/MediaBrowser.Controller/Entities/BaseItem.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -1390,7 +1392,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
+ await LibraryManager.UpdateImagesAsync(this).ConfigureAwait(false); // ensure all image properties in DB are fresh
if (ownedItemsChanged)
{
@@ -2279,7 +2281,7 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
/// <param name="type">The type.</param>
/// <param name="index">The index.</param>
- public void DeleteImage(ImageType type, int index)
+ public async Task DeleteImageAsync(ImageType type, int index)
{
var info = GetImageInfo(type, index);
@@ -2297,7 +2299,7 @@ namespace MediaBrowser.Controller.Entities
FileSystem.DeleteFile(info.Path);
}
- UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None);
+ await UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false);
}
public void RemoveImage(ItemImageInfo image)
@@ -2310,10 +2312,8 @@ namespace MediaBrowser.Controller.Entities
ImageInfos = ImageInfos.Except(deletedImages).ToArray();
}
- public virtual void UpdateToRepository(ItemUpdateType updateReason, CancellationToken cancellationToken)
- {
- LibraryManager.UpdateItem(this, GetParent(), updateReason, cancellationToken);
- }
+ public virtual Task UpdateToRepositoryAsync(ItemUpdateType updateReason, CancellationToken cancellationToken)
+ => LibraryManager.UpdateItemAsync(this, GetParent(), updateReason, cancellationToken);
/// <summary>
/// Validates that images within the item are still on the filesystem.
@@ -2558,7 +2558,7 @@ namespace MediaBrowser.Controller.Entities
return type == ImageType.Backdrop || type == ImageType.Screenshot || type == ImageType.Chapter;
}
- public void SwapImages(ImageType type, int index1, int index2)
+ public Task SwapImagesAsync(ImageType type, int index1, int index2)
{
if (!AllowsMultipleImages(type))
{
@@ -2571,13 +2571,13 @@ namespace MediaBrowser.Controller.Entities
if (info1 == null || info2 == null)
{
// Nothing to do
- return;
+ return Task.CompletedTask;
}
if (!info1.IsLocalFile || !info2.IsLocalFile)
{
// TODO: Not supported yet
- return;
+ return Task.CompletedTask;
}
var path1 = info1.Path;
@@ -2594,7 +2594,7 @@ namespace MediaBrowser.Controller.Entities
info2.Width = 0;
info2.Height = 0;
- UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None);
+ return UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None);
}
public virtual bool IsPlayed(User user)
diff --git a/MediaBrowser.Controller/Entities/BaseItemExtensions.cs b/MediaBrowser.Controller/Entities/BaseItemExtensions.cs
index 815239be2..8a69971d0 100644
--- a/MediaBrowser.Controller/Entities/BaseItemExtensions.cs
+++ b/MediaBrowser.Controller/Entities/BaseItemExtensions.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Linq;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
diff --git a/MediaBrowser.Controller/Entities/BasePluginFolder.cs b/MediaBrowser.Controller/Entities/BasePluginFolder.cs
index 106385bc6..ef5a5a734 100644
--- a/MediaBrowser.Controller/Entities/BasePluginFolder.cs
+++ b/MediaBrowser.Controller/Entities/BasePluginFolder.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Text.Json.Serialization;
namespace MediaBrowser.Controller.Entities
@@ -26,13 +28,5 @@ namespace MediaBrowser.Controller.Entities
[JsonIgnore]
public override bool SupportsPeople => false;
-
- // public override double? GetDefaultPrimaryImageAspectRatio()
- //{
- // double value = 16;
- // value /= 9;
-
- // return value;
- //}
}
}
diff --git a/MediaBrowser.Controller/Entities/Book.cs b/MediaBrowser.Controller/Entities/Book.cs
index 11c6c6e45..55945283c 100644
--- a/MediaBrowser.Controller/Entities/Book.cs
+++ b/MediaBrowser.Controller/Entities/Book.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Linq;
using System.Text.Json.Serialization;
@@ -49,11 +51,13 @@ namespace MediaBrowser.Controller.Entities
return SeriesId;
}
+ /// <inheritdoc />
public override bool CanDownload()
{
return IsFileProtocol;
}
+ /// <inheritdoc />
public override UnratedItem GetBlockUnratedType()
{
return UnratedItem.Book;
diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs
index 5c6a9d2a2..d25545a2f 100644
--- a/MediaBrowser.Controller/Entities/CollectionFolder.cs
+++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.IO;
diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs
index 6441340f9..11542c1ca 100644
--- a/MediaBrowser.Controller/Entities/Folder.cs
+++ b/MediaBrowser.Controller/Entities/Folder.cs
@@ -350,12 +350,12 @@ namespace MediaBrowser.Controller.Entities
if (currentChild.UpdateFromResolvedItem(child) > ItemUpdateType.None)
{
- currentChild.UpdateToRepository(ItemUpdateType.MetadataImport, cancellationToken);
+ await currentChild.UpdateToRepositoryAsync(ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false);
}
else
{
// metadata is up-to-date; make sure DB has correct images dimensions and hash
- LibraryManager.UpdateImages(currentChild);
+ await LibraryManager.UpdateImagesAsync(currentChild).ConfigureAwait(false);
}
continue;
diff --git a/MediaBrowser.Controller/Entities/Genre.cs b/MediaBrowser.Controller/Entities/Genre.cs
index 437def532..db6c85caf 100644
--- a/MediaBrowser.Controller/Entities/Genre.cs
+++ b/MediaBrowser.Controller/Entities/Genre.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
diff --git a/MediaBrowser.Controller/Entities/ICollectionFolder.cs b/MediaBrowser.Controller/Entities/ICollectionFolder.cs
index 245b23ff0..b84a9fa6f 100644
--- a/MediaBrowser.Controller/Entities/ICollectionFolder.cs
+++ b/MediaBrowser.Controller/Entities/ICollectionFolder.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace MediaBrowser.Controller.Entities
diff --git a/MediaBrowser.Controller/Entities/IHasMediaSources.cs b/MediaBrowser.Controller/Entities/IHasMediaSources.cs
index 213c0a794..a7b60d168 100644
--- a/MediaBrowser.Controller/Entities/IHasMediaSources.cs
+++ b/MediaBrowser.Controller/Entities/IHasMediaSources.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using MediaBrowser.Model.Dto;
@@ -7,15 +9,19 @@ namespace MediaBrowser.Controller.Entities
{
public interface IHasMediaSources
{
+ Guid Id { get; set; }
+
+ long? RunTimeTicks { get; set; }
+
+ string Path { get; }
+
/// <summary>
/// Gets the media sources.
/// </summary>
List<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution);
+
List<MediaStream> GetMediaStreams();
- Guid Id { get; set; }
- long? RunTimeTicks { get; set; }
- string Path { get; }
}
}
diff --git a/MediaBrowser.Controller/Entities/IHasProgramAttributes.cs b/MediaBrowser.Controller/Entities/IHasProgramAttributes.cs
index fd1c19c97..f747b5149 100644
--- a/MediaBrowser.Controller/Entities/IHasProgramAttributes.cs
+++ b/MediaBrowser.Controller/Entities/IHasProgramAttributes.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Model.LiveTv;
namespace MediaBrowser.Controller.Entities
diff --git a/MediaBrowser.Controller/Entities/IHasSeries.cs b/MediaBrowser.Controller/Entities/IHasSeries.cs
index 475a2ab85..5444f1f52 100644
--- a/MediaBrowser.Controller/Entities/IHasSeries.cs
+++ b/MediaBrowser.Controller/Entities/IHasSeries.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace MediaBrowser.Controller.Entities
@@ -10,12 +12,15 @@ namespace MediaBrowser.Controller.Entities
/// <value>The name of the series.</value>
string SeriesName { get; set; }
+ Guid SeriesId { get; set; }
+
+ string SeriesPresentationUniqueKey { get; set; }
+
string FindSeriesName();
+
string FindSeriesSortName();
- Guid SeriesId { get; set; }
Guid FindSeriesId();
- string SeriesPresentationUniqueKey { get; set; }
string FindSeriesPresentationUniqueKey();
}
diff --git a/MediaBrowser.Controller/Entities/IHasSpecialFeatures.cs b/MediaBrowser.Controller/Entities/IHasSpecialFeatures.cs
index 688439e6c..6a350212b 100644
--- a/MediaBrowser.Controller/Entities/IHasSpecialFeatures.cs
+++ b/MediaBrowser.Controller/Entities/IHasSpecialFeatures.cs
@@ -1,4 +1,7 @@
+#pragma warning disable CS1591
+
using System;
+using System.Collections.Generic;
namespace MediaBrowser.Controller.Entities
{
@@ -8,6 +11,6 @@ namespace MediaBrowser.Controller.Entities
/// Gets or sets the special feature ids.
/// </summary>
/// <value>The special feature ids.</value>
- Guid[] SpecialFeatureIds { get; set; }
+ IReadOnlyList<Guid> SpecialFeatureIds { get; set; }
}
}
diff --git a/MediaBrowser.Controller/Entities/IHasStartDate.cs b/MediaBrowser.Controller/Entities/IHasStartDate.cs
index 1ecde9af4..dab15eb01 100644
--- a/MediaBrowser.Controller/Entities/IHasStartDate.cs
+++ b/MediaBrowser.Controller/Entities/IHasStartDate.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace MediaBrowser.Controller.Entities
diff --git a/MediaBrowser.Controller/Entities/IHasTrailers.cs b/MediaBrowser.Controller/Entities/IHasTrailers.cs
index dd8e3c45f..d1f6f2b7e 100644
--- a/MediaBrowser.Controller/Entities/IHasTrailers.cs
+++ b/MediaBrowser.Controller/Entities/IHasTrailers.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using MediaBrowser.Model.Entities;
diff --git a/MediaBrowser.Controller/Entities/IItemByName.cs b/MediaBrowser.Controller/Entities/IItemByName.cs
index 8ef5c8d96..cac8aa61a 100644
--- a/MediaBrowser.Controller/Entities/IItemByName.cs
+++ b/MediaBrowser.Controller/Entities/IItemByName.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
namespace MediaBrowser.Controller.Entities
diff --git a/MediaBrowser.Controller/Entities/IMetadataContainer.cs b/MediaBrowser.Controller/Entities/IMetadataContainer.cs
index a384c0df3..77f5cfb79 100644
--- a/MediaBrowser.Controller/Entities/IMetadataContainer.cs
+++ b/MediaBrowser.Controller/Entities/IMetadataContainer.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Threading;
using System.Threading.Tasks;
diff --git a/MediaBrowser.Controller/Entities/ISupportsPlaceHolders.cs b/MediaBrowser.Controller/Entities/ISupportsPlaceHolders.cs
index 3e96771c3..cdda8ea39 100644
--- a/MediaBrowser.Controller/Entities/ISupportsPlaceHolders.cs
+++ b/MediaBrowser.Controller/Entities/ISupportsPlaceHolders.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Controller.Entities
{
public interface ISupportsPlaceHolders
diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
index 466cda67c..904752a22 100644
--- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
+++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Linq;
diff --git a/MediaBrowser.Controller/Entities/InternalPeopleQuery.cs b/MediaBrowser.Controller/Entities/InternalPeopleQuery.cs
index 011975dd2..4e09ee573 100644
--- a/MediaBrowser.Controller/Entities/InternalPeopleQuery.cs
+++ b/MediaBrowser.Controller/Entities/InternalPeopleQuery.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace MediaBrowser.Controller.Entities
diff --git a/MediaBrowser.Controller/Entities/ItemImageInfo.cs b/MediaBrowser.Controller/Entities/ItemImageInfo.cs
index 12f5db2e0..570d8eec0 100644
--- a/MediaBrowser.Controller/Entities/ItemImageInfo.cs
+++ b/MediaBrowser.Controller/Entities/ItemImageInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Text.Json.Serialization;
using MediaBrowser.Model.Entities;
diff --git a/MediaBrowser.Controller/Entities/LinkedChild.cs b/MediaBrowser.Controller/Entities/LinkedChild.cs
index 65753a26e..8e0f721e7 100644
--- a/MediaBrowser.Controller/Entities/LinkedChild.cs
+++ b/MediaBrowser.Controller/Entities/LinkedChild.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Globalization;
diff --git a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
index c131c5430..8de88cc1b 100644
--- a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
+++ b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Linq;
diff --git a/MediaBrowser.Controller/Entities/Movies/Movie.cs b/MediaBrowser.Controller/Entities/Movies/Movie.cs
index 53badac4d..8b67aaccc 100644
--- a/MediaBrowser.Controller/Entities/Movies/Movie.cs
+++ b/MediaBrowser.Controller/Entities/Movies/Movie.cs
@@ -1,5 +1,8 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
+using System.Globalization;
using System.Linq;
using System.Text.Json.Serialization;
using System.Threading;
@@ -17,8 +20,6 @@ namespace MediaBrowser.Controller.Entities.Movies
/// </summary>
public class Movie : Video, IHasSpecialFeatures, IHasTrailers, IHasLookupInfo<MovieInfo>, ISupportsBoxSetGrouping
{
- public Guid[] SpecialFeatureIds { get; set; }
-
public Movie()
{
SpecialFeatureIds = Array.Empty<Guid>();
@@ -28,6 +29,9 @@ namespace MediaBrowser.Controller.Entities.Movies
}
/// <inheritdoc />
+ public IReadOnlyList<Guid> SpecialFeatureIds { get; set; }
+
+ /// <inheritdoc />
public IReadOnlyList<Guid> LocalTrailerIds { get; set; }
/// <inheritdoc />
@@ -46,6 +50,9 @@ namespace MediaBrowser.Controller.Entities.Movies
set => TmdbCollectionName = value;
}
+ [JsonIgnore]
+ public override bool StopRefreshIfLocalMetadataFound => false;
+
public override double GetDefaultPrimaryImageAspectRatio()
{
// hack for tv plugins
@@ -105,6 +112,7 @@ namespace MediaBrowser.Controller.Entities.Movies
return itemsChanged;
}
+ /// <inheritdoc />
public override UnratedItem GetBlockUnratedType()
{
return UnratedItem.Movie;
@@ -133,6 +141,7 @@ namespace MediaBrowser.Controller.Entities.Movies
return info;
}
+ /// <inheritdoc />
public override bool BeforeMetadataRefresh(bool replaceAllMetdata)
{
var hasChanges = base.BeforeMetadataRefresh(replaceAllMetdata);
@@ -169,6 +178,7 @@ namespace MediaBrowser.Controller.Entities.Movies
return hasChanges;
}
+ /// <inheritdoc />
public override List<ExternalUrl> GetRelatedUrls()
{
var list = base.GetRelatedUrls();
@@ -179,14 +189,11 @@ namespace MediaBrowser.Controller.Entities.Movies
list.Add(new ExternalUrl
{
Name = "Trakt",
- Url = string.Format("https://trakt.tv/movies/{0}", imdbId)
+ Url = string.Format(CultureInfo.InvariantCulture, "https://trakt.tv/movies/{0}", imdbId)
});
}
return list;
}
-
- [JsonIgnore]
- public override bool StopRefreshIfLocalMetadataFound => false;
}
}
diff --git a/MediaBrowser.Controller/Entities/MusicVideo.cs b/MediaBrowser.Controller/Entities/MusicVideo.cs
index 6e7f2812d..b278a0142 100644
--- a/MediaBrowser.Controller/Entities/MusicVideo.cs
+++ b/MediaBrowser.Controller/Entities/MusicVideo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
diff --git a/MediaBrowser.Controller/Entities/PeopleHelper.cs b/MediaBrowser.Controller/Entities/PeopleHelper.cs
index c39495759..1f3758a73 100644
--- a/MediaBrowser.Controller/Entities/PeopleHelper.cs
+++ b/MediaBrowser.Controller/Entities/PeopleHelper.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Linq;
diff --git a/MediaBrowser.Controller/Entities/Person.cs b/MediaBrowser.Controller/Entities/Person.cs
index 331d17fc8..c4fcb0267 100644
--- a/MediaBrowser.Controller/Entities/Person.cs
+++ b/MediaBrowser.Controller/Entities/Person.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
diff --git a/MediaBrowser.Controller/Entities/PersonInfo.cs b/MediaBrowser.Controller/Entities/PersonInfo.cs
index f3ec73b32..4ff9b0955 100644
--- a/MediaBrowser.Controller/Entities/PersonInfo.cs
+++ b/MediaBrowser.Controller/Entities/PersonInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using MediaBrowser.Model.Entities;
diff --git a/MediaBrowser.Controller/Entities/Photo.cs b/MediaBrowser.Controller/Entities/Photo.cs
index 82d0826c5..1485d4c79 100644
--- a/MediaBrowser.Controller/Entities/Photo.cs
+++ b/MediaBrowser.Controller/Entities/Photo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Text.Json.Serialization;
using MediaBrowser.Model.Drawing;
diff --git a/MediaBrowser.Controller/Entities/PhotoAlbum.cs b/MediaBrowser.Controller/Entities/PhotoAlbum.cs
index b86f1ac2a..a7ecb9061 100644
--- a/MediaBrowser.Controller/Entities/PhotoAlbum.cs
+++ b/MediaBrowser.Controller/Entities/PhotoAlbum.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Text.Json.Serialization;
namespace MediaBrowser.Controller.Entities
diff --git a/MediaBrowser.Controller/Entities/Share.cs b/MediaBrowser.Controller/Entities/Share.cs
index a51f2b452..50f1655f3 100644
--- a/MediaBrowser.Controller/Entities/Share.cs
+++ b/MediaBrowser.Controller/Entities/Share.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Controller.Entities
{
public interface IHasShares
diff --git a/MediaBrowser.Controller/Entities/SourceType.cs b/MediaBrowser.Controller/Entities/SourceType.cs
index 927483b93..be19e1bda 100644
--- a/MediaBrowser.Controller/Entities/SourceType.cs
+++ b/MediaBrowser.Controller/Entities/SourceType.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Controller.Entities
{
public enum SourceType
diff --git a/MediaBrowser.Controller/Entities/Studio.cs b/MediaBrowser.Controller/Entities/Studio.cs
index 1f64de6a4..9018ddb75 100644
--- a/MediaBrowser.Controller/Entities/Studio.cs
+++ b/MediaBrowser.Controller/Entities/Studio.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs
index 9a5f9097d..dc12fbbea 100644
--- a/MediaBrowser.Controller/Entities/TV/Episode.cs
+++ b/MediaBrowser.Controller/Entities/TV/Episode.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Globalization;
diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs
index 2aba1d03d..93bdd6e70 100644
--- a/MediaBrowser.Controller/Entities/TV/Season.cs
+++ b/MediaBrowser.Controller/Entities/TV/Season.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Linq;
diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs
index 45daa8a53..72c696c1a 100644
--- a/MediaBrowser.Controller/Entities/TV/Series.cs
+++ b/MediaBrowser.Controller/Entities/TV/Series.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -496,7 +498,7 @@ namespace MediaBrowser.Controller.Entities.TV
list.Add(new ExternalUrl
{
Name = "Trakt",
- Url = string.Format("https://trakt.tv/shows/{0}", imdbId)
+ Url = string.Format(CultureInfo.InvariantCulture, "https://trakt.tv/shows/{0}", imdbId)
});
}
diff --git a/MediaBrowser.Controller/Entities/TagExtensions.cs b/MediaBrowser.Controller/Entities/TagExtensions.cs
index 97f590635..2ce396daf 100644
--- a/MediaBrowser.Controller/Entities/TagExtensions.cs
+++ b/MediaBrowser.Controller/Entities/TagExtensions.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Linq;
diff --git a/MediaBrowser.Controller/Entities/Trailer.cs b/MediaBrowser.Controller/Entities/Trailer.cs
index 6b544afc6..9ae8ad708 100644
--- a/MediaBrowser.Controller/Entities/Trailer.cs
+++ b/MediaBrowser.Controller/Entities/Trailer.cs
@@ -1,5 +1,8 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
+using System.Globalization;
using System.Text.Json.Serialization;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Providers;
@@ -86,7 +89,7 @@ namespace MediaBrowser.Controller.Entities
list.Add(new ExternalUrl
{
Name = "Trakt",
- Url = string.Format("https://trakt.tv/movies/{0}", imdbId)
+ Url = string.Format(CultureInfo.InvariantCulture, "https://trakt.tv/movies/{0}", imdbId)
});
}
diff --git a/MediaBrowser.Controller/Entities/UserItemData.cs b/MediaBrowser.Controller/Entities/UserItemData.cs
index 3298fa2d3..db63c42e4 100644
--- a/MediaBrowser.Controller/Entities/UserItemData.cs
+++ b/MediaBrowser.Controller/Entities/UserItemData.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Text.Json.Serialization;
@@ -24,6 +26,7 @@ namespace MediaBrowser.Controller.Entities
/// The _rating.
/// </summary>
private double? _rating;
+
/// <summary>
/// Gets or sets the users 0-10 rating.
/// </summary>
@@ -75,11 +78,13 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
/// <value><c>true</c> if played; otherwise, <c>false</c>.</value>
public bool Played { get; set; }
+
/// <summary>
/// Gets or sets the index of the audio stream.
/// </summary>
/// <value>The index of the audio stream.</value>
public int? AudioStreamIndex { get; set; }
+
/// <summary>
/// Gets or sets the index of the subtitle stream.
/// </summary>
diff --git a/MediaBrowser.Controller/Entities/UserRootFolder.cs b/MediaBrowser.Controller/Entities/UserRootFolder.cs
index 39f4e0b6c..7f7224ae0 100644
--- a/MediaBrowser.Controller/Entities/UserRootFolder.cs
+++ b/MediaBrowser.Controller/Entities/UserRootFolder.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Linq;
diff --git a/MediaBrowser.Controller/Entities/UserView.cs b/MediaBrowser.Controller/Entities/UserView.cs
index 1fba8a30f..b1da4d64c 100644
--- a/MediaBrowser.Controller/Entities/UserView.cs
+++ b/MediaBrowser.Controller/Entities/UserView.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Linq;
@@ -6,7 +8,6 @@ using System.Threading.Tasks;
using Jellyfin.Data.Entities;
using MediaBrowser.Controller.TV;
using MediaBrowser.Model.Querying;
-using Microsoft.Extensions.Logging;
namespace MediaBrowser.Controller.Entities
{
diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
index 22bb7fd55..b384b27d1 100644
--- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs
+++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -8,7 +10,6 @@ using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.TV;
-using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
using Microsoft.Extensions.Logging;
@@ -672,9 +673,7 @@ namespace MediaBrowser.Controller.Entities
var isPlaceHolder = false;
- var hasPlaceHolder = item as ISupportsPlaceHolders;
-
- if (hasPlaceHolder != null)
+ if (item is ISupportsPlaceHolders hasPlaceHolder)
{
isPlaceHolder = hasPlaceHolder.IsPlaceHolder;
}
@@ -689,13 +688,11 @@ namespace MediaBrowser.Controller.Entities
{
var filterValue = query.HasSpecialFeature.Value;
- var movie = item as IHasSpecialFeatures;
-
- if (movie != null)
+ if (item is IHasSpecialFeatures movie)
{
var ok = filterValue
- ? movie.SpecialFeatureIds.Length > 0
- : movie.SpecialFeatureIds.Length == 0;
+ ? movie.SpecialFeatureIds.Count > 0
+ : movie.SpecialFeatureIds.Count == 0;
if (!ok)
{
diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs
index b7d7e8e1a..07f381881 100644
--- a/MediaBrowser.Controller/Entities/Video.cs
+++ b/MediaBrowser.Controller/Entities/Video.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -495,9 +497,10 @@ namespace MediaBrowser.Controller.Entities
}
}
- public override void UpdateToRepository(ItemUpdateType updateReason, CancellationToken cancellationToken)
+ /// <inheritdoc />
+ public override async Task UpdateToRepositoryAsync(ItemUpdateType updateReason, CancellationToken cancellationToken)
{
- base.UpdateToRepository(updateReason, cancellationToken);
+ await base.UpdateToRepositoryAsync(updateReason, cancellationToken).ConfigureAwait(false);
var localAlternates = GetLocalAlternateVersionIds()
.Select(i => LibraryManager.GetItemById(i))
@@ -514,7 +517,7 @@ namespace MediaBrowser.Controller.Entities
item.Genres = Genres;
item.ProviderIds = ProviderIds;
- item.UpdateToRepository(ItemUpdateType.MetadataDownload, cancellationToken);
+ await item.UpdateToRepositoryAsync(ItemUpdateType.MetadataDownload, cancellationToken).ConfigureAwait(false);
}
}
diff --git a/MediaBrowser.Controller/Entities/Year.cs b/MediaBrowser.Controller/Entities/Year.cs
index c88498640..b2e4d307a 100644
--- a/MediaBrowser.Controller/Entities/Year.cs
+++ b/MediaBrowser.Controller/Entities/Year.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Globalization;
diff --git a/MediaBrowser.Controller/Events/IEventConsumer.cs b/MediaBrowser.Controller/Events/IEventConsumer.cs
new file mode 100644
index 000000000..5c4ab5d8d
--- /dev/null
+++ b/MediaBrowser.Controller/Events/IEventConsumer.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Controller.Events
+{
+ /// <summary>
+ /// An interface representing a type that consumes events of type <c>T</c>.
+ /// </summary>
+ /// <typeparam name="T">The type of events this consumes.</typeparam>
+ public interface IEventConsumer<in T>
+ where T : EventArgs
+ {
+ /// <summary>
+ /// A method that is called when an event of type <c>T</c> is fired.
+ /// </summary>
+ /// <param name="eventArgs">The event.</param>
+ /// <returns>A task representing the consumption of the event.</returns>
+ Task OnEvent(T eventArgs);
+ }
+}
diff --git a/MediaBrowser.Controller/Events/IEventManager.cs b/MediaBrowser.Controller/Events/IEventManager.cs
new file mode 100644
index 000000000..a1f40b3a6
--- /dev/null
+++ b/MediaBrowser.Controller/Events/IEventManager.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Controller.Events
+{
+ /// <summary>
+ /// An interface that handles eventing.
+ /// </summary>
+ public interface IEventManager
+ {
+ /// <summary>
+ /// Publishes an event.
+ /// </summary>
+ /// <param name="eventArgs">the event arguments.</param>
+ /// <typeparam name="T">The type of event.</typeparam>
+ void Publish<T>(T eventArgs)
+ where T : EventArgs;
+
+ /// <summary>
+ /// Publishes an event asynchronously.
+ /// </summary>
+ /// <param name="eventArgs">The event arguments.</param>
+ /// <typeparam name="T">The type of event.</typeparam>
+ /// <returns>A task representing the publishing of the event.</returns>
+ Task PublishAsync<T>(T eventArgs)
+ where T : EventArgs;
+ }
+}
diff --git a/MediaBrowser.Controller/Events/Session/SessionEndedEventArgs.cs b/MediaBrowser.Controller/Events/Session/SessionEndedEventArgs.cs
new file mode 100644
index 000000000..46d7e5a17
--- /dev/null
+++ b/MediaBrowser.Controller/Events/Session/SessionEndedEventArgs.cs
@@ -0,0 +1,19 @@
+using Jellyfin.Data.Events;
+using MediaBrowser.Controller.Session;
+
+namespace MediaBrowser.Controller.Events.Session
+{
+ /// <summary>
+ /// An event that fires when a session is ended.
+ /// </summary>
+ public class SessionEndedEventArgs : GenericEventArgs<SessionInfo>
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SessionEndedEventArgs"/> class.
+ /// </summary>
+ /// <param name="arg">The session info.</param>
+ public SessionEndedEventArgs(SessionInfo arg) : base(arg)
+ {
+ }
+ }
+}
diff --git a/MediaBrowser.Controller/Events/Session/SessionStartedEventArgs.cs b/MediaBrowser.Controller/Events/Session/SessionStartedEventArgs.cs
new file mode 100644
index 000000000..aab19cc46
--- /dev/null
+++ b/MediaBrowser.Controller/Events/Session/SessionStartedEventArgs.cs
@@ -0,0 +1,19 @@
+using Jellyfin.Data.Events;
+using MediaBrowser.Controller.Session;
+
+namespace MediaBrowser.Controller.Events.Session
+{
+ /// <summary>
+ /// An event that fires when a session is started.
+ /// </summary>
+ public class SessionStartedEventArgs : GenericEventArgs<SessionInfo>
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SessionStartedEventArgs"/> class.
+ /// </summary>
+ /// <param name="arg">The session info.</param>
+ public SessionStartedEventArgs(SessionInfo arg) : base(arg)
+ {
+ }
+ }
+}
diff --git a/MediaBrowser.Controller/Events/Updates/PluginInstallationCancelledEventArgs.cs b/MediaBrowser.Controller/Events/Updates/PluginInstallationCancelledEventArgs.cs
new file mode 100644
index 000000000..b06046c05
--- /dev/null
+++ b/MediaBrowser.Controller/Events/Updates/PluginInstallationCancelledEventArgs.cs
@@ -0,0 +1,19 @@
+using Jellyfin.Data.Events;
+using MediaBrowser.Model.Updates;
+
+namespace MediaBrowser.Controller.Events.Updates
+{
+ /// <summary>
+ /// An event that occurs when a plugin installation is cancelled.
+ /// </summary>
+ public class PluginInstallationCancelledEventArgs : GenericEventArgs<InstallationInfo>
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PluginInstallationCancelledEventArgs"/> class.
+ /// </summary>
+ /// <param name="arg">The installation info.</param>
+ public PluginInstallationCancelledEventArgs(InstallationInfo arg) : base(arg)
+ {
+ }
+ }
+}
diff --git a/MediaBrowser.Controller/Events/Updates/PluginInstalledEventArgs.cs b/MediaBrowser.Controller/Events/Updates/PluginInstalledEventArgs.cs
new file mode 100644
index 000000000..dfadc9f61
--- /dev/null
+++ b/MediaBrowser.Controller/Events/Updates/PluginInstalledEventArgs.cs
@@ -0,0 +1,19 @@
+using Jellyfin.Data.Events;
+using MediaBrowser.Model.Updates;
+
+namespace MediaBrowser.Controller.Events.Updates
+{
+ /// <summary>
+ /// An event that occurs when a plugin is installed.
+ /// </summary>
+ public class PluginInstalledEventArgs : GenericEventArgs<InstallationInfo>
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PluginInstalledEventArgs"/> class.
+ /// </summary>
+ /// <param name="arg">The installation info.</param>
+ public PluginInstalledEventArgs(InstallationInfo arg) : base(arg)
+ {
+ }
+ }
+}
diff --git a/MediaBrowser.Controller/Events/Updates/PluginInstallingEventArgs.cs b/MediaBrowser.Controller/Events/Updates/PluginInstallingEventArgs.cs
new file mode 100644
index 000000000..045a60027
--- /dev/null
+++ b/MediaBrowser.Controller/Events/Updates/PluginInstallingEventArgs.cs
@@ -0,0 +1,19 @@
+using Jellyfin.Data.Events;
+using MediaBrowser.Model.Updates;
+
+namespace MediaBrowser.Controller.Events.Updates
+{
+ /// <summary>
+ /// An event that occurs when a plugin is installing.
+ /// </summary>
+ public class PluginInstallingEventArgs : GenericEventArgs<InstallationInfo>
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PluginInstallingEventArgs"/> class.
+ /// </summary>
+ /// <param name="arg">The installation info.</param>
+ public PluginInstallingEventArgs(InstallationInfo arg) : base(arg)
+ {
+ }
+ }
+}
diff --git a/MediaBrowser.Controller/Events/Updates/PluginUninstalledEventArgs.cs b/MediaBrowser.Controller/Events/Updates/PluginUninstalledEventArgs.cs
new file mode 100644
index 000000000..7510b62b8
--- /dev/null
+++ b/MediaBrowser.Controller/Events/Updates/PluginUninstalledEventArgs.cs
@@ -0,0 +1,19 @@
+using Jellyfin.Data.Events;
+using MediaBrowser.Common.Plugins;
+
+namespace MediaBrowser.Controller.Events.Updates
+{
+ /// <summary>
+ /// An event that occurs when a plugin is uninstalled.
+ /// </summary>
+ public class PluginUninstalledEventArgs : GenericEventArgs<IPlugin>
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PluginUninstalledEventArgs"/> class.
+ /// </summary>
+ /// <param name="arg">The plugin.</param>
+ public PluginUninstalledEventArgs(IPlugin arg) : base(arg)
+ {
+ }
+ }
+}
diff --git a/MediaBrowser.Controller/Events/Updates/PluginUpdatedEventArgs.cs b/MediaBrowser.Controller/Events/Updates/PluginUpdatedEventArgs.cs
new file mode 100644
index 000000000..661ca066a
--- /dev/null
+++ b/MediaBrowser.Controller/Events/Updates/PluginUpdatedEventArgs.cs
@@ -0,0 +1,19 @@
+using Jellyfin.Data.Events;
+using MediaBrowser.Model.Updates;
+
+namespace MediaBrowser.Controller.Events.Updates
+{
+ /// <summary>
+ /// An event that occurs when a plugin is updated.
+ /// </summary>
+ public class PluginUpdatedEventArgs : GenericEventArgs<InstallationInfo>
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PluginUpdatedEventArgs"/> class.
+ /// </summary>
+ /// <param name="arg">The installation info.</param>
+ public PluginUpdatedEventArgs(InstallationInfo arg) : base(arg)
+ {
+ }
+ }
+}
diff --git a/MediaBrowser.Controller/Extensions/StringExtensions.cs b/MediaBrowser.Controller/Extensions/StringExtensions.cs
index e09543e14..3cc1f328a 100644
--- a/MediaBrowser.Controller/Extensions/StringExtensions.cs
+++ b/MediaBrowser.Controller/Extensions/StringExtensions.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Globalization;
using System.Linq;
diff --git a/MediaBrowser.Controller/IDisplayPreferencesManager.cs b/MediaBrowser.Controller/IDisplayPreferencesManager.cs
index b6bfed3e5..856b91b5d 100644
--- a/MediaBrowser.Controller/IDisplayPreferencesManager.cs
+++ b/MediaBrowser.Controller/IDisplayPreferencesManager.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using Jellyfin.Data.Entities;
diff --git a/MediaBrowser.Controller/IO/FileData.cs b/MediaBrowser.Controller/IO/FileData.cs
index e655f50eb..9bc4cac39 100644
--- a/MediaBrowser.Controller/IO/FileData.cs
+++ b/MediaBrowser.Controller/IO/FileData.cs
@@ -8,28 +8,17 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Controller.IO
{
/// <summary>
- /// Provides low level File access that is much faster than the File/Directory api's
+ /// Provides low level File access that is much faster than the File/Directory api's.
/// </summary>
public static class FileData
{
- private static Dictionary<string, FileSystemMetadata> GetFileSystemDictionary(FileSystemMetadata[] list)
- {
- var dict = new Dictionary<string, FileSystemMetadata>(StringComparer.OrdinalIgnoreCase);
-
- foreach (var file in list)
- {
- dict[file.FullName] = file;
- }
-
- return dict;
- }
-
/// <summary>
/// Gets the filtered file system entries.
/// </summary>
/// <param name="directoryService">The directory service.</param>
/// <param name="path">The path.</param>
/// <param name="fileSystem">The file system.</param>
+ /// <param name="appHost">The application host.</param>
/// <param name="logger">The logger.</param>
/// <param name="args">The args.</param>
/// <param name="flattenFolderDepth">The flatten folder depth.</param>
diff --git a/MediaBrowser.Controller/IResourceFileManager.cs b/MediaBrowser.Controller/IResourceFileManager.cs
index 69a51cec8..26f0424b7 100644
--- a/MediaBrowser.Controller/IResourceFileManager.cs
+++ b/MediaBrowser.Controller/IResourceFileManager.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Controller
{
public interface IResourceFileManager
diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs
index abdb0f695..39b896c0f 100644
--- a/MediaBrowser.Controller/IServerApplicationHost.cs
+++ b/MediaBrowser.Controller/IServerApplicationHost.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Net;
@@ -16,13 +18,7 @@ namespace MediaBrowser.Controller
{
event EventHandler HasUpdateAvailableChanged;
- /// <summary>
- /// Gets the system info.
- /// </summary>
- /// <returns>SystemInfo.</returns>
- Task<SystemInfo> GetSystemInfo(CancellationToken cancellationToken);
-
- Task<PublicSystemInfo> GetPublicSystemInfo(CancellationToken cancellationToken);
+ IServiceProvider ServiceProvider { get; }
bool CanLaunchWebBrowser { get; }
@@ -56,6 +52,14 @@ namespace MediaBrowser.Controller
string FriendlyName { get; }
/// <summary>
+ /// Gets the system info.
+ /// </summary>
+ /// <returns>SystemInfo.</returns>
+ Task<SystemInfo> GetSystemInfo(CancellationToken cancellationToken);
+
+ Task<PublicSystemInfo> GetPublicSystemInfo(CancellationToken cancellationToken);
+
+ /// <summary>
/// Gets all the local IP addresses of this API instance. Each address is validated by sending a 'ping' request
/// to the API that should exist at the address.
/// </summary>
diff --git a/MediaBrowser.Controller/IServerApplicationPaths.cs b/MediaBrowser.Controller/IServerApplicationPaths.cs
index 155bf9177..be57d6bca 100644
--- a/MediaBrowser.Controller/IServerApplicationPaths.cs
+++ b/MediaBrowser.Controller/IServerApplicationPaths.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Common.Configuration;
namespace MediaBrowser.Controller
@@ -81,6 +83,10 @@ namespace MediaBrowser.Controller
/// <value>The internal metadata path.</value>
string InternalMetadataPath { get; }
+ /// <summary>
+ /// Gets the virtual internal metadata path, either a custom path or the default.
+ /// </summary>
+ /// <value>The virtual internal metadata path.</value>
string VirtualInternalMetadataPath { get; }
/// <summary>
diff --git a/MediaBrowser.Controller/Library/DeleteOptions.cs b/MediaBrowser.Controller/Library/DeleteOptions.cs
index 2944d8259..b7417efcb 100644
--- a/MediaBrowser.Controller/Library/DeleteOptions.cs
+++ b/MediaBrowser.Controller/Library/DeleteOptions.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Controller.Library
{
public class DeleteOptions
diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs
index 9abcf2b62..d2f937d4f 100644
--- a/MediaBrowser.Controller/Library/ILibraryManager.cs
+++ b/MediaBrowser.Controller/Library/ILibraryManager.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Threading;
@@ -63,6 +65,7 @@ namespace MediaBrowser.Controller.Library
/// Finds the by path.
/// </summary>
/// <param name="path">The path.</param>
+ /// <param name="isFolder"><c>true</c> is the path is a directory; otherwise <c>false</c>.</param>
/// <returns>BaseItem.</returns>
BaseItem FindByPath(string path, bool? isFolder);
@@ -72,6 +75,7 @@ namespace MediaBrowser.Controller.Library
/// <param name="name">The name.</param>
/// <returns>Task{Artist}.</returns>
MusicArtist GetArtist(string name);
+
MusicArtist GetArtist(string name, DtoOptions options);
/// <summary>
/// Gets a Studio.
@@ -124,7 +128,7 @@ namespace MediaBrowser.Controller.Library
/// </summary>
void QueueLibraryScan();
- void UpdateImages(BaseItem item, bool forceUpdate = false);
+ Task UpdateImagesAsync(BaseItem item, bool forceUpdate = false);
/// <summary>
/// Gets the default view.
@@ -179,6 +183,7 @@ namespace MediaBrowser.Controller.Library
/// <param name="sortOrder">The sort order.</param>
/// <returns>IEnumerable{BaseItem}.</returns>
IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, User user, IEnumerable<string> sortBy, SortOrder sortOrder);
+
IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, User user, IEnumerable<ValueTuple<string, SortOrder>> orderBy);
/// <summary>
@@ -200,9 +205,16 @@ namespace MediaBrowser.Controller.Library
/// <summary>
/// Updates the item.
/// </summary>
- void UpdateItems(IReadOnlyList<BaseItem> items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken);
+ Task UpdateItemsAsync(IReadOnlyList<BaseItem> items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken);
- void UpdateItem(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken);
+ /// <summary>
+ /// Updates the item.
+ /// </summary>
+ /// <param name="item">The item.</param>
+ /// <param name="parent">The parent item.</param>
+ /// <param name="updateReason">The update reason.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ Task UpdateItemAsync(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the item.
@@ -317,7 +329,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(string name,
+ UserView GetNamedView(
+ string name,
string viewType,
string sortName);
@@ -329,7 +342,8 @@ namespace MediaBrowser.Controller.Library
/// <param name="viewType">Type of the view.</param>
/// <param name="sortName">Name of the sort.</param>
/// <param name="uniqueId">The unique identifier.</param>
- UserView GetNamedView(string name,
+ UserView GetNamedView(
+ string name,
Guid parentId,
string viewType,
string sortName,
@@ -341,7 +355,8 @@ namespace MediaBrowser.Controller.Library
/// <param name="parent">The parent.</param>
/// <param name="viewType">Type of the view.</param>
/// <param name="sortName">Name of the sort.</param>
- UserView GetShadowView(BaseItem parent,
+ UserView GetShadowView(
+ BaseItem parent,
string viewType,
string sortName);
@@ -393,7 +408,9 @@ namespace MediaBrowser.Controller.Library
/// <param name="fileSystemChildren">The file system children.</param>
/// <param name="directoryService">The directory service.</param>
/// <returns>IEnumerable&lt;Trailer&gt;.</returns>
- IEnumerable<Video> FindTrailers(BaseItem owner, List<FileSystemMetadata> fileSystemChildren,
+ IEnumerable<Video> FindTrailers(
+ BaseItem owner,
+ List<FileSystemMetadata> fileSystemChildren,
IDirectoryService directoryService);
/// <summary>
@@ -403,7 +420,9 @@ namespace MediaBrowser.Controller.Library
/// <param name="fileSystemChildren">The file system children.</param>
/// <param name="directoryService">The directory service.</param>
/// <returns>IEnumerable&lt;Video&gt;.</returns>
- IEnumerable<Video> FindExtras(BaseItem owner, List<FileSystemMetadata> fileSystemChildren,
+ IEnumerable<Video> FindExtras(
+ BaseItem owner,
+ List<FileSystemMetadata> fileSystemChildren,
IDirectoryService directoryService);
/// <summary>
@@ -522,16 +541,25 @@ namespace MediaBrowser.Controller.Library
Guid GetMusicGenreId(string name);
Task AddVirtualFolder(string name, string collectionType, LibraryOptions options, bool refreshLibrary);
+
Task RemoveVirtualFolder(string name, bool refreshLibrary);
+
void AddMediaPath(string virtualFolderName, MediaPathInfo path);
+
void UpdateMediaPath(string virtualFolderName, MediaPathInfo path);
+
void RemoveMediaPath(string virtualFolderName, string path);
QueryResult<(BaseItem, ItemCounts)> GetGenres(InternalItemsQuery query);
+
QueryResult<(BaseItem, ItemCounts)> GetMusicGenres(InternalItemsQuery query);
+
QueryResult<(BaseItem, ItemCounts)> GetStudios(InternalItemsQuery query);
+
QueryResult<(BaseItem, ItemCounts)> GetArtists(InternalItemsQuery query);
+
QueryResult<(BaseItem, ItemCounts)> GetAlbumArtists(InternalItemsQuery query);
+
QueryResult<(BaseItem, ItemCounts)> GetAllArtists(InternalItemsQuery query);
int GetCount(InternalItemsQuery query);
diff --git a/MediaBrowser.Controller/Library/ILibraryMonitor.cs b/MediaBrowser.Controller/Library/ILibraryMonitor.cs
index 233cfb197..455054bd1 100644
--- a/MediaBrowser.Controller/Library/ILibraryMonitor.cs
+++ b/MediaBrowser.Controller/Library/ILibraryMonitor.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace MediaBrowser.Controller.Library
diff --git a/MediaBrowser.Controller/Library/ILiveStream.cs b/MediaBrowser.Controller/Library/ILiveStream.cs
index 7c9a9b20e..ff25be657 100644
--- a/MediaBrowser.Controller/Library/ILiveStream.cs
+++ b/MediaBrowser.Controller/Library/ILiveStream.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Dto;
@@ -6,8 +8,6 @@ namespace MediaBrowser.Controller.Library
{
public interface ILiveStream
{
- Task Open(CancellationToken openCancellationToken);
- Task Close();
int ConsumerCount { get; set; }
string OriginalStreamId { get; set; }
@@ -19,5 +19,9 @@ namespace MediaBrowser.Controller.Library
MediaSourceInfo MediaSource { get; set; }
string UniqueId { get; }
+
+ Task Open(CancellationToken openCancellationToken);
+
+ Task Close();
}
}
diff --git a/MediaBrowser.Controller/Library/IMediaSourceManager.cs b/MediaBrowser.Controller/Library/IMediaSourceManager.cs
index 94528ff77..9e7b1e608 100644
--- a/MediaBrowser.Controller/Library/IMediaSourceManager.cs
+++ b/MediaBrowser.Controller/Library/IMediaSourceManager.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.IO;
diff --git a/MediaBrowser.Controller/Library/IMetadataFileSaver.cs b/MediaBrowser.Controller/Library/IMetadataFileSaver.cs
index 5b92388ce..9c6f03a23 100644
--- a/MediaBrowser.Controller/Library/IMetadataFileSaver.cs
+++ b/MediaBrowser.Controller/Library/IMetadataFileSaver.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Controller.Entities;
namespace MediaBrowser.Controller.Library
diff --git a/MediaBrowser.Controller/Library/IMusicManager.cs b/MediaBrowser.Controller/Library/IMusicManager.cs
index 36b250ec9..d12f008e7 100644
--- a/MediaBrowser.Controller/Library/IMusicManager.cs
+++ b/MediaBrowser.Controller/Library/IMusicManager.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Dto;
diff --git a/MediaBrowser.Controller/Library/IUserDataManager.cs b/MediaBrowser.Controller/Library/IUserDataManager.cs
index d08ad4cac..c6a83e4dc 100644
--- a/MediaBrowser.Controller/Library/IUserDataManager.cs
+++ b/MediaBrowser.Controller/Library/IUserDataManager.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Threading;
@@ -28,6 +30,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 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 6685861a9..6a4f5cf67 100644
--- a/MediaBrowser.Controller/Library/IUserManager.cs
+++ b/MediaBrowser.Controller/Library/IUserManager.cs
@@ -1,10 +1,12 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
+using Jellyfin.Data.Events;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Library
@@ -20,26 +22,6 @@ namespace MediaBrowser.Controller.Library
event EventHandler<GenericEventArgs<User>> OnUserUpdated;
/// <summary>
- /// Occurs when a user is created.
- /// </summary>
- event EventHandler<GenericEventArgs<User>> OnUserCreated;
-
- /// <summary>
- /// Occurs when a user is deleted.
- /// </summary>
- event EventHandler<GenericEventArgs<User>> OnUserDeleted;
-
- /// <summary>
- /// Occurs when a user's password is changed.
- /// </summary>
- event EventHandler<GenericEventArgs<User>> OnUserPasswordChanged;
-
- /// <summary>
- /// Occurs when a user is locked out.
- /// </summary>
- event EventHandler<GenericEventArgs<User>> OnUserLockedOut;
-
- /// <summary>
/// Gets the users.
/// </summary>
/// <value>The users.</value>
diff --git a/MediaBrowser.Controller/Library/IUserViewManager.cs b/MediaBrowser.Controller/Library/IUserViewManager.cs
index 0d7da7579..8d541e8b6 100644
--- a/MediaBrowser.Controller/Library/IUserViewManager.cs
+++ b/MediaBrowser.Controller/Library/IUserViewManager.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using MediaBrowser.Controller.Dto;
@@ -10,6 +12,7 @@ namespace MediaBrowser.Controller.Library
public interface IUserViewManager
{
Folder[] GetUserViews(UserViewQuery query);
+
UserView GetUserSubView(Guid parentId, string type, string localizationKey, string sortName);
List<Tuple<BaseItem, List<BaseItem>>> GetLatestItems(LatestItemsQuery request, DtoOptions options);
diff --git a/MediaBrowser.Controller/Library/IntroInfo.cs b/MediaBrowser.Controller/Library/IntroInfo.cs
index 0e761d549..283cc631c 100644
--- a/MediaBrowser.Controller/Library/IntroInfo.cs
+++ b/MediaBrowser.Controller/Library/IntroInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace MediaBrowser.Controller.Library
diff --git a/MediaBrowser.Controller/Library/ItemChangeEventArgs.cs b/MediaBrowser.Controller/Library/ItemChangeEventArgs.cs
index b5c48321b..1798a4fad 100644
--- a/MediaBrowser.Controller/Library/ItemChangeEventArgs.cs
+++ b/MediaBrowser.Controller/Library/ItemChangeEventArgs.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Controller.Entities;
namespace MediaBrowser.Controller.Library
diff --git a/MediaBrowser.Controller/Library/ItemResolveArgs.cs b/MediaBrowser.Controller/Library/ItemResolveArgs.cs
index 2e5dcc4c5..6a0dbeba2 100644
--- a/MediaBrowser.Controller/Library/ItemResolveArgs.cs
+++ b/MediaBrowser.Controller/Library/ItemResolveArgs.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Linq;
diff --git a/MediaBrowser.Controller/Library/ItemUpdateType.cs b/MediaBrowser.Controller/Library/ItemUpdateType.cs
index b62f314ba..1f3ebb499 100644
--- a/MediaBrowser.Controller/Library/ItemUpdateType.cs
+++ b/MediaBrowser.Controller/Library/ItemUpdateType.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace MediaBrowser.Controller.Library
diff --git a/MediaBrowser.Controller/Library/LibraryManagerExtensions.cs b/MediaBrowser.Controller/Library/LibraryManagerExtensions.cs
index 037b0b62c..9581603f0 100644
--- a/MediaBrowser.Controller/Library/LibraryManagerExtensions.cs
+++ b/MediaBrowser.Controller/Library/LibraryManagerExtensions.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using MediaBrowser.Controller.Entities;
diff --git a/MediaBrowser.Controller/Library/MetadataConfigurationStore.cs b/MediaBrowser.Controller/Library/MetadataConfigurationStore.cs
index 31adbdcf3..f16304db0 100644
--- a/MediaBrowser.Controller/Library/MetadataConfigurationStore.cs
+++ b/MediaBrowser.Controller/Library/MetadataConfigurationStore.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.Configuration;
diff --git a/MediaBrowser.Controller/Library/NameExtensions.cs b/MediaBrowser.Controller/Library/NameExtensions.cs
index 24d0347e9..21f33ad19 100644
--- a/MediaBrowser.Controller/Library/NameExtensions.cs
+++ b/MediaBrowser.Controller/Library/NameExtensions.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Linq;
diff --git a/MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs b/MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs
index 08cfea3c3..a2be3a42a 100644
--- a/MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs
+++ b/MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using Jellyfin.Data.Entities;
@@ -12,6 +14,11 @@ namespace MediaBrowser.Controller.Library
/// </summary>
public class PlaybackProgressEventArgs : EventArgs
{
+ public PlaybackProgressEventArgs()
+ {
+ Users = new List<User>();
+ }
+
public List<User> Users { get; set; }
public long? PlaybackPositionTicks { get; set; }
@@ -35,10 +42,5 @@ namespace MediaBrowser.Controller.Library
public string PlaySessionId { get; set; }
public SessionInfo Session { get; set; }
-
- public PlaybackProgressEventArgs()
- {
- Users = new List<User>();
- }
}
}
diff --git a/MediaBrowser.Controller/Library/PlaybackStartEventArgs.cs b/MediaBrowser.Controller/Library/PlaybackStartEventArgs.cs
new file mode 100644
index 000000000..ac372bceb
--- /dev/null
+++ b/MediaBrowser.Controller/Library/PlaybackStartEventArgs.cs
@@ -0,0 +1,9 @@
+namespace MediaBrowser.Controller.Library
+{
+ /// <summary>
+ /// An event that occurs when playback is started.
+ /// </summary>
+ public class PlaybackStartEventArgs : PlaybackProgressEventArgs
+ {
+ }
+}
diff --git a/MediaBrowser.Controller/Library/PlaybackStopEventArgs.cs b/MediaBrowser.Controller/Library/PlaybackStopEventArgs.cs
index 12add2573..f0d77ba2d 100644
--- a/MediaBrowser.Controller/Library/PlaybackStopEventArgs.cs
+++ b/MediaBrowser.Controller/Library/PlaybackStopEventArgs.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Controller.Library
{
public class PlaybackStopEventArgs : PlaybackProgressEventArgs
diff --git a/MediaBrowser.Controller/Library/Profiler.cs b/MediaBrowser.Controller/Library/Profiler.cs
index 399378a09..5efdc6a48 100644
--- a/MediaBrowser.Controller/Library/Profiler.cs
+++ b/MediaBrowser.Controller/Library/Profiler.cs
@@ -1,5 +1,6 @@
using System;
using System.Diagnostics;
+using System.Globalization;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.Controller.Library
@@ -13,6 +14,7 @@ namespace MediaBrowser.Controller.Library
/// The name.
/// </summary>
readonly string _name;
+
/// <summary>
/// The stopwatch.
/// </summary>
@@ -44,6 +46,7 @@ namespace MediaBrowser.Controller.Library
public void Dispose()
{
Dispose(true);
+ GC.SuppressFinalize(this);
}
/// <summary>
@@ -58,13 +61,19 @@ namespace MediaBrowser.Controller.Library
string message;
if (_stopwatch.ElapsedMilliseconds > 300000)
{
- message = string.Format("{0} took {1} minutes.",
- _name, ((float)_stopwatch.ElapsedMilliseconds / 60000).ToString("F"));
+ message = string.Format(
+ CultureInfo.InvariantCulture,
+ "{0} took {1} minutes.",
+ _name,
+ ((float)_stopwatch.ElapsedMilliseconds / 60000).ToString("F", CultureInfo.InvariantCulture));
}
else
{
- message = string.Format("{0} took {1} seconds.",
- _name, ((float)_stopwatch.ElapsedMilliseconds / 1000).ToString("#0.000"));
+ message = string.Format(
+ CultureInfo.InvariantCulture,
+ "{0} took {1} seconds.",
+ _name,
+ ((float)_stopwatch.ElapsedMilliseconds / 1000).ToString("#0.000", CultureInfo.InvariantCulture));
}
_logger.LogInformation(message);
diff --git a/MediaBrowser.Controller/Library/UserDataSaveEventArgs.cs b/MediaBrowser.Controller/Library/UserDataSaveEventArgs.cs
index fa0192784..cd9109753 100644
--- a/MediaBrowser.Controller/Library/UserDataSaveEventArgs.cs
+++ b/MediaBrowser.Controller/Library/UserDataSaveEventArgs.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using MediaBrowser.Controller.Entities;
diff --git a/MediaBrowser.Controller/LiveTv/ChannelInfo.cs b/MediaBrowser.Controller/LiveTv/ChannelInfo.cs
index 67d0df4fd..d7afd2118 100644
--- a/MediaBrowser.Controller/LiveTv/ChannelInfo.cs
+++ b/MediaBrowser.Controller/LiveTv/ChannelInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Model.LiveTv;
namespace MediaBrowser.Controller.LiveTv
diff --git a/MediaBrowser.Controller/LiveTv/IListingsProvider.cs b/MediaBrowser.Controller/LiveTv/IListingsProvider.cs
index 2ea0a748e..038ff2eae 100644
--- a/MediaBrowser.Controller/LiveTv/IListingsProvider.cs
+++ b/MediaBrowser.Controller/LiveTv/IListingsProvider.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Threading;
diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
index f619b011b..55c330931 100644
--- a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
+++ b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
@@ -1,13 +1,15 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
+using Jellyfin.Data.Events;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Querying;
@@ -105,6 +107,7 @@ namespace MediaBrowser.Controller.LiveTv
/// </summary>
/// <param name="id">The identifier.</param>
/// <param name="mediaSourceId">The media source identifier.</param>
+ /// <param name="currentLiveStreams">The current live streams.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{StreamResponseInfo}.</returns>
Task<Tuple<MediaSourceInfo, ILiveStream>> GetChannelStream(string id, string mediaSourceId, List<ILiveStream> currentLiveStreams, CancellationToken cancellationToken);
diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvService.cs b/MediaBrowser.Controller/LiveTv/ILiveTvService.cs
index b71a76648..3ca1d165e 100644
--- a/MediaBrowser.Controller/LiveTv/ILiveTvService.cs
+++ b/MediaBrowser.Controller/LiveTv/ILiveTvService.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Threading;
diff --git a/MediaBrowser.Controller/LiveTv/ITunerHost.cs b/MediaBrowser.Controller/LiveTv/ITunerHost.cs
index 3679e4f78..ff92bf856 100644
--- a/MediaBrowser.Controller/LiveTv/ITunerHost.cs
+++ b/MediaBrowser.Controller/LiveTv/ITunerHost.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
@@ -14,28 +16,37 @@ namespace MediaBrowser.Controller.LiveTv
/// </summary>
/// <value>The name.</value>
string Name { get; }
+
/// <summary>
/// Gets the type.
/// </summary>
/// <value>The type.</value>
string Type { get; }
+
+ bool IsSupported { get; }
+
/// <summary>
/// Gets the channels.
/// </summary>
/// <returns>Task&lt;IEnumerable&lt;ChannelInfo&gt;&gt;.</returns>
Task<List<ChannelInfo>> GetChannels(bool enableCache, CancellationToken cancellationToken);
+
/// <summary>
/// Gets the tuner infos.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task&lt;List&lt;LiveTvTunerInfo&gt;&gt;.</returns>
Task<List<LiveTvTunerInfo>> GetTunerInfos(CancellationToken cancellationToken);
+
/// <summary>
/// Gets the channel stream.
/// </summary>
/// <param name="channelId">The channel identifier.</param>
/// <param name="streamId">The stream identifier.</param>
+ /// <param name="currentLiveStreams">The current live streams.</param>
+ /// <param name="cancellationToken">The cancellation token to cancel operation.</param>
Task<ILiveStream> GetChannelStream(string channelId, string streamId, List<ILiveStream> currentLiveStreams, CancellationToken cancellationToken);
+
/// <summary>
/// Gets the channel stream media sources.
/// </summary>
@@ -45,10 +56,7 @@ namespace MediaBrowser.Controller.LiveTv
Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken);
Task<List<TunerHostInfo>> DiscoverDevices(int discoveryDurationMs, CancellationToken cancellationToken);
- bool IsSupported
- {
- get;
- }
+
}
public interface IConfigurableTunerHost
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs
index 10af98121..ec933caf3 100644
--- a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs
+++ b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -63,7 +65,7 @@ namespace MediaBrowser.Controller.LiveTv
if (double.TryParse(Number, NumberStyles.Any, CultureInfo.InvariantCulture, out number))
{
- return string.Format("{0:00000.0}", number) + "-" + (Name ?? string.Empty);
+ return string.Format(CultureInfo.InvariantCulture, "{0:00000.0}", number) + "-" + (Name ?? string.Empty);
}
}
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvConflictException.cs b/MediaBrowser.Controller/LiveTv/LiveTvConflictException.cs
index 0e09d1aeb..881c42c73 100644
--- a/MediaBrowser.Controller/LiveTv/LiveTvConflictException.cs
+++ b/MediaBrowser.Controller/LiveTv/LiveTvConflictException.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace MediaBrowser.Controller.LiveTv
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
index 472b061e6..43af495dd 100644
--- a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
+++ b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -261,7 +263,7 @@ namespace MediaBrowser.Controller.LiveTv
list.Add(new ExternalUrl
{
Name = "Trakt",
- Url = string.Format("https://trakt.tv/movies/{0}", imdbId)
+ Url = string.Format(CultureInfo.InvariantCulture, "https://trakt.tv/movies/{0}", imdbId)
});
}
}
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvServiceStatusInfo.cs b/MediaBrowser.Controller/LiveTv/LiveTvServiceStatusInfo.cs
index 67b2f0eb1..02178297b 100644
--- a/MediaBrowser.Controller/LiveTv/LiveTvServiceStatusInfo.cs
+++ b/MediaBrowser.Controller/LiveTv/LiveTvServiceStatusInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
using MediaBrowser.Model.LiveTv;
@@ -5,6 +7,12 @@ namespace MediaBrowser.Controller.LiveTv
{
public class LiveTvServiceStatusInfo
{
+ public LiveTvServiceStatusInfo()
+ {
+ Tuners = new List<LiveTvTunerInfo>();
+ IsVisible = true;
+ }
+
/// <summary>
/// Gets or sets the status.
/// </summary>
@@ -39,11 +47,5 @@ namespace MediaBrowser.Controller.LiveTv
/// </summary>
/// <value><c>true</c> if this instance is visible; otherwise, <c>false</c>.</value>
public bool IsVisible { get; set; }
-
- public LiveTvServiceStatusInfo()
- {
- Tuners = new List<LiveTvTunerInfo>();
- IsVisible = true;
- }
}
}
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvTunerInfo.cs b/MediaBrowser.Controller/LiveTv/LiveTvTunerInfo.cs
index 2857f73f6..739978e7c 100644
--- a/MediaBrowser.Controller/LiveTv/LiveTvTunerInfo.cs
+++ b/MediaBrowser.Controller/LiveTv/LiveTvTunerInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
using MediaBrowser.Model.LiveTv;
@@ -5,6 +7,11 @@ namespace MediaBrowser.Controller.LiveTv
{
public class LiveTvTunerInfo
{
+ public LiveTvTunerInfo()
+ {
+ Clients = new List<string>();
+ }
+
/// <summary>
/// Gets or sets the type of the source.
/// </summary>
@@ -64,10 +71,5 @@ namespace MediaBrowser.Controller.LiveTv
/// </summary>
/// <value><c>true</c> if this instance can reset; otherwise, <c>false</c>.</value>
public bool CanReset { get; set; }
-
- public LiveTvTunerInfo()
- {
- Clients = new List<string>();
- }
}
}
diff --git a/MediaBrowser.Controller/LiveTv/ProgramInfo.cs b/MediaBrowser.Controller/LiveTv/ProgramInfo.cs
index d06a15323..bdcffd5ca 100644
--- a/MediaBrowser.Controller/LiveTv/ProgramInfo.cs
+++ b/MediaBrowser.Controller/LiveTv/ProgramInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using MediaBrowser.Model.LiveTv;
diff --git a/MediaBrowser.Controller/LiveTv/RecordingInfo.cs b/MediaBrowser.Controller/LiveTv/RecordingInfo.cs
index b9e0218ab..303882b7e 100644
--- a/MediaBrowser.Controller/LiveTv/RecordingInfo.cs
+++ b/MediaBrowser.Controller/LiveTv/RecordingInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using MediaBrowser.Model.LiveTv;
diff --git a/MediaBrowser.Controller/LiveTv/RecordingStatusChangedEventArgs.cs b/MediaBrowser.Controller/LiveTv/RecordingStatusChangedEventArgs.cs
index 99460a686..847c0ea8c 100644
--- a/MediaBrowser.Controller/LiveTv/RecordingStatusChangedEventArgs.cs
+++ b/MediaBrowser.Controller/LiveTv/RecordingStatusChangedEventArgs.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using MediaBrowser.Model.LiveTv;
diff --git a/MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs b/MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs
index 6e7acaae3..1343ecd98 100644
--- a/MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs
+++ b/MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using MediaBrowser.Model.LiveTv;
diff --git a/MediaBrowser.Controller/LiveTv/TimerInfo.cs b/MediaBrowser.Controller/LiveTv/TimerInfo.cs
index df98bb6af..bcef4666d 100644
--- a/MediaBrowser.Controller/LiveTv/TimerInfo.cs
+++ b/MediaBrowser.Controller/LiveTv/TimerInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Linq;
diff --git a/MediaBrowser.Controller/LiveTv/TunerChannelMapping.cs b/MediaBrowser.Controller/LiveTv/TunerChannelMapping.cs
index df3f55c26..2759b314f 100644
--- a/MediaBrowser.Controller/LiveTv/TunerChannelMapping.cs
+++ b/MediaBrowser.Controller/LiveTv/TunerChannelMapping.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Controller.LiveTv
{
public class TunerChannelMapping
diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
index 67f17f7a5..df92eda38 100644
--- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj
+++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
@@ -8,8 +8,9 @@
<PropertyGroup>
<Authors>Jellyfin Contributors</Authors>
<PackageId>Jellyfin.Controller</PackageId>
- <PackageLicenseUrl>https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt</PackageLicenseUrl>
+ <VersionPrefix>10.7.0</VersionPrefix>
<RepositoryUrl>https://github.com/jellyfin/jellyfin</RepositoryUrl>
+ <PackageLicenseExpression>GPL-3.0-only</PackageLicenseExpression>
</PropertyGroup>
<ItemGroup>
@@ -30,6 +31,7 @@
<TargetFramework>netstandard2.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
+ <TreatWarningsAsErrors Condition=" '$(Configuration)' == 'Release' ">true</TreatWarningsAsErrors>
</PropertyGroup>
<!-- Code Analyzers-->
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index 2dd21be3c..550916f82 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -675,7 +677,7 @@ namespace MediaBrowser.Controller.MediaEncoding
// }
// }
- // fallbackFontParam = string.Format(":force_style='FontName=Droid Sans Fallback':fontsdir='{0}'", _mediaEncoder.EscapeSubtitleFilterPath(_fileSystem.GetDirectoryName(fallbackFontPath)));
+ // fallbackFontParam = string.Format(CultureInfo.InvariantCulture, ":force_style='FontName=Droid Sans Fallback':fontsdir='{0}'", _mediaEncoder.EscapeSubtitleFilterPath(_fileSystem.GetDirectoryName(fallbackFontPath)));
if (state.SubtitleStream.IsExternal)
{
@@ -880,7 +882,7 @@ namespace MediaBrowser.Controller.MediaEncoding
profileScore = Math.Min(profileScore, 2);
// http://www.webmproject.org/docs/encoder-parameters/
- param += string.Format("-speed 16 -quality good -profile:v {0} -slices 8 -crf {1} -qmin {2} -qmax {3}",
+ param += string.Format(CultureInfo.InvariantCulture, "-speed 16 -quality good -profile:v {0} -slices 8 -crf {1} -qmin {2} -qmax {3}",
profileScore.ToString(_usCulture),
crf,
qmin,
@@ -904,7 +906,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var framerate = GetFramerateParam(state);
if (framerate.HasValue)
{
- param += string.Format(" -r {0}", framerate.Value.ToString(_usCulture));
+ param += string.Format(CultureInfo.InvariantCulture, " -r {0}", framerate.Value.ToString(_usCulture));
}
var targetVideoCodec = state.ActualOutputVideoCodec;
@@ -1484,7 +1486,7 @@ namespace MediaBrowser.Controller.MediaEncoding
if (time > 0)
{
- return string.Format("-ss {0}", _mediaEncoder.GetTimeParameter(time));
+ return string.Format(CultureInfo.InvariantCulture, "-ss {0}", _mediaEncoder.GetTimeParameter(time));
}
return string.Empty;
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
index b971b7c4b..68bc502a0 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Globalization;
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs
index 8f6fcb9ab..4cbb63e46 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Linq;
diff --git a/MediaBrowser.Controller/MediaEncoding/IAttachmentExtractor.cs b/MediaBrowser.Controller/MediaEncoding/IAttachmentExtractor.cs
index 7c7e84de6..fbc827534 100644
--- a/MediaBrowser.Controller/MediaEncoding/IAttachmentExtractor.cs
+++ b/MediaBrowser.Controller/MediaEncoding/IAttachmentExtractor.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.IO;
using System.Threading;
using System.Threading.Tasks;
diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
index f60e70239..17d6dc5d2 100644
--- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
+++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Threading;
diff --git a/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs b/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs
index 174e74f34..6ebf7f159 100644
--- a/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs
+++ b/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.IO;
using System.Threading;
using System.Threading.Tasks;
@@ -12,7 +14,8 @@ namespace MediaBrowser.Controller.MediaEncoding
/// Gets the subtitles.
/// </summary>
/// <returns>Task{Stream}.</returns>
- Task<Stream> GetSubtitles(BaseItem item,
+ Task<Stream> GetSubtitles(
+ BaseItem item,
string mediaSourceId,
int subtitleStreamIndex,
string outputFormat,
@@ -25,6 +28,7 @@ namespace MediaBrowser.Controller.MediaEncoding
/// Gets the subtitle language encoding parameter.
/// </summary>
/// <param name="path">The path.</param>
+ /// <param name="language">The language.</param>
/// <param name="protocol">The protocol.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>System.String.</returns>
diff --git a/MediaBrowser.Controller/MediaEncoding/ImageEncodingOptions.cs b/MediaBrowser.Controller/MediaEncoding/ImageEncodingOptions.cs
index 361dd79dc..e7b4c8c15 100644
--- a/MediaBrowser.Controller/MediaEncoding/ImageEncodingOptions.cs
+++ b/MediaBrowser.Controller/MediaEncoding/ImageEncodingOptions.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Controller.MediaEncoding
{
public class ImageEncodingOptions
diff --git a/MediaBrowser.Controller/MediaEncoding/JobLogger.cs b/MediaBrowser.Controller/MediaEncoding/JobLogger.cs
index c9f64c707..ac520c5c4 100644
--- a/MediaBrowser.Controller/MediaEncoding/JobLogger.cs
+++ b/MediaBrowser.Controller/MediaEncoding/JobLogger.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Globalization;
using System.IO;
diff --git a/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs b/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs
index 6c9bbb043..ce53c23ad 100644
--- a/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs
+++ b/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.IO;
diff --git a/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs b/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs
index 39a47792a..59729de49 100644
--- a/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs
+++ b/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto;
diff --git a/MediaBrowser.Controller/Net/AuthenticatedAttribute.cs b/MediaBrowser.Controller/Net/AuthenticatedAttribute.cs
index 87a7f7e10..1366fd42e 100644
--- a/MediaBrowser.Controller/Net/AuthenticatedAttribute.cs
+++ b/MediaBrowser.Controller/Net/AuthenticatedAttribute.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using MediaBrowser.Model.Services;
using Microsoft.AspNetCore.Http;
@@ -53,7 +55,7 @@ namespace MediaBrowser.Controller.Net
}
public bool IgnoreLegacyAuth { get; set; }
-
+
public bool AllowLocalOnly { get; set; }
}
@@ -68,7 +70,7 @@ namespace MediaBrowser.Controller.Net
bool AllowLocalOnly { get; }
string[] GetRoles();
-
+
bool IgnoreLegacyAuth { get; }
}
}
diff --git a/MediaBrowser.Controller/Net/AuthorizationInfo.cs b/MediaBrowser.Controller/Net/AuthorizationInfo.cs
index 4361e253b..735c46ef8 100644
--- a/MediaBrowser.Controller/Net/AuthorizationInfo.cs
+++ b/MediaBrowser.Controller/Net/AuthorizationInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using Jellyfin.Data.Entities;
diff --git a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs
index a54f6d57b..916dea58b 100644
--- a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs
+++ b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Globalization;
diff --git a/MediaBrowser.Controller/Net/IHttpResultFactory.cs b/MediaBrowser.Controller/Net/IHttpResultFactory.cs
index 609bd5f59..8293a8714 100644
--- a/MediaBrowser.Controller/Net/IHttpResultFactory.cs
+++ b/MediaBrowser.Controller/Net/IHttpResultFactory.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.IO;
@@ -21,7 +23,9 @@ namespace MediaBrowser.Controller.Net
object GetResult(string content, string contentType, IDictionary<string, string> responseHeaders = null);
object GetResult(IRequest requestContext, byte[] content, string contentType, IDictionary<string, string> responseHeaders = null);
+
object GetResult(IRequest requestContext, Stream content, string contentType, IDictionary<string, string> responseHeaders = null);
+
object GetResult(IRequest requestContext, string content, string contentType, IDictionary<string, string> responseHeaders = null);
object GetRedirectResult(string url);
diff --git a/MediaBrowser.Controller/Net/IHttpServer.cs b/MediaBrowser.Controller/Net/IHttpServer.cs
index e6609fae3..b04ebda8c 100644
--- a/MediaBrowser.Controller/Net/IHttpServer.cs
+++ b/MediaBrowser.Controller/Net/IHttpServer.cs
@@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
-using MediaBrowser.Model.Events;
+using Jellyfin.Data.Events;
using MediaBrowser.Model.Services;
using Microsoft.AspNetCore.Http;
diff --git a/MediaBrowser.Controller/Net/ISessionContext.cs b/MediaBrowser.Controller/Net/ISessionContext.cs
index 421ac3fe2..5da748f41 100644
--- a/MediaBrowser.Controller/Net/ISessionContext.cs
+++ b/MediaBrowser.Controller/Net/ISessionContext.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Services;
@@ -7,9 +9,11 @@ namespace MediaBrowser.Controller.Net
public interface ISessionContext
{
SessionInfo GetSession(object requestContext);
+
User GetUser(object requestContext);
SessionInfo GetSession(IRequest requestContext);
+
User GetUser(IRequest requestContext);
}
}
diff --git a/MediaBrowser.Controller/Net/IWebSocketConnection.cs b/MediaBrowser.Controller/Net/IWebSocketConnection.cs
index 3ef8e5f6d..e87f3bca6 100644
--- a/MediaBrowser.Controller/Net/IWebSocketConnection.cs
+++ b/MediaBrowser.Controller/Net/IWebSocketConnection.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
#nullable enable
using System;
diff --git a/MediaBrowser.Controller/Net/SecurityException.cs b/MediaBrowser.Controller/Net/SecurityException.cs
index f0d0b45a0..c6347133a 100644
--- a/MediaBrowser.Controller/Net/SecurityException.cs
+++ b/MediaBrowser.Controller/Net/SecurityException.cs
@@ -1,3 +1,5 @@
+#nullable enable
+
using System;
namespace MediaBrowser.Controller.Net
diff --git a/MediaBrowser.Controller/Net/StaticResultOptions.cs b/MediaBrowser.Controller/Net/StaticResultOptions.cs
index 85772e036..c1e9bc845 100644
--- a/MediaBrowser.Controller/Net/StaticResultOptions.cs
+++ b/MediaBrowser.Controller/Net/StaticResultOptions.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.IO;
diff --git a/MediaBrowser.Controller/Notifications/INotificationManager.cs b/MediaBrowser.Controller/Notifications/INotificationManager.cs
index 44defbe0b..08d9bc12a 100644
--- a/MediaBrowser.Controller/Notifications/INotificationManager.cs
+++ b/MediaBrowser.Controller/Notifications/INotificationManager.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
diff --git a/MediaBrowser.Controller/Notifications/INotificationService.cs b/MediaBrowser.Controller/Notifications/INotificationService.cs
index ab5eb13cd..fa947220a 100644
--- a/MediaBrowser.Controller/Notifications/INotificationService.cs
+++ b/MediaBrowser.Controller/Notifications/INotificationService.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
diff --git a/MediaBrowser.Controller/Notifications/INotificationTypeFactory.cs b/MediaBrowser.Controller/Notifications/INotificationTypeFactory.cs
index 9f1d2841d..52a3e120b 100644
--- a/MediaBrowser.Controller/Notifications/INotificationTypeFactory.cs
+++ b/MediaBrowser.Controller/Notifications/INotificationTypeFactory.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
using MediaBrowser.Model.Notifications;
diff --git a/MediaBrowser.Controller/Notifications/UserNotification.cs b/MediaBrowser.Controller/Notifications/UserNotification.cs
index a1029589b..d768abfe7 100644
--- a/MediaBrowser.Controller/Notifications/UserNotification.cs
+++ b/MediaBrowser.Controller/Notifications/UserNotification.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using Jellyfin.Data.Entities;
using MediaBrowser.Model.Notifications;
diff --git a/MediaBrowser.Controller/Persistence/IItemRepository.cs b/MediaBrowser.Controller/Persistence/IItemRepository.cs
index 0ae1b8bbf..ebc37bd1f 100644
--- a/MediaBrowser.Controller/Persistence/IItemRepository.cs
+++ b/MediaBrowser.Controller/Persistence/IItemRepository.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Threading;
@@ -156,15 +158,23 @@ namespace MediaBrowser.Controller.Persistence
int GetCount(InternalItemsQuery query);
QueryResult<(BaseItem, ItemCounts)> GetGenres(InternalItemsQuery query);
+
QueryResult<(BaseItem, ItemCounts)> GetMusicGenres(InternalItemsQuery query);
+
QueryResult<(BaseItem, ItemCounts)> GetStudios(InternalItemsQuery query);
+
QueryResult<(BaseItem, ItemCounts)> GetArtists(InternalItemsQuery query);
+
QueryResult<(BaseItem, ItemCounts)> GetAlbumArtists(InternalItemsQuery query);
+
QueryResult<(BaseItem, ItemCounts)> GetAllArtists(InternalItemsQuery query);
List<string> GetMusicGenreNames();
+
List<string> GetStudioNames();
+
List<string> GetGenreNames();
+
List<string> GetAllArtistNames();
}
}
diff --git a/MediaBrowser.Controller/Persistence/IUserDataRepository.cs b/MediaBrowser.Controller/Persistence/IUserDataRepository.cs
index ba7c9fd50..81ba513ce 100644
--- a/MediaBrowser.Controller/Persistence/IUserDataRepository.cs
+++ b/MediaBrowser.Controller/Persistence/IUserDataRepository.cs
@@ -24,9 +24,15 @@ namespace MediaBrowser.Controller.Persistence
/// </summary>
/// <param name="userId">The user id.</param>
/// <param name="key">The key.</param>
- /// <returns>Task{UserItemData}.</returns>
+ /// <returns>The user data.</returns>
UserItemData GetUserData(long userId, string key);
+ /// <summary>
+ /// Gets the user data.
+ /// </summary>
+ /// <param name="userId">The user id.</param>
+ /// <param name="keys">The keys.</param>
+ /// <returns>The user data.</returns>
UserItemData GetUserData(long userId, List<string> keys);
/// <summary>
diff --git a/MediaBrowser.Controller/Persistence/MediaAttachmentQuery.cs b/MediaBrowser.Controller/Persistence/MediaAttachmentQuery.cs
index e3b2d4665..e07e96f73 100644
--- a/MediaBrowser.Controller/Persistence/MediaAttachmentQuery.cs
+++ b/MediaBrowser.Controller/Persistence/MediaAttachmentQuery.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace MediaBrowser.Controller.Persistence
diff --git a/MediaBrowser.Controller/Persistence/MediaStreamQuery.cs b/MediaBrowser.Controller/Persistence/MediaStreamQuery.cs
index 7dc563b3a..f9295c8fd 100644
--- a/MediaBrowser.Controller/Persistence/MediaStreamQuery.cs
+++ b/MediaBrowser.Controller/Persistence/MediaStreamQuery.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using MediaBrowser.Model.Entities;
diff --git a/MediaBrowser.Controller/Playlists/IPlaylistManager.cs b/MediaBrowser.Controller/Playlists/IPlaylistManager.cs
index 544cd2643..fbf2c5213 100644
--- a/MediaBrowser.Controller/Playlists/IPlaylistManager.cs
+++ b/MediaBrowser.Controller/Playlists/IPlaylistManager.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
@@ -29,7 +31,7 @@ namespace MediaBrowser.Controller.Playlists
/// <param name="itemIds">The item ids.</param>
/// <param name="userId">The user identifier.</param>
/// <returns>Task.</returns>
- void AddToPlaylist(string playlistId, ICollection<Guid> itemIds, Guid userId);
+ Task AddToPlaylistAsync(Guid playlistId, ICollection<Guid> itemIds, Guid userId);
/// <summary>
/// Removes from playlist.
@@ -37,7 +39,7 @@ namespace MediaBrowser.Controller.Playlists
/// <param name="playlistId">The playlist identifier.</param>
/// <param name="entryIds">The entry ids.</param>
/// <returns>Task.</returns>
- void RemoveFromPlaylist(string playlistId, IEnumerable<string> entryIds);
+ Task RemoveFromPlaylistAsync(string playlistId, IEnumerable<string> entryIds);
/// <summary>
/// Gets the playlists folder.
@@ -53,6 +55,6 @@ namespace MediaBrowser.Controller.Playlists
/// <param name="entryId">The entry identifier.</param>
/// <param name="newIndex">The new index.</param>
/// <returns>Task.</returns>
- void MoveItem(string playlistId, string entryId, int newIndex);
+ Task MoveItemAsync(string playlistId, string entryId, int newIndex);
}
}
diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs
index 0fd63770f..216dd2709 100644
--- a/MediaBrowser.Controller/Playlists/Playlist.cs
+++ b/MediaBrowser.Controller/Playlists/Playlist.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Globalization;
diff --git a/MediaBrowser.Controller/Plugins/ILocalizablePlugin.cs b/MediaBrowser.Controller/Plugins/ILocalizablePlugin.cs
index 5deb165f6..bf15fe040 100644
--- a/MediaBrowser.Controller/Plugins/ILocalizablePlugin.cs
+++ b/MediaBrowser.Controller/Plugins/ILocalizablePlugin.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.IO;
using System.Reflection;
diff --git a/MediaBrowser.Controller/Plugins/IPluginConfigurationPage.cs b/MediaBrowser.Controller/Plugins/IPluginConfigurationPage.cs
index 077f5ab63..93eab42cc 100644
--- a/MediaBrowser.Controller/Plugins/IPluginConfigurationPage.cs
+++ b/MediaBrowser.Controller/Plugins/IPluginConfigurationPage.cs
@@ -42,6 +42,7 @@ namespace MediaBrowser.Controller.Plugins
/// The plugin configuration.
/// </summary>
PluginConfiguration,
+
/// <summary>
/// The none.
/// </summary>
diff --git a/MediaBrowser.Controller/Providers/AlbumInfo.cs b/MediaBrowser.Controller/Providers/AlbumInfo.cs
index dbda4843f..276bcf125 100644
--- a/MediaBrowser.Controller/Providers/AlbumInfo.cs
+++ b/MediaBrowser.Controller/Providers/AlbumInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
diff --git a/MediaBrowser.Controller/Providers/ArtistInfo.cs b/MediaBrowser.Controller/Providers/ArtistInfo.cs
index 08bf3982b..adf885baa 100644
--- a/MediaBrowser.Controller/Providers/ArtistInfo.cs
+++ b/MediaBrowser.Controller/Providers/ArtistInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
namespace MediaBrowser.Controller.Providers
diff --git a/MediaBrowser.Controller/Providers/BookInfo.cs b/MediaBrowser.Controller/Providers/BookInfo.cs
index 03a6737c5..cce0a25fc 100644
--- a/MediaBrowser.Controller/Providers/BookInfo.cs
+++ b/MediaBrowser.Controller/Providers/BookInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Controller.Providers
{
public class BookInfo : ItemLookupInfo
diff --git a/MediaBrowser.Controller/Providers/BoxSetInfo.cs b/MediaBrowser.Controller/Providers/BoxSetInfo.cs
index d23f2b9bf..f43ea6717 100644
--- a/MediaBrowser.Controller/Providers/BoxSetInfo.cs
+++ b/MediaBrowser.Controller/Providers/BoxSetInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Controller.Providers
{
public class BoxSetInfo : ItemLookupInfo
diff --git a/MediaBrowser.Controller/Providers/DirectoryService.cs b/MediaBrowser.Controller/Providers/DirectoryService.cs
index b7640c205..f77455485 100644
--- a/MediaBrowser.Controller/Providers/DirectoryService.cs
+++ b/MediaBrowser.Controller/Providers/DirectoryService.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Linq;
diff --git a/MediaBrowser.Controller/Providers/DynamicImageResponse.cs b/MediaBrowser.Controller/Providers/DynamicImageResponse.cs
index 7c1371702..006174be8 100644
--- a/MediaBrowser.Controller/Providers/DynamicImageResponse.cs
+++ b/MediaBrowser.Controller/Providers/DynamicImageResponse.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.IO;
using MediaBrowser.Model.Drawing;
diff --git a/MediaBrowser.Controller/Providers/EpisodeInfo.cs b/MediaBrowser.Controller/Providers/EpisodeInfo.cs
index 55c41ff82..a4c8dab7e 100644
--- a/MediaBrowser.Controller/Providers/EpisodeInfo.cs
+++ b/MediaBrowser.Controller/Providers/EpisodeInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
diff --git a/MediaBrowser.Controller/Providers/ICustomMetadataProvider.cs b/MediaBrowser.Controller/Providers/ICustomMetadataProvider.cs
index 6b4c9feb5..32a9cbef2 100644
--- a/MediaBrowser.Controller/Providers/ICustomMetadataProvider.cs
+++ b/MediaBrowser.Controller/Providers/ICustomMetadataProvider.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Entities;
diff --git a/MediaBrowser.Controller/Providers/IDirectoryService.cs b/MediaBrowser.Controller/Providers/IDirectoryService.cs
index 949a17740..f06481c7a 100644
--- a/MediaBrowser.Controller/Providers/IDirectoryService.cs
+++ b/MediaBrowser.Controller/Providers/IDirectoryService.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
using MediaBrowser.Model.IO;
diff --git a/MediaBrowser.Controller/Providers/IDynamicImageProvider.cs b/MediaBrowser.Controller/Providers/IDynamicImageProvider.cs
index dec327d66..ab66462fa 100644
--- a/MediaBrowser.Controller/Providers/IDynamicImageProvider.cs
+++ b/MediaBrowser.Controller/Providers/IDynamicImageProvider.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
diff --git a/MediaBrowser.Controller/Providers/IHasItemChangeMonitor.cs b/MediaBrowser.Controller/Providers/IHasItemChangeMonitor.cs
index 68acb3910..a0e20e312 100644
--- a/MediaBrowser.Controller/Providers/IHasItemChangeMonitor.cs
+++ b/MediaBrowser.Controller/Providers/IHasItemChangeMonitor.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Controller.Entities;
namespace MediaBrowser.Controller.Providers
diff --git a/MediaBrowser.Controller/Providers/IHasLookupInfo.cs b/MediaBrowser.Controller/Providers/IHasLookupInfo.cs
index 4c0c38442..42cb52371 100644
--- a/MediaBrowser.Controller/Providers/IHasLookupInfo.cs
+++ b/MediaBrowser.Controller/Providers/IHasLookupInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Controller.Providers
{
public interface IHasLookupInfo<out TLookupInfoType>
diff --git a/MediaBrowser.Controller/Providers/IHasOrder.cs b/MediaBrowser.Controller/Providers/IHasOrder.cs
index a3db61225..9fde0e695 100644
--- a/MediaBrowser.Controller/Providers/IHasOrder.cs
+++ b/MediaBrowser.Controller/Providers/IHasOrder.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Controller.Providers
{
public interface IHasOrder
diff --git a/MediaBrowser.Controller/Providers/ILocalImageProvider.cs b/MediaBrowser.Controller/Providers/ILocalImageProvider.cs
index 463c81376..c129eddb3 100644
--- a/MediaBrowser.Controller/Providers/ILocalImageProvider.cs
+++ b/MediaBrowser.Controller/Providers/ILocalImageProvider.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
using MediaBrowser.Controller.Entities;
diff --git a/MediaBrowser.Controller/Providers/ILocalMetadataProvider.cs b/MediaBrowser.Controller/Providers/ILocalMetadataProvider.cs
index 44fb1b394..e771c881d 100644
--- a/MediaBrowser.Controller/Providers/ILocalMetadataProvider.cs
+++ b/MediaBrowser.Controller/Providers/ILocalMetadataProvider.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Entities;
diff --git a/MediaBrowser.Controller/Providers/IMetadataProvider.cs b/MediaBrowser.Controller/Providers/IMetadataProvider.cs
index 62b16dadd..1a87e0625 100644
--- a/MediaBrowser.Controller/Providers/IMetadataProvider.cs
+++ b/MediaBrowser.Controller/Providers/IMetadataProvider.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Controller.Entities;
namespace MediaBrowser.Controller.Providers
diff --git a/MediaBrowser.Controller/Providers/IMetadataService.cs b/MediaBrowser.Controller/Providers/IMetadataService.cs
index 21204e6d3..5f3d4274e 100644
--- a/MediaBrowser.Controller/Providers/IMetadataService.cs
+++ b/MediaBrowser.Controller/Providers/IMetadataService.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Threading;
using System.Threading.Tasks;
diff --git a/MediaBrowser.Controller/Providers/IPreRefreshProvider.cs b/MediaBrowser.Controller/Providers/IPreRefreshProvider.cs
index 28da27ae7..6d98af33e 100644
--- a/MediaBrowser.Controller/Providers/IPreRefreshProvider.cs
+++ b/MediaBrowser.Controller/Providers/IPreRefreshProvider.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Controller.Providers
{
public interface IPreRefreshProvider : ICustomMetadataProvider
diff --git a/MediaBrowser.Controller/Providers/IProviderManager.cs b/MediaBrowser.Controller/Providers/IProviderManager.cs
index 8ba01d773..996ec27c0 100644
--- a/MediaBrowser.Controller/Providers/IProviderManager.cs
+++ b/MediaBrowser.Controller/Providers/IProviderManager.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.IO;
@@ -5,12 +7,12 @@ using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
+using Jellyfin.Data.Events;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.Providers;
namespace MediaBrowser.Controller.Providers
@@ -72,7 +74,7 @@ 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);
+ Task SaveImage(Stream source, string mimeType, string path);
/// <summary>
/// Adds the metadata providers.
diff --git a/MediaBrowser.Controller/Providers/IRemoteMetadataProvider.cs b/MediaBrowser.Controller/Providers/IRemoteMetadataProvider.cs
index c143b15cd..f146decb6 100644
--- a/MediaBrowser.Controller/Providers/IRemoteMetadataProvider.cs
+++ b/MediaBrowser.Controller/Providers/IRemoteMetadataProvider.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
diff --git a/MediaBrowser.Controller/Providers/IRemoteSearchProvider.cs b/MediaBrowser.Controller/Providers/IRemoteSearchProvider.cs
index 17ad9e4a3..9592baa7c 100644
--- a/MediaBrowser.Controller/Providers/IRemoteSearchProvider.cs
+++ b/MediaBrowser.Controller/Providers/IRemoteSearchProvider.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
diff --git a/MediaBrowser.Controller/Providers/ImageRefreshOptions.cs b/MediaBrowser.Controller/Providers/ImageRefreshOptions.cs
index 3f8c409f5..9fc379f04 100644
--- a/MediaBrowser.Controller/Providers/ImageRefreshOptions.cs
+++ b/MediaBrowser.Controller/Providers/ImageRefreshOptions.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Linq;
using MediaBrowser.Model.Entities;
diff --git a/MediaBrowser.Controller/Providers/ItemInfo.cs b/MediaBrowser.Controller/Providers/ItemInfo.cs
index d61153dfa..b50def043 100644
--- a/MediaBrowser.Controller/Providers/ItemInfo.cs
+++ b/MediaBrowser.Controller/Providers/ItemInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Entities;
diff --git a/MediaBrowser.Controller/Providers/ItemLookupInfo.cs b/MediaBrowser.Controller/Providers/ItemLookupInfo.cs
index 4707b0c7f..49974c2a3 100644
--- a/MediaBrowser.Controller/Providers/ItemLookupInfo.cs
+++ b/MediaBrowser.Controller/Providers/ItemLookupInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using MediaBrowser.Model.Entities;
diff --git a/MediaBrowser.Controller/Providers/LocalImageInfo.cs b/MediaBrowser.Controller/Providers/LocalImageInfo.cs
index 184281025..41801862f 100644
--- a/MediaBrowser.Controller/Providers/LocalImageInfo.cs
+++ b/MediaBrowser.Controller/Providers/LocalImageInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
diff --git a/MediaBrowser.Controller/Providers/MetadataRefreshMode.cs b/MediaBrowser.Controller/Providers/MetadataRefreshMode.cs
index 6d49b5510..920e3da5b 100644
--- a/MediaBrowser.Controller/Providers/MetadataRefreshMode.cs
+++ b/MediaBrowser.Controller/Providers/MetadataRefreshMode.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Controller.Providers
{
public enum MetadataRefreshMode
diff --git a/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs b/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs
index 0a473b80c..b92b83701 100644
--- a/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs
+++ b/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Linq;
using MediaBrowser.Controller.Entities;
diff --git a/MediaBrowser.Controller/Providers/MetadataResult.cs b/MediaBrowser.Controller/Providers/MetadataResult.cs
index 270ea2444..1c695cafa 100644
--- a/MediaBrowser.Controller/Providers/MetadataResult.cs
+++ b/MediaBrowser.Controller/Providers/MetadataResult.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Globalization;
diff --git a/MediaBrowser.Controller/Providers/MovieInfo.cs b/MediaBrowser.Controller/Providers/MovieInfo.cs
index 5b2c3ed03..20e6b697a 100644
--- a/MediaBrowser.Controller/Providers/MovieInfo.cs
+++ b/MediaBrowser.Controller/Providers/MovieInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Controller.Providers
{
public class MovieInfo : ItemLookupInfo
diff --git a/MediaBrowser.Controller/Providers/MusicVideoInfo.cs b/MediaBrowser.Controller/Providers/MusicVideoInfo.cs
index 9835351fc..0b927f6eb 100644
--- a/MediaBrowser.Controller/Providers/MusicVideoInfo.cs
+++ b/MediaBrowser.Controller/Providers/MusicVideoInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
namespace MediaBrowser.Controller.Providers
diff --git a/MediaBrowser.Controller/Providers/PersonLookupInfo.cs b/MediaBrowser.Controller/Providers/PersonLookupInfo.cs
index a6218c039..11cb71f90 100644
--- a/MediaBrowser.Controller/Providers/PersonLookupInfo.cs
+++ b/MediaBrowser.Controller/Providers/PersonLookupInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Controller.Providers
{
public class PersonLookupInfo : ItemLookupInfo
diff --git a/MediaBrowser.Controller/Providers/RemoteSearchQuery.cs b/MediaBrowser.Controller/Providers/RemoteSearchQuery.cs
index a2ac6c9ae..9653bc1c4 100644
--- a/MediaBrowser.Controller/Providers/RemoteSearchQuery.cs
+++ b/MediaBrowser.Controller/Providers/RemoteSearchQuery.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace MediaBrowser.Controller.Providers
diff --git a/MediaBrowser.Controller/Providers/SeasonInfo.cs b/MediaBrowser.Controller/Providers/SeasonInfo.cs
index dd2ef9ad7..2a4c1f03c 100644
--- a/MediaBrowser.Controller/Providers/SeasonInfo.cs
+++ b/MediaBrowser.Controller/Providers/SeasonInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
diff --git a/MediaBrowser.Controller/Providers/SeriesInfo.cs b/MediaBrowser.Controller/Providers/SeriesInfo.cs
index 6c206e031..976fa175a 100644
--- a/MediaBrowser.Controller/Providers/SeriesInfo.cs
+++ b/MediaBrowser.Controller/Providers/SeriesInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Controller.Providers
{
public class SeriesInfo : ItemLookupInfo
diff --git a/MediaBrowser.Controller/Providers/SongInfo.cs b/MediaBrowser.Controller/Providers/SongInfo.cs
index 50615b0bd..58f76dca9 100644
--- a/MediaBrowser.Controller/Providers/SongInfo.cs
+++ b/MediaBrowser.Controller/Providers/SongInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
diff --git a/MediaBrowser.Controller/Providers/TrailerInfo.cs b/MediaBrowser.Controller/Providers/TrailerInfo.cs
index 13f07562d..630850f9d 100644
--- a/MediaBrowser.Controller/Providers/TrailerInfo.cs
+++ b/MediaBrowser.Controller/Providers/TrailerInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Controller.Providers
{
public class TrailerInfo : ItemLookupInfo
diff --git a/MediaBrowser.Controller/Resolvers/IItemResolver.cs b/MediaBrowser.Controller/Resolvers/IItemResolver.cs
index a73937b3e..b99c46843 100644
--- a/MediaBrowser.Controller/Resolvers/IItemResolver.cs
+++ b/MediaBrowser.Controller/Resolvers/IItemResolver.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
diff --git a/MediaBrowser.Controller/Resolvers/ResolverPriority.cs b/MediaBrowser.Controller/Resolvers/ResolverPriority.cs
index 1911e5c1d..ac73a5ea8 100644
--- a/MediaBrowser.Controller/Resolvers/ResolverPriority.cs
+++ b/MediaBrowser.Controller/Resolvers/ResolverPriority.cs
@@ -9,15 +9,22 @@ namespace MediaBrowser.Controller.Resolvers
/// The first.
/// </summary>
First = 1,
+
/// <summary>
/// The second.
/// </summary>
Second = 2,
+
/// <summary>
/// The third.
/// </summary>
Third = 3,
+
+ /// <summary>
+ /// The Fourth.
+ /// </summary>
Fourth = 4,
+
/// <summary>
/// The last.
/// </summary>
diff --git a/MediaBrowser.Controller/Security/AuthenticationInfo.cs b/MediaBrowser.Controller/Security/AuthenticationInfo.cs
index 1d0b959b7..efac9273e 100644
--- a/MediaBrowser.Controller/Security/AuthenticationInfo.cs
+++ b/MediaBrowser.Controller/Security/AuthenticationInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace MediaBrowser.Controller.Security
diff --git a/MediaBrowser.Controller/Security/AuthenticationInfoQuery.cs b/MediaBrowser.Controller/Security/AuthenticationInfoQuery.cs
index 2bd17eb26..c5f3da0b1 100644
--- a/MediaBrowser.Controller/Security/AuthenticationInfoQuery.cs
+++ b/MediaBrowser.Controller/Security/AuthenticationInfoQuery.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace MediaBrowser.Controller.Security
diff --git a/MediaBrowser.Controller/Security/IAuthenticationRepository.cs b/MediaBrowser.Controller/Security/IAuthenticationRepository.cs
index 6a9625613..883b74165 100644
--- a/MediaBrowser.Controller/Security/IAuthenticationRepository.cs
+++ b/MediaBrowser.Controller/Security/IAuthenticationRepository.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Model.Devices;
using MediaBrowser.Model.Querying;
@@ -29,6 +31,7 @@ namespace MediaBrowser.Controller.Security
void Delete(AuthenticationInfo info);
DeviceOptions GetDeviceOptions(string deviceId);
+
void UpdateDeviceOptions(string deviceId, DeviceOptions options);
}
}
diff --git a/MediaBrowser.Controller/Session/AuthenticationRequest.cs b/MediaBrowser.Controller/Session/AuthenticationRequest.cs
index 685ca3bdd..cc321fd22 100644
--- a/MediaBrowser.Controller/Session/AuthenticationRequest.cs
+++ b/MediaBrowser.Controller/Session/AuthenticationRequest.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace MediaBrowser.Controller.Session
diff --git a/MediaBrowser.Controller/Session/ISessionController.cs b/MediaBrowser.Controller/Session/ISessionController.cs
index 04450085b..22d6e2a04 100644
--- a/MediaBrowser.Controller/Session/ISessionController.cs
+++ b/MediaBrowser.Controller/Session/ISessionController.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Threading;
using System.Threading.Tasks;
diff --git a/MediaBrowser.Controller/Session/ISessionManager.cs b/MediaBrowser.Controller/Session/ISessionManager.cs
index e54f21050..9237d21df 100644
--- a/MediaBrowser.Controller/Session/ISessionManager.cs
+++ b/MediaBrowser.Controller/Session/ISessionManager.cs
@@ -1,12 +1,14 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Events;
using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Security;
using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.Session;
using MediaBrowser.Model.SyncPlay;
diff --git a/MediaBrowser.Controller/Session/SessionEventArgs.cs b/MediaBrowser.Controller/Session/SessionEventArgs.cs
index 08baaf647..097e32eae 100644
--- a/MediaBrowser.Controller/Session/SessionEventArgs.cs
+++ b/MediaBrowser.Controller/Session/SessionEventArgs.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace MediaBrowser.Controller.Session
diff --git a/MediaBrowser.Controller/Session/SessionInfo.cs b/MediaBrowser.Controller/Session/SessionInfo.cs
index 4b088998c..054fd33d9 100644
--- a/MediaBrowser.Controller/Session/SessionInfo.cs
+++ b/MediaBrowser.Controller/Session/SessionInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Linq;
using System.Text.Json.Serialization;
diff --git a/MediaBrowser.Controller/Sorting/AlphanumComparator.cs b/MediaBrowser.Controller/Sorting/AlphanumComparator.cs
index de7f72d1c..70cb9eebe 100644
--- a/MediaBrowser.Controller/Sorting/AlphanumComparator.cs
+++ b/MediaBrowser.Controller/Sorting/AlphanumComparator.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
#nullable enable
using System;
@@ -127,7 +129,7 @@ namespace MediaBrowser.Controller.Sorting
}
/// <inheritdoc />
- public int Compare(string x, string y)
+ public int Compare(string? x, string? y)
{
return CompareValues(x, y);
}
diff --git a/MediaBrowser.Controller/Sorting/SortExtensions.cs b/MediaBrowser.Controller/Sorting/SortExtensions.cs
index 2a68f4678..88467814c 100644
--- a/MediaBrowser.Controller/Sorting/SortExtensions.cs
+++ b/MediaBrowser.Controller/Sorting/SortExtensions.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Linq;
@@ -7,6 +9,7 @@ namespace MediaBrowser.Controller.Sorting
public static class SortExtensions
{
private static readonly AlphanumComparator _comparer = new AlphanumComparator();
+
public static IEnumerable<T> OrderByString<T>(this IEnumerable<T> list, Func<T, string> getName)
{
return list.OrderBy(getName, _comparer);
diff --git a/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs b/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs
index 39538aacd..f43d523a6 100644
--- a/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs
+++ b/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Threading;
diff --git a/MediaBrowser.Controller/Subtitles/ISubtitleProvider.cs b/MediaBrowser.Controller/Subtitles/ISubtitleProvider.cs
index 8ffd7c530..a633262de 100644
--- a/MediaBrowser.Controller/Subtitles/ISubtitleProvider.cs
+++ b/MediaBrowser.Controller/Subtitles/ISubtitleProvider.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
diff --git a/MediaBrowser.Controller/Subtitles/SubtitleDownloadEventArgs.cs b/MediaBrowser.Controller/Subtitles/SubtitleDownloadEventArgs.cs
deleted file mode 100644
index 5703aea17..000000000
--- a/MediaBrowser.Controller/Subtitles/SubtitleDownloadEventArgs.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-using System;
-using MediaBrowser.Controller.Entities;
-
-namespace MediaBrowser.Controller.Subtitles
-{
- public class SubtitleDownloadEventArgs
- {
- public BaseItem Item { get; set; }
-
- public string Format { get; set; }
-
- public string Language { get; set; }
-
- public bool IsForced { get; set; }
-
- public string Provider { get; set; }
- }
-
- public class SubtitleDownloadFailureEventArgs
- {
- public BaseItem Item { get; set; }
-
- public string Provider { get; set; }
-
- public Exception Exception { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/Subtitles/SubtitleDownloadFailureEventArgs.cs b/MediaBrowser.Controller/Subtitles/SubtitleDownloadFailureEventArgs.cs
new file mode 100644
index 000000000..ce8141219
--- /dev/null
+++ b/MediaBrowser.Controller/Subtitles/SubtitleDownloadFailureEventArgs.cs
@@ -0,0 +1,26 @@
+using System;
+using MediaBrowser.Controller.Entities;
+
+namespace MediaBrowser.Controller.Subtitles
+{
+ /// <summary>
+ /// An event that occurs when subtitle downloading fails.
+ /// </summary>
+ public class SubtitleDownloadFailureEventArgs : EventArgs
+ {
+ /// <summary>
+ /// Gets or sets the item.
+ /// </summary>
+ public BaseItem Item { get; set; }
+
+ /// <summary>
+ /// Gets or sets the provider.
+ /// </summary>
+ public string Provider { get; set; }
+
+ /// <summary>
+ /// Gets or sets the exception.
+ /// </summary>
+ public Exception Exception { get; set; }
+ }
+}
diff --git a/MediaBrowser.Controller/Subtitles/SubtitleResponse.cs b/MediaBrowser.Controller/Subtitles/SubtitleResponse.cs
index ad6025927..a86b05778 100644
--- a/MediaBrowser.Controller/Subtitles/SubtitleResponse.cs
+++ b/MediaBrowser.Controller/Subtitles/SubtitleResponse.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.IO;
namespace MediaBrowser.Controller.Subtitles
diff --git a/MediaBrowser.Controller/Subtitles/SubtitleSearchRequest.cs b/MediaBrowser.Controller/Subtitles/SubtitleSearchRequest.cs
index a202723b9..7d3c20e8f 100644
--- a/MediaBrowser.Controller/Subtitles/SubtitleSearchRequest.cs
+++ b/MediaBrowser.Controller/Subtitles/SubtitleSearchRequest.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using MediaBrowser.Controller.Providers;
diff --git a/MediaBrowser.Controller/Sync/IHasDynamicAccess.cs b/MediaBrowser.Controller/Sync/IHasDynamicAccess.cs
index d6bac23be..e7395b136 100644
--- a/MediaBrowser.Controller/Sync/IHasDynamicAccess.cs
+++ b/MediaBrowser.Controller/Sync/IHasDynamicAccess.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Sync;
diff --git a/MediaBrowser.Controller/Sync/IServerSyncProvider.cs b/MediaBrowser.Controller/Sync/IServerSyncProvider.cs
index 8b2d5d779..c97fd7044 100644
--- a/MediaBrowser.Controller/Sync/IServerSyncProvider.cs
+++ b/MediaBrowser.Controller/Sync/IServerSyncProvider.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.IO;
using System.Threading;
diff --git a/MediaBrowser.Controller/Sync/ISyncProvider.cs b/MediaBrowser.Controller/Sync/ISyncProvider.cs
index 56f6f3729..950cc73e8 100644
--- a/MediaBrowser.Controller/Sync/ISyncProvider.cs
+++ b/MediaBrowser.Controller/Sync/ISyncProvider.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
using MediaBrowser.Model.Sync;
diff --git a/MediaBrowser.Controller/Sync/SyncedFileInfo.cs b/MediaBrowser.Controller/Sync/SyncedFileInfo.cs
index 687a46d78..a626738fb 100644
--- a/MediaBrowser.Controller/Sync/SyncedFileInfo.cs
+++ b/MediaBrowser.Controller/Sync/SyncedFileInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
using MediaBrowser.Model.MediaInfo;
@@ -5,6 +7,11 @@ namespace MediaBrowser.Controller.Sync
{
public class SyncedFileInfo
{
+ public SyncedFileInfo()
+ {
+ RequiredHttpHeaders = new Dictionary<string, string>();
+ }
+
/// <summary>
/// Gets or sets the path.
/// </summary>
@@ -12,25 +19,23 @@ namespace MediaBrowser.Controller.Sync
public string Path { get; set; }
public string[] PathParts { get; set; }
+
/// <summary>
/// Gets or sets the protocol.
/// </summary>
/// <value>The protocol.</value>
public MediaProtocol Protocol { get; set; }
+
/// <summary>
/// Gets or sets the required HTTP headers.
/// </summary>
/// <value>The required HTTP headers.</value>
public Dictionary<string, string> RequiredHttpHeaders { get; set; }
+
/// <summary>
/// Gets or sets the identifier.
/// </summary>
/// <value>The identifier.</value>
public string Id { get; set; }
-
- public SyncedFileInfo()
- {
- RequiredHttpHeaders = new Dictionary<string, string>();
- }
}
}
diff --git a/MediaBrowser.Controller/SyncPlay/ISyncPlayController.cs b/MediaBrowser.Controller/SyncPlay/ISyncPlayController.cs
index 45c543806..60d17fe36 100644
--- a/MediaBrowser.Controller/SyncPlay/ISyncPlayController.cs
+++ b/MediaBrowser.Controller/SyncPlay/ISyncPlayController.cs
@@ -64,4 +64,4 @@ namespace MediaBrowser.Controller.SyncPlay
/// <value>The group info for the clients.</value>
GroupInfoView GetInfo();
}
-} \ No newline at end of file
+}
diff --git a/MediaBrowser.Controller/TV/ITVSeriesManager.cs b/MediaBrowser.Controller/TV/ITVSeriesManager.cs
index 09a0f6fea..291dea04e 100644
--- a/MediaBrowser.Controller/TV/ITVSeriesManager.cs
+++ b/MediaBrowser.Controller/TV/ITVSeriesManager.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Querying;
diff --git a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs
index a8ebe6bc5..21b5d0c5b 100644
--- a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs
+++ b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs
@@ -240,11 +240,11 @@ namespace MediaBrowser.MediaEncoding.Attachments
if (protocol == MediaProtocol.File)
{
var date = _fileSystem.GetLastWriteTimeUtc(mediaPath);
- filename = (mediaPath + attachmentStreamIndex.ToString(CultureInfo.InvariantCulture) + "_" + date.Ticks.ToString(CultureInfo.InvariantCulture)).GetMD5().ToString("D");
+ filename = (mediaPath + attachmentStreamIndex.ToString(CultureInfo.InvariantCulture) + "_" + date.Ticks.ToString(CultureInfo.InvariantCulture)).GetMD5().ToString("D", CultureInfo.InvariantCulture);
}
else
{
- filename = (mediaPath + attachmentStreamIndex.ToString(CultureInfo.InvariantCulture)).GetMD5().ToString("D");
+ filename = (mediaPath + attachmentStreamIndex.ToString(CultureInfo.InvariantCulture)).GetMD5().ToString("D", CultureInfo.InvariantCulture);
}
var prefix = filename.Substring(0, 1);
diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
index 75123a843..c8bf5557b 100644
--- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
@@ -3,8 +3,8 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
+using System.Globalization;
using System.Linq;
-using System.Text;
using System.Text.RegularExpressions;
using Microsoft.Extensions.Logging;
@@ -87,19 +87,17 @@ namespace MediaBrowser.MediaEncoding.Encoder
"hevc_videotoolbox"
};
- // Try and use the individual library versions to determine a FFmpeg version
- // This lookup table is to be maintained with the following command line:
- // $ ffmpeg -version | perl -ne ' print "$1=$2.$3," if /^(lib\w+)\s+(\d+)\.\s*(\d+)/'
- private static readonly IReadOnlyDictionary<string, Version> _ffmpegVersionMap = new Dictionary<string, Version>
+ // These are the library versions that corresponds to our minimum ffmpeg version 4.x according to the version table below
+ private static readonly IReadOnlyDictionary<string, Version> _ffmpegMinimumLibraryVersions = new Dictionary<string, Version>
{
- { "libavutil=56.51,libavcodec=58.91,libavformat=58.45,libavdevice=58.10,libavfilter=7.85,libswscale=5.7,libswresample=3.7,libpostproc=55.7,", new Version(4, 3) },
- { "libavutil=56.31,libavcodec=58.54,libavformat=58.29,libavdevice=58.8,libavfilter=7.57,libswscale=5.5,libswresample=3.5,libpostproc=55.5,", new Version(4, 2) },
- { "libavutil=56.22,libavcodec=58.35,libavformat=58.20,libavdevice=58.5,libavfilter=7.40,libswscale=5.3,libswresample=3.3,libpostproc=55.3,", new Version(4, 1) },
- { "libavutil=56.14,libavcodec=58.18,libavformat=58.12,libavdevice=58.3,libavfilter=7.16,libswscale=5.1,libswresample=3.1,libpostproc=55.1,", new Version(4, 0) },
- { "libavutil=55.78,libavcodec=57.107,libavformat=57.83,libavdevice=57.10,libavfilter=6.107,libswscale=4.8,libswresample=2.9,libpostproc=54.7,", new Version(3, 4) },
- { "libavutil=55.58,libavcodec=57.89,libavformat=57.71,libavdevice=57.6,libavfilter=6.82,libswscale=4.6,libswresample=2.7,libpostproc=54.5,", new Version(3, 3) },
- { "libavutil=55.34,libavcodec=57.64,libavformat=57.56,libavdevice=57.1,libavfilter=6.65,libswscale=4.2,libswresample=2.3,libpostproc=54.1,", new Version(3, 2) },
- { "libavutil=54.31,libavcodec=56.60,libavformat=56.40,libavdevice=56.4,libavfilter=5.40,libswscale=3.1,libswresample=1.2,libpostproc=53.3,", new Version(2, 8) }
+ { "libavutil", new Version(56, 14) },
+ { "libavcodec", new Version(58, 18) },
+ { "libavformat", new Version(58, 12) },
+ { "libavdevice", new Version(58, 3) },
+ { "libavfilter", new Version(7, 16) },
+ { "libswscale", new Version(5, 1) },
+ { "libswresample", new Version(3, 1) },
+ { "libpostproc", new Version(55, 1) }
};
private readonly ILogger _logger;
@@ -118,6 +116,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
Decoder
}
+ // When changing this, also change the minimum library versions in _ffmpegMinimumLibraryVersions
public static Version MinVersion { get; } = new Version(4, 0);
public static Version MaxVersion { get; } = null;
@@ -156,32 +155,36 @@ namespace MediaBrowser.MediaEncoding.Encoder
// Work out what the version under test is
var version = GetFFmpegVersion(versionOutput);
- _logger.LogInformation("Found ffmpeg version {0}", version != null ? version.ToString() : "unknown");
+ _logger.LogInformation("Found ffmpeg version {Version}", version != null ? version.ToString() : "unknown");
if (version == null)
{
- if (MinVersion != null && MaxVersion != null) // Version is unknown
+ if (MaxVersion != null) // Version is unknown
{
if (MinVersion == MaxVersion)
{
- _logger.LogWarning("FFmpeg validation: We recommend ffmpeg version {0}", MinVersion);
+ _logger.LogWarning("FFmpeg validation: We recommend version {MinVersion}", MinVersion);
}
else
{
- _logger.LogWarning("FFmpeg validation: We recommend a minimum of {0} and maximum of {1}", MinVersion, MaxVersion);
+ _logger.LogWarning("FFmpeg validation: We recommend a minimum of {MinVersion} and maximum of {MaxVersion}", MinVersion, MaxVersion);
}
}
+ else
+ {
+ _logger.LogWarning("FFmpeg validation: We recommend minimum version {MinVersion}", MinVersion);
+ }
return false;
}
- else if (MinVersion != null && version < MinVersion) // Version is below what we recommend
+ else if (version < MinVersion) // Version is below what we recommend
{
- _logger.LogWarning("FFmpeg validation: The minimum recommended ffmpeg version is {0}", MinVersion);
+ _logger.LogWarning("FFmpeg validation: The minimum recommended version is {MinVersion}", MinVersion);
return false;
}
else if (MaxVersion != null && version > MaxVersion) // Version is above what we recommend
{
- _logger.LogWarning("FFmpeg validation: The maximum recommended ffmpeg version is {0}", MaxVersion);
+ _logger.LogWarning("FFmpeg validation: The maximum recommended version is {MaxVersion}", MaxVersion);
return false;
}
@@ -197,13 +200,12 @@ namespace MediaBrowser.MediaEncoding.Encoder
/// <summary>
/// Using the output from "ffmpeg -version" work out the FFmpeg version.
/// For pre-built binaries the first line should contain a string like "ffmpeg version x.y", which is easy
- /// to parse. If this is not available, then we try to match known library versions to FFmpeg versions.
- /// If that fails then we use one of the main libraries to determine if it's new/older than the latest
- /// we have stored.
+ /// to parse. If this is not available, then we try to match known library versions to FFmpeg versions.
+ /// If that fails then we test the libraries to determine if they're newer than our minimum versions.
/// </summary>
/// <param name="output">The output from "ffmpeg -version".</param>
/// <returns>The FFmpeg version.</returns>
- internal static Version GetFFmpegVersion(string output)
+ internal Version GetFFmpegVersion(string output)
{
// For pre-built binaries the FFmpeg version should be mentioned at the very start of the output
var match = Regex.Match(output, @"^ffmpeg version n?((?:[0-9]+\.?)+)");
@@ -212,14 +214,33 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
return new Version(match.Groups[1].Value);
}
- else
- {
- // Create a reduced version string and lookup key from dictionary
- var reducedVersion = GetLibrariesVersionString(output);
- // Try to lookup the string and return Key, otherwise if not found returns null
- return _ffmpegVersionMap.TryGetValue(reducedVersion, out Version version) ? version : null;
+ var versionMap = GetFFmpegLibraryVersions(output);
+
+ var allVersionsValidated = true;
+
+ foreach (var minimumVersion in _ffmpegMinimumLibraryVersions)
+ {
+ if (versionMap.TryGetValue(minimumVersion.Key, out var foundVersion))
+ {
+ if (foundVersion >= minimumVersion.Value)
+ {
+ _logger.LogInformation("Found {Library} version {FoundVersion} ({MinimumVersion})", minimumVersion.Key, foundVersion, minimumVersion.Value);
+ }
+ else
+ {
+ _logger.LogWarning("Found {Library} version {FoundVersion} lower than recommended version {MinimumVersion}", minimumVersion.Key, foundVersion, minimumVersion.Value);
+ allVersionsValidated = false;
+ }
+ }
+ else
+ {
+ _logger.LogError("{Library} version not found", minimumVersion.Key);
+ allVersionsValidated = false;
+ }
}
+
+ return allVersionsValidated ? MinVersion : null;
}
/// <summary>
@@ -228,23 +249,23 @@ namespace MediaBrowser.MediaEncoding.Encoder
/// </summary>
/// <param name="output">The 'ffmpeg -version' output.</param>
/// <returns>The library names and major.minor version numbers.</returns>
- private static string GetLibrariesVersionString(string output)
+ private static IReadOnlyDictionary<string, Version> GetFFmpegLibraryVersions(string output)
{
- var rc = new StringBuilder(144);
- foreach (Match m in Regex.Matches(
+ var map = new Dictionary<string, Version>();
+
+ foreach (Match match in Regex.Matches(
output,
@"((?<name>lib\w+)\s+(?<major>[0-9]+)\.\s*(?<minor>[0-9]+))",
RegexOptions.Multiline))
{
- rc.Append(m.Groups["name"])
- .Append('=')
- .Append(m.Groups["major"])
- .Append('.')
- .Append(m.Groups["minor"])
- .Append(',');
+ var version = new Version(
+ int.Parse(match.Groups["major"].Value, CultureInfo.InvariantCulture),
+ int.Parse(match.Groups["minor"].Value, CultureInfo.InvariantCulture));
+
+ map.Add(match.Groups["name"].Value, version);
}
- return rc.Length == 0 ? null : rc.ToString();
+ return map;
}
private IEnumerable<string> GetHwaccelTypes()
diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs
index 7c2d9f1fd..63310fdf6 100644
--- a/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs
@@ -1,6 +1,8 @@
#pragma warning disable CS1591
+using System;
using System.Collections.Generic;
+using System.Globalization;
using System.Linq;
using MediaBrowser.Model.MediaInfo;
@@ -14,7 +16,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
var url = inputFiles[0];
- return string.Format("\"{0}\"", url);
+ return string.Format(CultureInfo.InvariantCulture, "\"{0}\"", url);
}
return GetConcatInputArgument(inputFiles);
@@ -33,7 +35,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
var files = string.Join("|", inputFiles.Select(NormalizePath));
- return string.Format("concat:\"{0}\"", files);
+ return string.Format(CultureInfo.InvariantCulture, "concat:\"{0}\"", files);
}
// Determine the input path for video files
@@ -47,15 +49,15 @@ namespace MediaBrowser.MediaEncoding.Encoder
/// <returns>System.String.</returns>
private static string GetFileInputArgument(string path)
{
- if (path.IndexOf("://") != -1)
+ if (path.IndexOf("://", StringComparison.Ordinal) != -1)
{
- return string.Format("\"{0}\"", path);
+ return string.Format(CultureInfo.InvariantCulture, "\"{0}\"", path);
}
// Quotes are valid path characters in linux and they need to be escaped here with a leading \
path = NormalizePath(path);
- return string.Format("file:\"{0}\"", path);
+ return string.Format(CultureInfo.InvariantCulture, "file:\"{0}\"", path);
}
/// <summary>
@@ -66,7 +68,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
private static string NormalizePath(string path)
{
// Quotes are valid path characters in linux and they need to be escaped here with a leading \
- return path.Replace("\"", "\\\"");
+ return path.Replace("\"", "\\\"", StringComparison.Ordinal);
}
}
}
diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
index 778c0b18c..5a3a9185d 100644
--- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
@@ -12,6 +12,7 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.Json;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.MediaEncoding.Probing;
@@ -54,6 +55,9 @@ namespace MediaBrowser.MediaEncoding.Encoder
private readonly object _runningProcessesLock = new object();
private readonly List<ProcessWrapper> _runningProcesses = new List<ProcessWrapper>();
+ // MediaEncoder is registered as a Singleton
+ private readonly JsonSerializerOptions _jsonSerializerOptions;
+
private List<string> _encoders = new List<string>();
private List<string> _decoders = new List<string>();
private List<string> _hwaccels = new List<string>();
@@ -75,6 +79,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
_localization = localization;
_encodingHelperFactory = encodingHelperFactory;
_startupOptionFFmpegPath = config.GetValue<string>(Controller.Extensions.ConfigurationExtensions.FfmpegPathKey) ?? string.Empty;
+ _jsonSerializerOptions = JsonDefaults.GetOptions();
}
private EncodingHelper EncodingHelper => _encodingHelperFactory.Value;
@@ -196,9 +201,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
_logger.LogWarning("FFmpeg: {Location}: Failed version check: {Path}", location, path);
}
- // ToDo - Enable the ffmpeg validator. At the moment any version can be used.
- rc = true;
-
_ffmpegPath = path;
EncoderLocation = location;
}
@@ -377,7 +379,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
var args = extractChapters
? "{0} -i {1} -threads 0 -v warning -print_format json -show_streams -show_chapters -show_format"
: "{0} -i {1} -threads 0 -v warning -print_format json -show_streams -show_format";
- args = string.Format(args, probeSizeArgument, inputPath).Trim();
+ args = string.Format(CultureInfo.InvariantCulture, args, probeSizeArgument, inputPath).Trim();
var process = new Process
{
@@ -417,6 +419,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
result = await JsonSerializer.DeserializeAsync<InternalMediaInfoResult>(
process.StandardOutput.BaseStream,
+ _jsonSerializerOptions,
cancellationToken: cancellationToken).ConfigureAwait(false);
}
catch
@@ -552,8 +555,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
// Use ffmpeg to sample 100 (we can drop this if required using thumbnail=50 for 50 frames) frames and pick the best thumbnail. Have a fall back just in case.
var thumbnail = enableThumbnail ? ",thumbnail=24" : string.Empty;
- var args = useIFrame ? string.Format("-i {0}{3} -threads 0 -v quiet -vframes 1 -vf \"{2}{4}\" -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg, thumbnail) :
- string.Format("-i {0}{3} -threads 0 -v quiet -vframes 1 -vf \"{2}\" -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg);
+ var args = useIFrame ? string.Format(CultureInfo.InvariantCulture, "-i {0}{3} -threads 0 -v quiet -vframes 1 -vf \"{2}{4}\" -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg, thumbnail) :
+ string.Format(CultureInfo.InvariantCulture, "-i {0}{3} -threads 0 -v quiet -vframes 1 -vf \"{2}\" -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg);
var probeSizeArgument = EncodingHelper.GetProbeSizeArgument(1);
var analyzeDurationArgument = EncodingHelper.GetAnalyzeDurationArgument(1);
@@ -570,7 +573,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
if (offset.HasValue)
{
- args = string.Format("-ss {0} ", GetTimeParameter(offset.Value)) + args;
+ args = string.Format(CultureInfo.InvariantCulture, "-ss {0} ", GetTimeParameter(offset.Value)) + args;
}
if (videoStream != null)
@@ -641,7 +644,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
if (exitCode == -1 || !file.Exists || file.Length == 0)
{
- var msg = string.Format("ffmpeg image extraction failed for {0}", inputPath);
+ var msg = string.Format(CultureInfo.InvariantCulture, "ffmpeg image extraction failed for {0}", inputPath);
_logger.LogError(msg);
@@ -684,13 +687,13 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
var maxWidthParam = maxWidth.Value.ToString(_usCulture);
- vf += string.Format(",scale=min(iw\\,{0}):trunc(ow/dar/2)*2", maxWidthParam);
+ vf += string.Format(CultureInfo.InvariantCulture, ",scale=min(iw\\,{0}):trunc(ow/dar/2)*2", maxWidthParam);
}
Directory.CreateDirectory(targetDirectory);
var outputPath = Path.Combine(targetDirectory, filenamePrefix + "%05d.jpg");
- var args = string.Format("-i {0} -threads 0 -v quiet -vf \"{2}\" -f image2 \"{1}\"", inputArgument, outputPath, vf);
+ var args = string.Format(CultureInfo.InvariantCulture, "-i {0} -threads 0 -v quiet -vf \"{2}\" -f image2 \"{1}\"", inputArgument, outputPath, vf);
var probeSizeArgument = EncodingHelper.GetProbeSizeArgument(1);
var analyzeDurationArgument = EncodingHelper.GetAnalyzeDurationArgument(1);
@@ -790,7 +793,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
if (exitCode == -1)
{
- var msg = string.Format("ffmpeg image extraction failed for {0}", inputArgument);
+ var msg = string.Format(CultureInfo.InvariantCulture, "ffmpeg image extraction failed for {0}", inputArgument);
_logger.LogError(msg);
@@ -856,7 +859,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
// https://ffmpeg.org/ffmpeg-filters.html#Notes-on-filtergraph-escaping
// We need to double escape
- return path.Replace('\\', '/').Replace(":", "\\:").Replace("'", "'\\\\\\''");
+ return path.Replace('\\', '/').Replace(":", "\\:", StringComparison.Ordinal).Replace("'", "'\\\\\\''", StringComparison.Ordinal);
}
/// <inheritdoc />
diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
index 017f917e2..814edd732 100644
--- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
+++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
@@ -34,7 +34,7 @@
<!-- Code Analyzers-->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
- <!-- <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8" PrivateAssets="All" /> -->
+ <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="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
diff --git a/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs b/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs
index 8996d3b09..b7b23deff 100644
--- a/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs
+++ b/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs
@@ -133,7 +133,6 @@ namespace MediaBrowser.MediaEncoding.Probing
/// </summary>
/// <value>The bits_per_raw_sample.</value>
[JsonPropertyName("bits_per_raw_sample")]
- [JsonConverter(typeof(JsonInt32Converter))]
public int BitsPerRawSample { get; set; }
/// <summary>
diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
index 19e3bd8e6..40a3b43e1 100644
--- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
+++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
@@ -42,7 +42,8 @@ namespace MediaBrowser.MediaEncoding.Probing
var info = new MediaInfo
{
Path = path,
- Protocol = protocol
+ Protocol = protocol,
+ VideoType = videoType
};
FFProbeHelpers.NormalizeFFProbeResult(data);
@@ -1133,7 +1134,7 @@ namespace MediaBrowser.MediaEncoding.Probing
{
// Only use the comma as a delimeter if there are no slashes or pipes.
// We want to be careful not to split names that have commas in them
- var delimeter = !allowCommaDelimiter || _nameDelimiters.Any(i => val.IndexOf(i) != -1) ?
+ var delimeter = !allowCommaDelimiter || _nameDelimiters.Any(i => val.IndexOf(i, StringComparison.Ordinal) != -1) ?
_nameDelimiters :
new[] { ',' };
@@ -1377,8 +1378,8 @@ namespace MediaBrowser.MediaEncoding.Probing
if (subtitle.Contains('/', StringComparison.Ordinal)) // It contains a episode number and season number
{
string[] numbers = subtitle.Split(' ');
- video.IndexNumber = int.Parse(numbers[0].Replace(".", string.Empty, StringComparison.Ordinal).Split('/')[0]);
- int totalEpisodesInSeason = int.Parse(numbers[0].Replace(".", string.Empty, StringComparison.Ordinal).Split('/')[1]);
+ video.IndexNumber = int.Parse(numbers[0].Replace(".", string.Empty, StringComparison.Ordinal).Split('/')[0], CultureInfo.InvariantCulture);
+ int totalEpisodesInSeason = int.Parse(numbers[0].Replace(".", string.Empty, StringComparison.Ordinal).Split('/')[1], CultureInfo.InvariantCulture);
description = string.Join(" ", numbers, 1, numbers.Length - 1).Trim(); // Skip the first, concatenate the rest, clean up spaces and save it
}
diff --git a/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs b/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs
index 308b62886..86b87fddd 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs
@@ -86,9 +86,9 @@ namespace MediaBrowser.MediaEncoding.Subtitles
private void RemoteNativeFormatting(SubtitleTrackEvent p)
{
- int indexOfBegin = p.Text.IndexOf('{');
+ int indexOfBegin = p.Text.IndexOf('{', StringComparison.Ordinal);
string pre = string.Empty;
- while (indexOfBegin >= 0 && p.Text.IndexOf('}') > indexOfBegin)
+ while (indexOfBegin >= 0 && p.Text.IndexOf('}', StringComparison.Ordinal) > indexOfBegin)
{
string s = p.Text.Substring(indexOfBegin);
if (s.StartsWith("{\\an1}", StringComparison.Ordinal) ||
@@ -116,10 +116,10 @@ namespace MediaBrowser.MediaEncoding.Subtitles
pre = s.Substring(0, 5) + "}";
}
- int indexOfEnd = p.Text.IndexOf('}');
+ int indexOfEnd = p.Text.IndexOf('}', StringComparison.Ordinal);
p.Text = p.Text.Remove(indexOfBegin, (indexOfEnd - indexOfBegin) + 1);
- indexOfBegin = p.Text.IndexOf('{');
+ indexOfBegin = p.Text.IndexOf('{', StringComparison.Ordinal);
}
p.Text = pre + p.Text;
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs
index 6b7a81e6e..a5d641747 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Globalization;
using System.IO;
using System.Text;
using System.Threading;
@@ -50,14 +51,14 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
eventsStarted = true;
}
- else if (!string.IsNullOrEmpty(line) && line.Trim().StartsWith(";"))
+ else if (!string.IsNullOrEmpty(line) && line.Trim().StartsWith(";", StringComparison.Ordinal))
{
// skip comment lines
}
else if (eventsStarted && line.Trim().Length > 0)
{
string s = line.Trim().ToLowerInvariant();
- if (s.StartsWith("format:"))
+ if (s.StartsWith("format:", StringComparison.Ordinal))
{
if (line.Length > 10)
{
@@ -103,7 +104,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
string[] splittedLine;
- if (s.StartsWith("dialogue:"))
+ if (s.StartsWith("dialogue:", StringComparison.Ordinal))
{
splittedLine = line.Substring(10).Split(',');
}
@@ -181,10 +182,10 @@ namespace MediaBrowser.MediaEncoding.Subtitles
string[] timeCode = time.Split(':', '.');
return new TimeSpan(
0,
- int.Parse(timeCode[0]),
- int.Parse(timeCode[1]),
- int.Parse(timeCode[2]),
- int.Parse(timeCode[3]) * 10).Ticks;
+ int.Parse(timeCode[0], CultureInfo.InvariantCulture),
+ int.Parse(timeCode[1], CultureInfo.InvariantCulture),
+ int.Parse(timeCode[2], CultureInfo.InvariantCulture),
+ int.Parse(timeCode[3], CultureInfo.InvariantCulture) * 10).Ticks;
}
private static string GetFormattedText(string text)
@@ -193,11 +194,11 @@ namespace MediaBrowser.MediaEncoding.Subtitles
for (int i = 0; i < 10; i++) // just look ten times...
{
- if (text.Contains(@"{\fn"))
+ if (text.Contains(@"{\fn", StringComparison.Ordinal))
{
- int start = text.IndexOf(@"{\fn");
+ int start = text.IndexOf(@"{\fn", StringComparison.Ordinal);
int end = text.IndexOf('}', start);
- if (end > 0 && !text.Substring(start).StartsWith("{\\fn}"))
+ if (end > 0 && !text.Substring(start).StartsWith("{\\fn}", StringComparison.Ordinal))
{
string fontName = text.Substring(start + 4, end - (start + 4));
string extraTags = string.Empty;
@@ -212,7 +213,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
text = text.Insert(start, "<font face=\"" + fontName + "\"" + extraTags + ">");
}
- int indexOfEndTag = text.IndexOf("{\\fn}", start);
+ int indexOfEndTag = text.IndexOf("{\\fn}", start, StringComparison.Ordinal);
if (indexOfEndTag > 0)
{
text = text.Remove(indexOfEndTag, "{\\fn}".Length).Insert(indexOfEndTag, "</font>");
@@ -224,11 +225,11 @@ namespace MediaBrowser.MediaEncoding.Subtitles
}
}
- if (text.Contains(@"{\fs"))
+ if (text.Contains(@"{\fs", StringComparison.Ordinal))
{
- int start = text.IndexOf(@"{\fs");
+ int start = text.IndexOf(@"{\fs", StringComparison.Ordinal);
int end = text.IndexOf('}', start);
- if (end > 0 && !text.Substring(start).StartsWith("{\\fs}"))
+ if (end > 0 && !text.Substring(start).StartsWith("{\\fs}", StringComparison.Ordinal))
{
string fontSize = text.Substring(start + 4, end - (start + 4));
string extraTags = string.Empty;
@@ -245,7 +246,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
text = text.Insert(start, "<font size=\"" + fontSize + "\"" + extraTags + ">");
}
- int indexOfEndTag = text.IndexOf("{\\fs}", start);
+ int indexOfEndTag = text.IndexOf("{\\fs}", start, StringComparison.Ordinal);
if (indexOfEndTag > 0)
{
text = text.Remove(indexOfEndTag, "{\\fs}".Length).Insert(indexOfEndTag, "</font>");
@@ -258,17 +259,17 @@ namespace MediaBrowser.MediaEncoding.Subtitles
}
}
- if (text.Contains(@"{\c"))
+ if (text.Contains(@"{\c", StringComparison.Ordinal))
{
- int start = text.IndexOf(@"{\c");
+ int start = text.IndexOf(@"{\c", StringComparison.Ordinal);
int end = text.IndexOf('}', start);
- if (end > 0 && !text.Substring(start).StartsWith("{\\c}"))
+ if (end > 0 && !text.Substring(start).StartsWith("{\\c}", StringComparison.Ordinal))
{
string color = text.Substring(start + 4, end - (start + 4));
string extraTags = string.Empty;
CheckAndAddSubTags(ref color, ref extraTags, out bool italic);
- color = color.Replace("&", string.Empty).TrimStart('H');
+ color = color.Replace("&", string.Empty, StringComparison.Ordinal).TrimStart('H');
color = color.PadLeft(6, '0');
// switch to rrggbb from bbggrr
@@ -285,7 +286,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
text = text.Insert(start, "<font color=\"" + color + "\"" + extraTags + ">");
}
- int indexOfEndTag = text.IndexOf("{\\c}", start);
+ int indexOfEndTag = text.IndexOf("{\\c}", start, StringComparison.Ordinal);
if (indexOfEndTag > 0)
{
text = text.Remove(indexOfEndTag, "{\\c}".Length).Insert(indexOfEndTag, "</font>");
@@ -297,17 +298,17 @@ namespace MediaBrowser.MediaEncoding.Subtitles
}
}
- if (text.Contains(@"{\1c")) // "1" specifices primary color
+ if (text.Contains(@"{\1c", StringComparison.Ordinal)) // "1" specifices primary color
{
- int start = text.IndexOf(@"{\1c");
+ int start = text.IndexOf(@"{\1c", StringComparison.Ordinal);
int end = text.IndexOf('}', start);
- if (end > 0 && !text.Substring(start).StartsWith("{\\1c}"))
+ if (end > 0 && !text.Substring(start).StartsWith("{\\1c}", StringComparison.Ordinal))
{
string color = text.Substring(start + 5, end - (start + 5));
string extraTags = string.Empty;
CheckAndAddSubTags(ref color, ref extraTags, out bool italic);
- color = color.Replace("&", string.Empty).TrimStart('H');
+ color = color.Replace("&", string.Empty, StringComparison.Ordinal).TrimStart('H');
color = color.PadLeft(6, '0');
// switch to rrggbb from bbggrr
@@ -329,25 +330,25 @@ namespace MediaBrowser.MediaEncoding.Subtitles
}
}
- text = text.Replace(@"{\i1}", "<i>");
- text = text.Replace(@"{\i0}", "</i>");
- text = text.Replace(@"{\i}", "</i>");
+ text = text.Replace(@"{\i1}", "<i>", StringComparison.Ordinal);
+ text = text.Replace(@"{\i0}", "</i>", StringComparison.Ordinal);
+ text = text.Replace(@"{\i}", "</i>", StringComparison.Ordinal);
if (CountTagInText(text, "<i>") > CountTagInText(text, "</i>"))
{
text += "</i>";
}
- text = text.Replace(@"{\u1}", "<u>");
- text = text.Replace(@"{\u0}", "</u>");
- text = text.Replace(@"{\u}", "</u>");
+ text = text.Replace(@"{\u1}", "<u>", StringComparison.Ordinal);
+ text = text.Replace(@"{\u0}", "</u>", StringComparison.Ordinal);
+ text = text.Replace(@"{\u}", "</u>", StringComparison.Ordinal);
if (CountTagInText(text, "<u>") > CountTagInText(text, "</u>"))
{
text += "</u>";
}
- text = text.Replace(@"{\b1}", "<b>");
- text = text.Replace(@"{\b0}", "</b>");
- text = text.Replace(@"{\b}", "</b>");
+ text = text.Replace(@"{\b1}", "<b>", StringComparison.Ordinal);
+ text = text.Replace(@"{\b0}", "</b>", StringComparison.Ordinal);
+ text = text.Replace(@"{\b}", "</b>", StringComparison.Ordinal);
if (CountTagInText(text, "<b>") > CountTagInText(text, "</b>"))
{
text += "</b>";
@@ -362,7 +363,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
private static int CountTagInText(string text, string tag)
{
int count = 0;
- int index = text.IndexOf(tag);
+ int index = text.IndexOf(tag, StringComparison.Ordinal);
while (index >= 0)
{
count++;
@@ -371,7 +372,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
return count;
}
- index = text.IndexOf(tag, index + 1);
+ index = text.IndexOf(tag, index + 1, StringComparison.Ordinal);
}
return count;
@@ -380,7 +381,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
private static void CheckAndAddSubTags(ref string tagName, ref string extraTags, out bool italic)
{
italic = false;
- int indexOfSPlit = tagName.IndexOf(@"\");
+ int indexOfSPlit = tagName.IndexOf('\\', StringComparison.Ordinal);
if (indexOfSPlit > 0)
{
string rest = tagName.Substring(indexOfSPlit).TrimStart('\\');
@@ -388,9 +389,9 @@ namespace MediaBrowser.MediaEncoding.Subtitles
for (int i = 0; i < 10; i++)
{
- if (rest.StartsWith("fs") && rest.Length > 2)
+ if (rest.StartsWith("fs", StringComparison.Ordinal) && rest.Length > 2)
{
- indexOfSPlit = rest.IndexOf(@"\");
+ indexOfSPlit = rest.IndexOf('\\', StringComparison.Ordinal);
string fontSize = rest;
if (indexOfSPlit > 0)
{
@@ -404,9 +405,9 @@ namespace MediaBrowser.MediaEncoding.Subtitles
extraTags += " size=\"" + fontSize.Substring(2) + "\"";
}
- else if (rest.StartsWith("fn") && rest.Length > 2)
+ else if (rest.StartsWith("fn", StringComparison.Ordinal) && rest.Length > 2)
{
- indexOfSPlit = rest.IndexOf(@"\");
+ indexOfSPlit = rest.IndexOf('\\', StringComparison.Ordinal);
string fontName = rest;
if (indexOfSPlit > 0)
{
@@ -420,9 +421,9 @@ namespace MediaBrowser.MediaEncoding.Subtitles
extraTags += " face=\"" + fontName.Substring(2) + "\"";
}
- else if (rest.StartsWith("c") && rest.Length > 2)
+ else if (rest.StartsWith("c", StringComparison.Ordinal) && rest.Length > 2)
{
- indexOfSPlit = rest.IndexOf(@"\");
+ indexOfSPlit = rest.IndexOf('\\', StringComparison.Ordinal);
string fontColor = rest;
if (indexOfSPlit > 0)
{
@@ -435,7 +436,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
}
string color = fontColor.Substring(2);
- color = color.Replace("&", string.Empty).TrimStart('H');
+ color = color.Replace("&", string.Empty, StringComparison.Ordinal).TrimStart('H');
color = color.PadLeft(6, '0');
// switch to rrggbb from bbggrr
color = "#" + color.Remove(color.Length - 6) + color.Substring(color.Length - 2, 2) + color.Substring(color.Length - 4, 2) + color.Substring(color.Length - 6, 2);
@@ -443,9 +444,9 @@ namespace MediaBrowser.MediaEncoding.Subtitles
extraTags += " color=\"" + color + "\"";
}
- else if (rest.StartsWith("i1") && rest.Length > 1)
+ else if (rest.StartsWith("i1", StringComparison.Ordinal) && rest.Length > 1)
{
- indexOfSPlit = rest.IndexOf(@"\");
+ indexOfSPlit = rest.IndexOf('\\', StringComparison.Ordinal);
italic = true;
if (indexOfSPlit > 0)
{
@@ -456,9 +457,9 @@ namespace MediaBrowser.MediaEncoding.Subtitles
rest = string.Empty;
}
}
- else if (rest.Length > 0 && rest.Contains("\\"))
+ else if (rest.Length > 0 && rest.Contains('\\', StringComparison.Ordinal))
{
- indexOfSPlit = rest.IndexOf(@"\");
+ indexOfSPlit = rest.IndexOf('\\', StringComparison.Ordinal);
rest = rest.Substring(indexOfSPlit).TrimStart('\\');
}
}
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
index 374e35b96..6ac5ac2ff 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
@@ -415,7 +415,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
// FFmpeg automatically convert character encoding when it is UTF-16
// If we specify character encoding, it rejects with "do not specify a character encoding" and "Unable to recode subtitle event"
- if ((inputPath.EndsWith(".smi") || inputPath.EndsWith(".sami")) &&
+ if ((inputPath.EndsWith(".smi", StringComparison.Ordinal) || inputPath.EndsWith(".sami", StringComparison.Ordinal)) &&
(encodingParam.Equals("UTF-16BE", StringComparison.OrdinalIgnoreCase) ||
encodingParam.Equals("UTF-16LE", StringComparison.OrdinalIgnoreCase)))
{
@@ -435,7 +435,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
CreateNoWindow = true,
UseShellExecute = false,
FileName = _mediaEncoder.EncoderPath,
- Arguments = string.Format("{0} -i \"{1}\" -c:s srt \"{2}\"", encodingParam, inputPath, outputPath),
+ Arguments = string.Format(CultureInfo.InvariantCulture, "{0} -i \"{1}\" -c:s srt \"{2}\"", encodingParam, inputPath, outputPath),
WindowStyle = ProcessWindowStyle.Hidden,
ErrorDialog = false
},
@@ -506,7 +506,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
string.Format(CultureInfo.InvariantCulture, "ffmpeg subtitle conversion failed for {0}", inputPath));
}
- await SetAssFont(outputPath).ConfigureAwait(false);
+ await SetAssFont(outputPath, cancellationToken).ConfigureAwait(false);
_logger.LogInformation("ffmpeg subtitle conversion succeeded for {Path}", inputPath);
}
@@ -668,7 +668,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
if (string.Equals(outputCodec, "ass", StringComparison.OrdinalIgnoreCase))
{
- await SetAssFont(outputPath).ConfigureAwait(false);
+ await SetAssFont(outputPath, cancellationToken).ConfigureAwait(false);
}
}
@@ -676,8 +676,9 @@ namespace MediaBrowser.MediaEncoding.Subtitles
/// Sets the ass font.
/// </summary>
/// <param name="file">The file.</param>
+ /// <param name="cancellationToken">The token to monitor for cancellation requests. The default value is <c>System.Threading.CancellationToken.None</c>.</param>
/// <returns>Task.</returns>
- private async Task SetAssFont(string file)
+ private async Task SetAssFont(string file, CancellationToken cancellationToken = default)
{
_logger.LogInformation("Setting ass font within {File}", file);
@@ -692,14 +693,14 @@ namespace MediaBrowser.MediaEncoding.Subtitles
text = await reader.ReadToEndAsync().ConfigureAwait(false);
}
- var newText = text.Replace(",Arial,", ",Arial Unicode MS,");
+ var newText = text.Replace(",Arial,", ",Arial Unicode MS,", StringComparison.Ordinal);
- if (!string.Equals(text, newText))
+ if (!string.Equals(text, newText, StringComparison.Ordinal))
{
using (var fileStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read))
using (var writer = new StreamWriter(fileStream, encoding))
{
- writer.Write(newText);
+ await writer.WriteAsync(newText.AsMemory(), cancellationToken).ConfigureAwait(false);
}
}
}
@@ -736,7 +737,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
var charset = CharsetDetector.DetectFromStream(stream).Detected?.EncodingName;
// UTF16 is automatically converted to UTF8 by FFmpeg, do not specify a character encoding
- if ((path.EndsWith(".ass") || path.EndsWith(".ssa") || path.EndsWith(".srt"))
+ if ((path.EndsWith(".ass", StringComparison.Ordinal) || path.EndsWith(".ssa", StringComparison.Ordinal) || path.EndsWith(".srt", StringComparison.Ordinal))
&& (string.Equals(charset, "utf-16le", StringComparison.OrdinalIgnoreCase)
|| string.Equals(charset, "utf-16be", StringComparison.OrdinalIgnoreCase)))
{
diff --git a/MediaBrowser.Model/Activity/IActivityManager.cs b/MediaBrowser.Model/Activity/IActivityManager.cs
index 9dab5e77b..d5344494e 100644
--- a/MediaBrowser.Model/Activity/IActivityManager.cs
+++ b/MediaBrowser.Model/Activity/IActivityManager.cs
@@ -4,7 +4,7 @@ using System;
using System.Linq;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
-using MediaBrowser.Model.Events;
+using Jellyfin.Data.Events;
using MediaBrowser.Model.Querying;
namespace MediaBrowser.Model.Activity
@@ -13,8 +13,6 @@ namespace MediaBrowser.Model.Activity
{
event EventHandler<GenericEventArgs<ActivityLogEntry>> EntryCreated;
- void Create(ActivityLog entry);
-
Task CreateAsync(ActivityLog entry);
QueryResult<ActivityLogEntry> GetPagedResult(int? startIndex, int? limit);
diff --git a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs
index a579f8464..93e60753a 100644
--- a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs
+++ b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs
@@ -157,7 +157,7 @@ namespace MediaBrowser.Model.Dlna
// flagValue = flagValue | DlnaFlags.TimeBasedSeek;
//}
- string dlnaflags = string.Format(";DLNA.ORG_FLAGS={0}",
+ string dlnaflags = string.Format(CultureInfo.InvariantCulture, ";DLNA.ORG_FLAGS={0}",
DlnaMaps.FlagsToString(flagValue));
ResponseProfile mediaProfile = _profile.GetVideoMediaProfile(container,
diff --git a/MediaBrowser.Model/Dlna/DlnaMaps.cs b/MediaBrowser.Model/Dlna/DlnaMaps.cs
index 052b4b78b..95cd0ac27 100644
--- a/MediaBrowser.Model/Dlna/DlnaMaps.cs
+++ b/MediaBrowser.Model/Dlna/DlnaMaps.cs
@@ -1,18 +1,20 @@
#pragma warning disable CS1591
+using System.Globalization;
+
namespace MediaBrowser.Model.Dlna
{
public static class DlnaMaps
{
private static readonly string DefaultStreaming =
- FlagsToString(DlnaFlags.StreamingTransferMode |
+ FlagsToString(DlnaFlags.StreamingTransferMode |
DlnaFlags.BackgroundTransferMode |
DlnaFlags.ConnectionStall |
DlnaFlags.ByteBasedSeek |
DlnaFlags.DlnaV15);
private static readonly string DefaultInteractive =
- FlagsToString(DlnaFlags.InteractiveTransferMode |
+ FlagsToString(DlnaFlags.InteractiveTransferMode |
DlnaFlags.BackgroundTransferMode |
DlnaFlags.ConnectionStall |
DlnaFlags.ByteBasedSeek |
@@ -20,7 +22,7 @@ namespace MediaBrowser.Model.Dlna
public static string FlagsToString(DlnaFlags flags)
{
- return string.Format("{0:X8}{1:D24}", (ulong)flags, 0);
+ return string.Format(CultureInfo.InvariantCulture, "{0:X8}{1:D24}", (ulong)flags, 0);
}
public static string GetOrgOpValue(bool hasKnownRuntime, bool isDirectStream, TranscodeSeekInfo profileTranscodeSeekInfo)
diff --git a/MediaBrowser.Model/Dlna/IDeviceDiscovery.cs b/MediaBrowser.Model/Dlna/IDeviceDiscovery.cs
index 76c9a4b04..05209e53d 100644
--- a/MediaBrowser.Model/Dlna/IDeviceDiscovery.cs
+++ b/MediaBrowser.Model/Dlna/IDeviceDiscovery.cs
@@ -1,7 +1,7 @@
#pragma warning disable CS1591
using System;
-using MediaBrowser.Model.Events;
+using Jellyfin.Data.Events;
namespace MediaBrowser.Model.Dlna
{
diff --git a/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs b/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs
index bdc5f8bb7..3c955989a 100644
--- a/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs
+++ b/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
+using System.Globalization;
using System.Linq;
using MediaBrowser.Model.MediaInfo;
@@ -142,26 +143,26 @@ namespace MediaBrowser.Model.Dlna
{
if (timestampType == TransportStreamTimestamp.None)
{
- return new MediaFormatProfile[] { ValueOf(string.Format("AVC_TS_HP_{0}D_MPEG1_L2_ISO", resolution)) };
+ return new MediaFormatProfile[] { ValueOf(string.Format(CultureInfo.InvariantCulture, "AVC_TS_HP_{0}D_MPEG1_L2_ISO", resolution)) };
}
- return new MediaFormatProfile[] { ValueOf(string.Format("AVC_TS_HP_{0}D_MPEG1_L2_T", resolution)) };
+ return new MediaFormatProfile[] { ValueOf(string.Format(CultureInfo.InvariantCulture, "AVC_TS_HP_{0}D_MPEG1_L2_T", resolution)) };
}
if (string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase))
{
- return new MediaFormatProfile[] { ValueOf(string.Format("AVC_TS_MP_{0}D_AAC_MULT5{1}", resolution, suffix)) };
+ return new MediaFormatProfile[] { ValueOf(string.Format(CultureInfo.InvariantCulture, "AVC_TS_MP_{0}D_AAC_MULT5{1}", resolution, suffix)) };
}
if (string.Equals(audioCodec, "mp3", StringComparison.OrdinalIgnoreCase))
{
- return new MediaFormatProfile[] { ValueOf(string.Format("AVC_TS_MP_{0}D_MPEG1_L3{1}", resolution, suffix)) };
+ return new MediaFormatProfile[] { ValueOf(string.Format(CultureInfo.InvariantCulture, "AVC_TS_MP_{0}D_MPEG1_L3{1}", resolution, suffix)) };
}
if (string.IsNullOrEmpty(audioCodec) ||
string.Equals(audioCodec, "ac3", StringComparison.OrdinalIgnoreCase))
{
- return new MediaFormatProfile[] { ValueOf(string.Format("AVC_TS_MP_{0}D_AC3{1}", resolution, suffix)) };
+ return new MediaFormatProfile[] { ValueOf(string.Format(CultureInfo.InvariantCulture, "AVC_TS_MP_{0}D_AC3{1}", resolution, suffix)) };
}
}
else if (string.Equals(videoCodec, "vc1", StringComparison.OrdinalIgnoreCase))
@@ -180,29 +181,29 @@ namespace MediaBrowser.Model.Dlna
{
suffix = string.Equals(suffix, "_ISO", StringComparison.OrdinalIgnoreCase) ? suffix : "_T";
- return new MediaFormatProfile[] { ValueOf(string.Format("VC1_TS_HD_DTS{0}", suffix)) };
+ return new MediaFormatProfile[] { ValueOf(string.Format(CultureInfo.InvariantCulture, "VC1_TS_HD_DTS{0}", suffix)) };
}
}
else if (string.Equals(videoCodec, "mpeg4", StringComparison.OrdinalIgnoreCase) || string.Equals(videoCodec, "msmpeg4", StringComparison.OrdinalIgnoreCase))
{
if (string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase))
{
- return new MediaFormatProfile[] { ValueOf(string.Format("MPEG4_P2_TS_ASP_AAC{0}", suffix)) };
+ return new MediaFormatProfile[] { ValueOf(string.Format(CultureInfo.InvariantCulture, "MPEG4_P2_TS_ASP_AAC{0}", suffix)) };
}
if (string.Equals(audioCodec, "mp3", StringComparison.OrdinalIgnoreCase))
{
- return new MediaFormatProfile[] { ValueOf(string.Format("MPEG4_P2_TS_ASP_MPEG1_L3{0}", suffix)) };
+ return new MediaFormatProfile[] { ValueOf(string.Format(CultureInfo.InvariantCulture, "MPEG4_P2_TS_ASP_MPEG1_L3{0}", suffix)) };
}
if (string.Equals(audioCodec, "mp2", StringComparison.OrdinalIgnoreCase))
{
- return new MediaFormatProfile[] { ValueOf(string.Format("MPEG4_P2_TS_ASP_MPEG2_L2{0}", suffix)) };
+ return new MediaFormatProfile[] { ValueOf(string.Format(CultureInfo.InvariantCulture, "MPEG4_P2_TS_ASP_MPEG2_L2{0}", suffix)) };
}
if (string.Equals(audioCodec, "ac3", StringComparison.OrdinalIgnoreCase))
{
- return new MediaFormatProfile[] { ValueOf(string.Format("MPEG4_P2_TS_ASP_AC3{0}", suffix)) };
+ return new MediaFormatProfile[] { ValueOf(string.Format(CultureInfo.InvariantCulture, "MPEG4_P2_TS_ASP_AC3{0}", suffix)) };
}
}
diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs
index 204340c46..94d53ab70 100644
--- a/MediaBrowser.Model/Dlna/StreamInfo.cs
+++ b/MediaBrowser.Model/Dlna/StreamInfo.cs
@@ -191,7 +191,7 @@ namespace MediaBrowser.Model.Dlna
var encodedValue = pair.Value.Replace(" ", "%20");
- list.Add(string.Format("{0}={1}", pair.Name, encodedValue));
+ list.Add(string.Format(CultureInfo.InvariantCulture, "{0}={1}", pair.Name, encodedValue));
}
string queryString = string.Join("&", list.ToArray());
@@ -214,18 +214,18 @@ namespace MediaBrowser.Model.Dlna
{
if (string.Equals(SubProtocol, "hls", StringComparison.OrdinalIgnoreCase))
{
- return string.Format("{0}/audio/{1}/master.m3u8?{2}", baseUrl, ItemId, queryString);
+ return string.Format(CultureInfo.InvariantCulture, "{0}/audio/{1}/master.m3u8?{2}", baseUrl, ItemId, queryString);
}
- return string.Format("{0}/audio/{1}/stream{2}?{3}", baseUrl, ItemId, extension, queryString);
+ return string.Format(CultureInfo.InvariantCulture, "{0}/audio/{1}/stream{2}?{3}", baseUrl, ItemId, extension, queryString);
}
if (string.Equals(SubProtocol, "hls", StringComparison.OrdinalIgnoreCase))
{
- return string.Format("{0}/videos/{1}/master.m3u8?{2}", baseUrl, ItemId, queryString);
+ return string.Format(CultureInfo.InvariantCulture, "{0}/videos/{1}/master.m3u8?{2}", baseUrl, ItemId, queryString);
}
- return string.Format("{0}/videos/{1}/stream{2}?{3}", baseUrl, ItemId, extension, queryString);
+ return string.Format(CultureInfo.InvariantCulture, "{0}/videos/{1}/stream{2}?{3}", baseUrl, ItemId, extension, queryString);
}
private static List<NameValuePair> BuildParams(StreamInfo item, string accessToken)
@@ -457,7 +457,7 @@ namespace MediaBrowser.Model.Dlna
{
if (MediaSource.Protocol == MediaProtocol.File || !string.Equals(stream.Codec, subtitleProfile.Format, StringComparison.OrdinalIgnoreCase) || !stream.IsExternal)
{
- info.Url = string.Format("{0}/Videos/{1}/{2}/Subtitles/{3}/{4}/Stream.{5}",
+ info.Url = string.Format(CultureInfo.InvariantCulture, "{0}/Videos/{1}/{2}/Subtitles/{3}/{4}/Stream.{5}",
baseUrl,
ItemId,
MediaSourceId,
diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj
index 902e29b20..0491c9072 100644
--- a/MediaBrowser.Model/MediaBrowser.Model.csproj
+++ b/MediaBrowser.Model/MediaBrowser.Model.csproj
@@ -8,8 +8,9 @@
<PropertyGroup>
<Authors>Jellyfin Contributors</Authors>
<PackageId>Jellyfin.Model</PackageId>
- <PackageLicenseUrl>https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt</PackageLicenseUrl>
+ <VersionPrefix>10.7.0</VersionPrefix>
<RepositoryUrl>https://github.com/jellyfin/jellyfin</RepositoryUrl>
+ <PackageLicenseExpression>GPL-3.0-only</PackageLicenseExpression>
</PropertyGroup>
<PropertyGroup>
@@ -25,7 +26,7 @@
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="3.1.6" />
<PackageReference Include="System.Globalization" Version="4.3.0" />
- <PackageReference Include="System.Text.Json" Version="4.7.2" />
+ <PackageReference Include="System.Text.Json" Version="5.0.0-preview.8.20407.11" />
</ItemGroup>
<ItemGroup>
diff --git a/MediaBrowser.Model/Session/PlayRequest.cs b/MediaBrowser.Model/Session/PlayRequest.cs
index d57bed171..eeb25c2e8 100644
--- a/MediaBrowser.Model/Session/PlayRequest.cs
+++ b/MediaBrowser.Model/Session/PlayRequest.cs
@@ -15,21 +15,18 @@ namespace MediaBrowser.Model.Session
/// Gets or sets the item ids.
/// </summary>
/// <value>The item ids.</value>
- [ApiMember(Name = "ItemIds", Description = "The ids of the items to play, comma delimited", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST", AllowMultiple = true)]
public Guid[] ItemIds { get; set; }
/// <summary>
/// Gets or sets the start position ticks that the first item should be played at.
/// </summary>
/// <value>The start position ticks.</value>
- [ApiMember(Name = "StartPositionTicks", Description = "The starting position of the first item.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
public long? StartPositionTicks { get; set; }
/// <summary>
/// Gets or sets the play command.
/// </summary>
/// <value>The play command.</value>
- [ApiMember(Name = "PlayCommand", Description = "The type of play command to issue (PlayNow, PlayNext, PlayLast). Clients who have not yet implemented play next and play last may play now.", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
public PlayCommand PlayCommand { get; set; }
/// <summary>
diff --git a/MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs b/MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs
index b08acba2c..2f05e08c5 100644
--- a/MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs
+++ b/MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs
@@ -1,6 +1,6 @@
#nullable disable
using System;
-using MediaBrowser.Model.Events;
+using Jellyfin.Data.Events;
namespace MediaBrowser.Model.Tasks
{
diff --git a/MediaBrowser.Model/Tasks/ITaskManager.cs b/MediaBrowser.Model/Tasks/ITaskManager.cs
index 363773ff7..02b29074e 100644
--- a/MediaBrowser.Model/Tasks/ITaskManager.cs
+++ b/MediaBrowser.Model/Tasks/ITaskManager.cs
@@ -3,7 +3,7 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
-using MediaBrowser.Model.Events;
+using Jellyfin.Data.Events;
namespace MediaBrowser.Model.Tasks
{
diff --git a/MediaBrowser.Model/Users/UserPolicy.cs b/MediaBrowser.Model/Users/UserPolicy.cs
index caf2e0f54..a1f01f7e8 100644
--- a/MediaBrowser.Model/Users/UserPolicy.cs
+++ b/MediaBrowser.Model/Users/UserPolicy.cs
@@ -80,11 +80,11 @@ namespace MediaBrowser.Model.Users
public bool EnableAllDevices { get; set; }
- public string[] EnabledChannels { get; set; }
+ public Guid[] EnabledChannels { get; set; }
public bool EnableAllChannels { get; set; }
- public string[] EnabledFolders { get; set; }
+ public Guid[] EnabledFolders { get; set; }
public bool EnableAllFolders { get; set; }
@@ -94,9 +94,9 @@ namespace MediaBrowser.Model.Users
public bool EnablePublicSharing { get; set; }
- public string[] BlockedMediaFolders { get; set; }
+ public Guid[] BlockedMediaFolders { get; set; }
- public string[] BlockedChannels { get; set; }
+ public Guid[] BlockedChannels { get; set; }
public int RemoteClientBitrateLimit { get; set; }
@@ -145,10 +145,10 @@ namespace MediaBrowser.Model.Users
LoginAttemptsBeforeLockout = -1;
EnableAllChannels = true;
- EnabledChannels = Array.Empty<string>();
+ EnabledChannels = Array.Empty<Guid>();
EnableAllFolders = true;
- EnabledFolders = Array.Empty<string>();
+ EnabledFolders = Array.Empty<Guid>();
EnabledDevices = Array.Empty<string>();
EnableAllDevices = true;
diff --git a/MediaBrowser.Providers/Manager/ImageSaver.cs b/MediaBrowser.Providers/Manager/ImageSaver.cs
index 26b50784b..413d297cb 100644
--- a/MediaBrowser.Providers/Manager/ImageSaver.cs
+++ b/MediaBrowser.Providers/Manager/ImageSaver.cs
@@ -187,7 +187,7 @@ namespace MediaBrowser.Providers.Manager
}
}
- public async Task SaveImage(User user, Stream source, string path)
+ public async Task SaveImage(Stream source, string path)
{
await SaveImageToLocation(source, path, path, CancellationToken.None).ConfigureAwait(false);
}
@@ -355,7 +355,7 @@ namespace MediaBrowser.Providers.Manager
if (string.IsNullOrWhiteSpace(extension))
{
- throw new ArgumentException(string.Format("Unable to determine image file extension from mime type {0}", mimeType));
+ throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Unable to determine image file extension from mime type {0}", mimeType));
}
if (type == ImageType.Thumb && saveLocally)
diff --git a/MediaBrowser.Providers/Manager/ItemImageProvider.cs b/MediaBrowser.Providers/Manager/ItemImageProvider.cs
index a5eb095c4..9227b6d93 100644
--- a/MediaBrowser.Providers/Manager/ItemImageProvider.cs
+++ b/MediaBrowser.Providers/Manager/ItemImageProvider.cs
@@ -54,7 +54,12 @@ namespace MediaBrowser.Providers.Manager
return hasChanges;
}
- public async Task<RefreshResult> RefreshImages(BaseItem item, LibraryOptions libraryOptions, List<IImageProvider> providers, ImageRefreshOptions refreshOptions, MetadataOptions savedOptions, CancellationToken cancellationToken)
+ public async Task<RefreshResult> RefreshImages(
+ BaseItem item,
+ LibraryOptions libraryOptions,
+ List<IImageProvider> providers,
+ ImageRefreshOptions refreshOptions,
+ CancellationToken cancellationToken)
{
if (refreshOptions.IsReplacingImage(ImageType.Backdrop))
{
@@ -78,19 +83,15 @@ namespace MediaBrowser.Providers.Manager
foreach (var provider in providers)
{
- var remoteProvider = provider as IRemoteImageProvider;
-
- if (remoteProvider != null)
+ if (provider is IRemoteImageProvider remoteProvider)
{
await RefreshFromProvider(item, libraryOptions, remoteProvider, refreshOptions, typeOptions, backdropLimit, screenshotLimit, downloadedImages, result, cancellationToken).ConfigureAwait(false);
continue;
}
- var dynamicImageProvider = provider as IDynamicImageProvider;
-
- if (dynamicImageProvider != null)
+ if (provider is IDynamicImageProvider dynamicImageProvider)
{
- await RefreshFromProvider(item, dynamicImageProvider, refreshOptions, typeOptions, libraryOptions, downloadedImages, result, cancellationToken).ConfigureAwait(false);
+ await RefreshFromProvider(item, dynamicImageProvider, refreshOptions, typeOptions, downloadedImages, result, cancellationToken).ConfigureAwait(false);
}
}
@@ -100,11 +101,11 @@ namespace MediaBrowser.Providers.Manager
/// <summary>
/// Refreshes from provider.
/// </summary>
- private async Task RefreshFromProvider(BaseItem item,
+ private async Task RefreshFromProvider(
+ BaseItem item,
IDynamicImageProvider provider,
ImageRefreshOptions refreshOptions,
TypeOptions savedOptions,
- LibraryOptions libraryOptions,
ICollection<ImageType> downloadedImages,
RefreshResult result,
CancellationToken cancellationToken)
@@ -115,7 +116,7 @@ namespace MediaBrowser.Providers.Manager
foreach (var imageType in images)
{
- if (!IsEnabled(savedOptions, imageType, item))
+ if (!IsEnabled(savedOptions, imageType))
{
continue;
}
@@ -133,12 +134,13 @@ namespace MediaBrowser.Providers.Manager
if (response.Protocol == MediaProtocol.Http)
{
_logger.LogDebug("Setting image url into item {0}", item.Id);
- item.SetImage(new ItemImageInfo
- {
- Path = response.Path,
- Type = imageType
-
- }, 0);
+ item.SetImage(
+ new ItemImageInfo
+ {
+ Path = response.Path,
+ Type = imageType
+ },
+ 0);
}
else
{
@@ -157,7 +159,7 @@ namespace MediaBrowser.Providers.Manager
}
downloadedImages.Add(imageType);
- result.UpdateType = result.UpdateType | ItemUpdateType.ImageUpdate;
+ result.UpdateType |= ItemUpdateType.ImageUpdate;
}
}
}
@@ -279,7 +281,7 @@ namespace MediaBrowser.Providers.Manager
foreach (var imageType in _singularImages)
{
- if (!IsEnabled(savedOptions, imageType, item))
+ if (!IsEnabled(savedOptions, imageType))
{
continue;
}
@@ -299,8 +301,7 @@ namespace MediaBrowser.Providers.Manager
minWidth = savedOptions.GetMinWidth(ImageType.Backdrop);
await DownloadBackdrops(item, libraryOptions, ImageType.Backdrop, backdropLimit, provider, result, list, minWidth, cancellationToken).ConfigureAwait(false);
- var hasScreenshots = item as IHasScreenshots;
- if (hasScreenshots != null)
+ if (item is IHasScreenshots hasScreenshots)
{
minWidth = savedOptions.GetMinWidth(ImageType.Screenshot);
await DownloadBackdrops(item, libraryOptions, ImageType.Screenshot, screenshotLimit, provider, result, list, minWidth, cancellationToken).ConfigureAwait(false);
@@ -317,7 +318,7 @@ namespace MediaBrowser.Providers.Manager
}
}
- private bool IsEnabled(TypeOptions options, ImageType type, BaseItem item)
+ private bool IsEnabled(TypeOptions options, ImageType type)
{
return options.IsEnabled(type);
}
@@ -452,10 +453,10 @@ namespace MediaBrowser.Providers.Manager
.Where(i => i.Type == type && !(i.Width.HasValue && i.Width.Value < minWidth))
.ToList();
- if (EnableImageStub(item, type, libraryOptions) && eligibleImages.Count > 0)
+ if (EnableImageStub(item, libraryOptions) && eligibleImages.Count > 0)
{
SaveImageStub(item, type, eligibleImages.Select(i => i.Url));
- result.UpdateType = result.UpdateType | ItemUpdateType.ImageUpdate;
+ result.UpdateType |= ItemUpdateType.ImageUpdate;
return true;
}
@@ -476,7 +477,7 @@ namespace MediaBrowser.Providers.Manager
null,
cancellationToken).ConfigureAwait(false);
- result.UpdateType = result.UpdateType | ItemUpdateType.ImageUpdate;
+ result.UpdateType |= ItemUpdateType.ImageUpdate;
return true;
}
catch (HttpException ex)
@@ -495,7 +496,7 @@ namespace MediaBrowser.Providers.Manager
return false;
}
- private bool EnableImageStub(BaseItem item, ImageType type, LibraryOptions libraryOptions)
+ private bool EnableImageStub(BaseItem item, LibraryOptions libraryOptions)
{
if (item is LiveTvProgram)
{
@@ -563,10 +564,10 @@ namespace MediaBrowser.Providers.Manager
var url = image.Url;
- if (EnableImageStub(item, imageType, libraryOptions))
+ if (EnableImageStub(item, libraryOptions))
{
SaveImageStub(item, imageType, new[] { url });
- result.UpdateType = result.UpdateType | ItemUpdateType.ImageUpdate;
+ result.UpdateType |= ItemUpdateType.ImageUpdate;
continue;
}
diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs
index 3b0c7b56c..d0de58427 100644
--- a/MediaBrowser.Providers/Manager/MetadataService.cs
+++ b/MediaBrowser.Providers/Manager/MetadataService.cs
@@ -52,7 +52,6 @@ namespace MediaBrowser.Providers.Manager
public async Task<ItemUpdateType> RefreshMetadata(BaseItem item, MetadataRefreshOptions refreshOptions, CancellationToken cancellationToken)
{
var itemOfType = (TItemType)item;
- var config = ProviderManager.GetMetadataOptions(item);
var updateType = ItemUpdateType.None;
var requiresRefresh = false;
@@ -86,7 +85,7 @@ namespace MediaBrowser.Providers.Manager
// Always validate images and check for new locally stored ones.
if (itemImageProvider.ValidateImages(item, allImageProviders.OfType<ILocalImageProvider>(), refreshOptions.DirectoryService))
{
- updateType = updateType | ItemUpdateType.ImageUpdate;
+ updateType |= ItemUpdateType.ImageUpdate;
}
}
catch (Exception ex)
@@ -102,7 +101,7 @@ namespace MediaBrowser.Providers.Manager
bool hasRefreshedMetadata = true;
bool hasRefreshedImages = true;
- var isFirstRefresh = item.DateLastRefreshed == default(DateTime);
+ var isFirstRefresh = item.DateLastRefreshed == default;
// Next run metadata providers
if (refreshOptions.MetadataRefreshMode != MetadataRefreshMode.None)
@@ -114,7 +113,7 @@ namespace MediaBrowser.Providers.Manager
{
if (item.BeforeMetadataRefresh(refreshOptions.ReplaceAllMetadata))
{
- updateType = updateType | ItemUpdateType.MetadataImport;
+ updateType |= ItemUpdateType.MetadataImport;
}
}
@@ -132,7 +131,7 @@ namespace MediaBrowser.Providers.Manager
var result = await RefreshWithProviders(metadataResult, id, refreshOptions, providers, itemImageProvider, cancellationToken).ConfigureAwait(false);
- updateType = updateType | result.UpdateType;
+ updateType |= result.UpdateType;
if (result.Failures > 0)
{
hasRefreshedMetadata = false;
@@ -147,9 +146,9 @@ namespace MediaBrowser.Providers.Manager
if (providers.Count > 0)
{
- var result = await itemImageProvider.RefreshImages(itemOfType, libraryOptions, providers, refreshOptions, config, cancellationToken).ConfigureAwait(false);
+ var result = await itemImageProvider.RefreshImages(itemOfType, libraryOptions, providers, refreshOptions, cancellationToken).ConfigureAwait(false);
- updateType = updateType | result.UpdateType;
+ updateType |= result.UpdateType;
if (result.Failures > 0)
{
hasRefreshedImages = false;
@@ -158,7 +157,7 @@ namespace MediaBrowser.Providers.Manager
}
var beforeSaveResult = BeforeSave(itemOfType, isFirstRefresh || refreshOptions.ReplaceAllMetadata || refreshOptions.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || requiresRefresh || refreshOptions.ForceSave, updateType);
- updateType = updateType | beforeSaveResult;
+ updateType |= beforeSaveResult;
// Save if changes were made, or it's never been saved before
if (refreshOptions.ForceSave || updateType > ItemUpdateType.None || isFirstRefresh || refreshOptions.ReplaceAllMetadata || requiresRefresh)
@@ -175,7 +174,7 @@ namespace MediaBrowser.Providers.Manager
// If any of these properties are set then make sure the updateType is not None, just to force everything to save
if (refreshOptions.ForceSave || refreshOptions.ReplaceAllMetadata)
{
- updateType = updateType | ItemUpdateType.MetadataDownload;
+ updateType |= ItemUpdateType.MetadataDownload;
}
if (hasRefreshedMetadata && hasRefreshedImages)
@@ -184,11 +183,11 @@ namespace MediaBrowser.Providers.Manager
}
else
{
- item.DateLastRefreshed = default(DateTime);
+ item.DateLastRefreshed = default;
}
// Save to database
- SaveItem(metadataResult, libraryOptions, updateType, cancellationToken);
+ await SaveItemAsync(metadataResult, libraryOptions, updateType, cancellationToken).ConfigureAwait(false);
}
await AfterMetadataRefresh(itemOfType, refreshOptions, cancellationToken).ConfigureAwait(false);
@@ -203,26 +202,26 @@ namespace MediaBrowser.Providers.Manager
lookupInfo.Year = result.ProductionYear;
}
- protected void SaveItem(MetadataResult<TItemType> result, LibraryOptions libraryOptions, ItemUpdateType reason, CancellationToken cancellationToken)
+ protected async Task SaveItemAsync(MetadataResult<TItemType> result, LibraryOptions libraryOptions, ItemUpdateType reason, CancellationToken cancellationToken)
{
if (result.Item.SupportsPeople && result.People != null)
{
var baseItem = result.Item;
LibraryManager.UpdatePeople(baseItem, result.People);
- SavePeopleMetadata(result.People, libraryOptions, cancellationToken);
+ await SavePeopleMetadataAsync(result.People, libraryOptions, cancellationToken).ConfigureAwait(false);
}
- result.Item.UpdateToRepository(reason, cancellationToken);
+ await result.Item.UpdateToRepositoryAsync(reason, cancellationToken).ConfigureAwait(false);
}
- private void SavePeopleMetadata(List<PersonInfo> people, LibraryOptions libraryOptions, CancellationToken cancellationToken)
+ private async Task SavePeopleMetadataAsync(List<PersonInfo> people, LibraryOptions libraryOptions, CancellationToken cancellationToken)
{
foreach (var person in people)
{
cancellationToken.ThrowIfCancellationRequested();
- if (person.ProviderIds.Any() || !string.IsNullOrWhiteSpace(person.ImageUrl))
+ if (person.ProviderIds.Count > 0 || !string.IsNullOrWhiteSpace(person.ImageUrl))
{
var updateType = ItemUpdateType.MetadataDownload;
@@ -239,40 +238,42 @@ namespace MediaBrowser.Providers.Manager
if (!string.IsNullOrWhiteSpace(person.ImageUrl) && !personEntity.HasImage(ImageType.Primary))
{
- AddPersonImage(personEntity, libraryOptions, person.ImageUrl, cancellationToken);
+ await AddPersonImageAsync(personEntity, libraryOptions, person.ImageUrl, cancellationToken).ConfigureAwait(false);
saveEntity = true;
- updateType = updateType | ItemUpdateType.ImageUpdate;
+ updateType |= ItemUpdateType.ImageUpdate;
}
if (saveEntity)
{
- personEntity.UpdateToRepository(updateType, cancellationToken);
+ await personEntity.UpdateToRepositoryAsync(updateType, cancellationToken).ConfigureAwait(false);
}
}
}
}
- private void AddPersonImage(Person personEntity, LibraryOptions libraryOptions, string imageUrl, CancellationToken cancellationToken)
+ private async Task AddPersonImageAsync(Person personEntity, LibraryOptions libraryOptions, string imageUrl, CancellationToken cancellationToken)
{
- // if (libraryOptions.DownloadImagesInAdvance)
- //{
- // try
- // {
- // await ProviderManager.SaveImage(personEntity, imageUrl, ImageType.Primary, null, cancellationToken).ConfigureAwait(false);
- // return;
- // }
- // catch (Exception ex)
- // {
- // Logger.LogError(ex, "Error in AddPersonImage");
- // }
- //}
-
- personEntity.SetImage(new ItemImageInfo
- {
- Path = imageUrl,
- Type = ImageType.Primary
- }, 0);
+ if (libraryOptions.DownloadImagesInAdvance)
+ {
+ try
+ {
+ await ProviderManager.SaveImage(personEntity, imageUrl, ImageType.Primary, null, cancellationToken).ConfigureAwait(false);
+ return;
+ }
+ catch (Exception ex)
+ {
+ Logger.LogError(ex, "Error in AddPersonImage");
+ }
+ }
+
+ personEntity.SetImage(
+ new ItemImageInfo
+ {
+ Path = imageUrl,
+ Type = ImageType.Primary
+ },
+ 0);
}
protected virtual Task AfterMetadataRefresh(TItemType item, MetadataRefreshOptions refreshOptions, CancellationToken cancellationToken)
diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs
index e67d1b8c3..155e8cb8a 100644
--- a/MediaBrowser.Providers/Manager/ProviderManager.cs
+++ b/MediaBrowser.Providers/Manager/ProviderManager.cs
@@ -10,6 +10,7 @@ using System.Net.Mime;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
+using Jellyfin.Data.Events;
using MediaBrowser.Common.Net;
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller;
@@ -23,7 +24,6 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Subtitles;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Providers;
@@ -210,10 +210,10 @@ namespace MediaBrowser.Providers.Manager
}
/// <inheritdoc/>
- public Task SaveImage(User user, Stream source, string mimeType, string path)
+ public Task SaveImage(Stream source, string mimeType, string path)
{
return new ImageSaver(_configurationManager, _libraryMonitor, _fileSystem, _logger)
- .SaveImage(user, source, path);
+ .SaveImage(source, path);
}
/// <inheritdoc/>
@@ -563,7 +563,7 @@ namespace MediaBrowser.Providers.Manager
var pluginList = summary.Plugins.ToList();
AddMetadataPlugins(pluginList, dummy, libraryOptions, options);
- AddImagePlugins(pluginList, dummy, imageProviders);
+ AddImagePlugins(pluginList, imageProviders);
var subtitleProviders = _subtitleManager.GetSupportedProviders(dummy);
@@ -594,14 +594,14 @@ namespace MediaBrowser.Providers.Manager
var providers = GetMetadataProvidersInternal<T>(item, libraryOptions, options, true, true).ToList();
// Locals
- list.AddRange(providers.Where(i => (i is ILocalMetadataProvider)).Select(i => new MetadataPlugin
+ list.AddRange(providers.Where(i => i is ILocalMetadataProvider).Select(i => new MetadataPlugin
{
Name = i.Name,
Type = MetadataPluginType.LocalMetadataProvider
}));
// Fetchers
- list.AddRange(providers.Where(i => (i is IRemoteMetadataProvider)).Select(i => new MetadataPlugin
+ list.AddRange(providers.Where(i => i is IRemoteMetadataProvider).Select(i => new MetadataPlugin
{
Name = i.Name,
Type = MetadataPluginType.MetadataFetcher
@@ -615,11 +615,10 @@ namespace MediaBrowser.Providers.Manager
}));
}
- private void AddImagePlugins<T>(List<MetadataPlugin> list, T item, List<IImageProvider> imageProviders)
- where T : BaseItem
+ private void AddImagePlugins(List<MetadataPlugin> list, List<IImageProvider> imageProviders)
{
// Locals
- list.AddRange(imageProviders.Where(i => (i is ILocalImageProvider)).Select(i => new MetadataPlugin
+ list.AddRange(imageProviders.Where(i => i is ILocalImageProvider).Select(i => new MetadataPlugin
{
Name = i.Name,
Type = MetadataPluginType.LocalImageProvider
@@ -1166,12 +1165,32 @@ namespace MediaBrowser.Providers.Manager
/// <inheritdoc/>
public void Dispose()
{
- _disposed = true;
+ 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;
+ }
if (!_disposeCancellationTokenSource.IsCancellationRequested)
{
_disposeCancellationTokenSource.Cancel();
}
+
+ if (disposing)
+ {
+ _disposeCancellationTokenSource.Dispose();
+ }
+
+ _disposed = true;
}
}
}
diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs
index 69c6fd722..77f03580a 100644
--- a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs
+++ b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs
@@ -2,11 +2,9 @@
using System;
using System.Collections.Generic;
-using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
@@ -17,7 +15,6 @@ using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.MediaInfo;
-using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Providers.MediaInfo
{
@@ -25,19 +22,17 @@ namespace MediaBrowser.Providers.MediaInfo
{
private readonly IMediaEncoder _mediaEncoder;
private readonly IItemRepository _itemRepo;
- private readonly IApplicationPaths _appPaths;
- private readonly IJsonSerializer _json;
private readonly ILibraryManager _libraryManager;
private readonly IMediaSourceManager _mediaSourceManager;
- private readonly CultureInfo _usCulture = new CultureInfo("en-US");
-
- public FFProbeAudioInfo(IMediaSourceManager mediaSourceManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, IApplicationPaths appPaths, IJsonSerializer json, ILibraryManager libraryManager)
+ public FFProbeAudioInfo(
+ IMediaSourceManager mediaSourceManager,
+ IMediaEncoder mediaEncoder,
+ IItemRepository itemRepo,
+ ILibraryManager libraryManager)
{
_mediaEncoder = mediaEncoder;
_itemRepo = itemRepo;
- _appPaths = appPaths;
- _json = json;
_libraryManager = libraryManager;
_mediaSourceManager = mediaSourceManager;
}
diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
index 4fabe709b..9926275ae 100644
--- a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
+++ b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
@@ -40,19 +40,15 @@ namespace MediaBrowser.Providers.MediaInfo
IHasItemChangeMonitor
{
private readonly ILogger<FFProbeProvider> _logger;
- private readonly IIsoManager _isoManager;
private readonly IMediaEncoder _mediaEncoder;
private readonly IItemRepository _itemRepo;
private readonly IBlurayExaminer _blurayExaminer;
private readonly ILocalizationManager _localization;
- private readonly IApplicationPaths _appPaths;
- private readonly IJsonSerializer _json;
private readonly IEncodingManager _encodingManager;
private readonly IServerConfigurationManager _config;
private readonly ISubtitleManager _subtitleManager;
private readonly IChapterManager _chapterManager;
private readonly ILibraryManager _libraryManager;
- private readonly IChannelManager _channelManager;
private readonly IMediaSourceManager _mediaSourceManager;
public string Name => "ffprobe";
@@ -126,14 +122,10 @@ namespace MediaBrowser.Providers.MediaInfo
public FFProbeProvider(
ILogger<FFProbeProvider> logger,
IMediaSourceManager mediaSourceManager,
- IChannelManager channelManager,
- IIsoManager isoManager,
IMediaEncoder mediaEncoder,
IItemRepository itemRepo,
IBlurayExaminer blurayExaminer,
ILocalizationManager localization,
- IApplicationPaths appPaths,
- IJsonSerializer json,
IEncodingManager encodingManager,
IServerConfigurationManager config,
ISubtitleManager subtitleManager,
@@ -141,19 +133,15 @@ namespace MediaBrowser.Providers.MediaInfo
ILibraryManager libraryManager)
{
_logger = logger;
- _isoManager = isoManager;
_mediaEncoder = mediaEncoder;
_itemRepo = itemRepo;
_blurayExaminer = blurayExaminer;
_localization = localization;
- _appPaths = appPaths;
- _json = json;
_encodingManager = encodingManager;
_config = config;
_subtitleManager = subtitleManager;
_chapterManager = chapterManager;
_libraryManager = libraryManager;
- _channelManager = channelManager;
_mediaSourceManager = mediaSourceManager;
_subtitleResolver = new SubtitleResolver(BaseItem.LocalizationManager);
@@ -211,9 +199,9 @@ namespace MediaBrowser.Providers.MediaInfo
private string NormalizeStrmLine(string line)
{
- return line.Replace("\t", string.Empty)
- .Replace("\r", string.Empty)
- .Replace("\n", string.Empty)
+ return line.Replace("\t", string.Empty, StringComparison.Ordinal)
+ .Replace("\r", string.Empty, StringComparison.Ordinal)
+ .Replace("\n", string.Empty, StringComparison.Ordinal)
.Trim();
}
@@ -242,10 +230,11 @@ namespace MediaBrowser.Providers.MediaInfo
FetchShortcutInfo(item);
}
- var prober = new FFProbeAudioInfo(_mediaSourceManager, _mediaEncoder, _itemRepo, _appPaths, _json, _libraryManager);
+ var prober = new FFProbeAudioInfo(_mediaSourceManager, _mediaEncoder, _itemRepo, _libraryManager);
return prober.Probe(item, options, cancellationToken);
}
+
// Run last
public int Order => 100;
}
diff --git a/MediaBrowser.Providers/Music/Extensions.cs b/MediaBrowser.Providers/Music/Extensions.cs
index b57d35256..dddfd02e4 100644
--- a/MediaBrowser.Providers/Music/Extensions.cs
+++ b/MediaBrowser.Providers/Music/Extensions.cs
@@ -6,7 +6,7 @@ using MediaBrowser.Model.Entities;
namespace MediaBrowser.Providers.Music
{
- public static class Extensions
+ public static class AlbumInfoExtensions
{
public static string GetAlbumArtist(this AlbumInfo info)
{
@@ -18,7 +18,7 @@ namespace MediaBrowser.Providers.Music
return id;
}
- return info.AlbumArtists.FirstOrDefault();
+ return info.AlbumArtists.Count > 0 ? info.AlbumArtists[0] : default;
}
public static string GetReleaseGroupId(this AlbumInfo info)
diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/AlbumProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/AlbumProvider.cs
index 23acb7fd6..7f10e6922 100644
--- a/MediaBrowser.Providers/Plugins/MusicBrainz/AlbumProvider.cs
+++ b/MediaBrowser.Providers/Plugins/MusicBrainz/AlbumProvider.cs
@@ -276,7 +276,7 @@ namespace MediaBrowser.Providers.Music
private async Task<ReleaseResult> GetReleaseResult(string albumName, string artistId, CancellationToken cancellationToken)
{
- var url = string.Format("/ws/2/release/?query=\"{0}\" AND arid:{1}",
+ var url = string.Format(CultureInfo.InvariantCulture, "/ws/2/release/?query=\"{0}\" AND arid:{1}",
WebUtility.UrlEncode(albumName),
artistId);
@@ -740,11 +740,11 @@ namespace MediaBrowser.Providers.Music
// MusicBrainz request a contact email address is supplied, as comment, in user agent field:
// https://musicbrainz.org/doc/XML_Web_Service/Rate_Limiting#User-Agent
- options.Headers.UserAgent.Add(new ProductInfoHeaderValue(string.Format(
+ options.Headers.UserAgent.ParseAdd(string.Format(
CultureInfo.InvariantCulture,
"{0} ( {1} )",
_appHost.ApplicationUserAgent,
- _appHost.ApplicationUserAgentAddress)));
+ _appHost.ApplicationUserAgentAddress));
HttpResponseMessage response;
var attempts = 0u;
diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs
index b829ed378..781b71640 100644
--- a/MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs
+++ b/MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs
@@ -46,7 +46,7 @@ namespace MediaBrowser.Providers.Music
// They seem to throw bad request failures on any term with a slash
var nameToSearch = searchInfo.Name.Replace('/', ' ');
- var url = string.Format("/ws/2/artist/?query=\"{0}\"&dismax=true", UrlEncode(nameToSearch));
+ var url = string.Format(CultureInfo.InvariantCulture, "/ws/2/artist/?query=\"{0}\"&dismax=true", UrlEncode(nameToSearch));
using (var response = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false))
await using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
@@ -62,7 +62,7 @@ namespace MediaBrowser.Providers.Music
if (HasDiacritics(searchInfo.Name))
{
// Try again using the search with accent characters url
- url = string.Format("/ws/2/artist/?query=artistaccent:\"{0}\"", UrlEncode(nameToSearch));
+ url = string.Format(CultureInfo.InvariantCulture, "/ws/2/artist/?query=artistaccent:\"{0}\"", UrlEncode(nameToSearch));
using var response = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false);
await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs
index 41e664aac..c18725e0a 100644
--- a/MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Net.Http;
+using System.Globalization;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common;
@@ -70,7 +71,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
list.Add(new RemoteImageInfo
{
ProviderName = Name,
- Url = string.Format("https://img.omdbapi.com/?i={0}&apikey=2c9d9507", imdbId)
+ Url = string.Format(CultureInfo.InvariantCulture, "https://img.omdbapi.com/?i={0}&apikey=2c9d9507", imdbId)
});
}
}
diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs
index d2823a08c..102ad82e1 100644
--- a/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs
@@ -127,7 +127,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
}
}
- var url = OmdbProvider.GetOmdbUrl(urlQuery, _appHost, cancellationToken);
+ var url = OmdbProvider.GetOmdbUrl(urlQuery);
using var response = await OmdbProvider.GetOmdbResponse(_httpClientFactory.CreateClient(), url, cancellationToken).ConfigureAwait(false);
await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs
index 6ad5298de..c45149c3a 100644
--- a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs
@@ -256,16 +256,16 @@ namespace MediaBrowser.Providers.Plugins.Omdb
return false;
}
- public static string GetOmdbUrl(string query, IApplicationHost appHost, CancellationToken cancellationToken)
+ public static string GetOmdbUrl(string query)
{
- const string url = "https://www.omdbapi.com?apikey=2c9d9507";
+ const string Url = "https://www.omdbapi.com?apikey=2c9d9507";
if (string.IsNullOrWhiteSpace(query))
{
- return url;
+ return Url;
}
- return url + "&" + query;
+ return Url + "&" + query;
}
private async Task<string> EnsureItemInfo(string imdbId, CancellationToken cancellationToken)
@@ -290,7 +290,11 @@ namespace MediaBrowser.Providers.Plugins.Omdb
}
}
- var url = GetOmdbUrl(string.Format("i={0}&plot=short&tomatoes=true&r=json", imdbParam), _appHost, cancellationToken);
+ var url = GetOmdbUrl(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ "i={0}&plot=short&tomatoes=true&r=json",
+ imdbParam));
using var response = await GetOmdbResponse(_httpClientFactory.CreateClient(), url, cancellationToken).ConfigureAwait(false);
await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
@@ -323,7 +327,12 @@ namespace MediaBrowser.Providers.Plugins.Omdb
}
}
- var url = GetOmdbUrl(string.Format("i={0}&season={1}&detail=full", imdbParam, seasonId), _appHost, cancellationToken);
+ var url = GetOmdbUrl(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ "i={0}&season={1}&detail=full",
+ imdbParam,
+ seasonId));
using var response = await GetOmdbResponse(_httpClientFactory.CreateClient(), url, cancellationToken).ConfigureAwait(false);
await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
@@ -348,7 +357,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
var dataPath = Path.Combine(_configurationManager.ApplicationPaths.CachePath, "omdb");
- var filename = string.Format("{0}.json", imdbId);
+ var filename = string.Format(CultureInfo.InvariantCulture, "{0}.json", imdbId);
return Path.Combine(dataPath, filename);
}
@@ -362,7 +371,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
var dataPath = Path.Combine(_configurationManager.ApplicationPaths.CachePath, "omdb");
- var filename = string.Format("{0}_season_{1}.json", imdbId, seasonId);
+ var filename = string.Format(CultureInfo.InvariantCulture, "{0}_season_{1}.json", imdbId, seasonId);
return Path.Combine(dataPath, filename);
}
diff --git a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbClientManager.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbClientManager.cs
index cd2f96f14..f22d484ab 100644
--- a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbClientManager.cs
+++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbClientManager.cs
@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
+using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
@@ -19,7 +20,6 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
{
private const string DefaultLanguage = "en";
- private readonly SemaphoreSlim _cacheWriteLock = new SemaphoreSlim(1, 1);
private readonly IMemoryCache _cache;
private readonly TvDbClient _tvDbClient;
private DateTime _tokenCreatedAt;
@@ -176,7 +176,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
string language,
CancellationToken cancellationToken)
{
- searchInfo.SeriesProviderIds.TryGetValue(MetadataProvider.Tvdb.ToString(),
+ searchInfo.SeriesProviderIds.TryGetValue(nameof(MetadataProvider.Tvdb),
out var seriesTvdbId);
var episodeQuery = new EpisodeQuery();
@@ -203,10 +203,10 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
else if (searchInfo.PremiereDate.HasValue)
{
// tvdb expects yyyy-mm-dd format
- episodeQuery.FirstAired = searchInfo.PremiereDate.Value.ToString("yyyy-MM-dd");
+ episodeQuery.FirstAired = searchInfo.PremiereDate.Value.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
}
- return GetEpisodeTvdbId(Convert.ToInt32(seriesTvdbId), episodeQuery, language, cancellationToken);
+ return GetEpisodeTvdbId(Convert.ToInt32(seriesTvdbId, CultureInfo.InvariantCulture), episodeQuery, language, cancellationToken);
}
public async Task<string> GetEpisodeTvdbId(
@@ -218,7 +218,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
var episodePage =
await GetEpisodesPageAsync(Convert.ToInt32(seriesTvdbId), episodeQuery, language, cancellationToken)
.ConfigureAwait(false);
- return episodePage.Data.FirstOrDefault()?.Id.ToString();
+ return episodePage.Data.FirstOrDefault()?.Id.ToString(CultureInfo.InvariantCulture);
}
public Task<TvDbResponse<EpisodeRecord[]>> GetEpisodesPageAsync(
@@ -276,23 +276,10 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
return cachedValue;
}
- await _cacheWriteLock.WaitAsync().ConfigureAwait(false);
- try
- {
- if (_cache.TryGetValue(key, out cachedValue))
- {
- return cachedValue;
- }
-
- _tvDbClient.AcceptedLanguage = TvdbUtils.NormalizeLanguage(language) ?? DefaultLanguage;
- var result = await resultFactory.Invoke().ConfigureAwait(false);
- _cache.Set(key, result, TimeSpan.FromHours(1));
- return result;
- }
- finally
- {
- _cacheWriteLock.Release();
- }
+ _tvDbClient.AcceptedLanguage = TvdbUtils.NormalizeLanguage(language) ?? DefaultLanguage;
+ var result = await resultFactory.Invoke().ConfigureAwait(false);
+ _cache.Set(key, result, TimeSpan.FromHours(1));
+ return result;
}
private static string GenerateKey(params object[] objects)
diff --git a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeImageProvider.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeImageProvider.cs
index 4d38d38dc..4e7c0e5a6 100644
--- a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeImageProvider.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
+using System.Globalization;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Entities;
@@ -76,7 +77,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
var episodeResult =
await _tvdbClientManager
- .GetEpisodesAsync(Convert.ToInt32(episodeTvdbId), language, cancellationToken)
+ .GetEpisodesAsync(Convert.ToInt32(episodeTvdbId, CultureInfo.InvariantCulture), language, cancellationToken)
.ConfigureAwait(false);
var image = GetImageInfo(episodeResult.Data);
@@ -103,8 +104,8 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
return new RemoteImageInfo
{
- Width = Convert.ToInt32(episode.ThumbWidth),
- Height = Convert.ToInt32(episode.ThumbHeight),
+ Width = Convert.ToInt32(episode.ThumbWidth, CultureInfo.InvariantCulture),
+ Height = Convert.ToInt32(episode.ThumbHeight, CultureInfo.InvariantCulture),
ProviderName = Name,
Url = TvdbUtils.BannerUrl + episode.Filename,
Type = ImageType.Primary
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs
index 4f86a0293..4da2c042f 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs
@@ -180,7 +180,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets
if (!string.IsNullOrEmpty(language))
{
- url += string.Format("&language={0}", TmdbMovieProvider.NormalizeLanguage(language));
+ url += string.Format(CultureInfo.InvariantCulture, "&language={0}", TmdbMovieProvider.NormalizeLanguage(language));
// Get images in english and with no language
url += "&include_image_language=" + TmdbMovieProvider.GetImageLanguagesParam(language);
@@ -250,7 +250,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets
{
var path = GetDataPath(appPaths, tmdbId);
- var filename = string.Format("all-{0}.json", preferredLanguage ?? string.Empty);
+ var filename = string.Format(CultureInfo.InvariantCulture, "all-{0}.json", preferredLanguage ?? string.Empty);
return Path.Combine(path, filename);
}
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/GenericTmdbMovieInfo.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/GenericTmdbMovieInfo.cs
index 27ca3759e..01a887eed 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/GenericTmdbMovieInfo.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/GenericTmdbMovieInfo.cs
@@ -300,7 +300,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
{
movie.RemoteTrailers = movieData.Trailers.Youtube.Select(i => new MediaUrl
{
- Url = string.Format("https://www.youtube.com/watch?v={0}", i.Source),
+ Url = string.Format(CultureInfo.InvariantCulture, "https://www.youtube.com/watch?v={0}", i.Source),
Name = i.Name
}).ToArray();
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs
index 685561e2d..d8918bb6b 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs
@@ -30,6 +30,9 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
/// </summary>
public class TmdbMovieProvider : IRemoteMetadataProvider<Movie, MovieInfo>, IHasOrder
{
+ private const string TmdbConfigUrl = TmdbUtils.BaseTmdbApiUrl + "3/configuration?api_key={0}";
+ private const string GetMovieInfo3 = TmdbUtils.BaseTmdbApiUrl + @"3/movie/{0}?api_key={1}&append_to_response=casts,releases,images,keywords,trailers";
+
internal static TmdbMovieProvider Current { get; private set; }
private readonly IJsonSerializer _jsonSerializer;
@@ -157,9 +160,6 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
return _tmdbSettings;
}
- private const string TmdbConfigUrl = TmdbUtils.BaseTmdbApiUrl + "3/configuration?api_key={0}";
- private const string GetMovieInfo3 = TmdbUtils.BaseTmdbApiUrl + @"3/movie/{0}?api_key={1}&append_to_response=casts,releases,images,keywords,trailers";
-
/// <summary>
/// Gets the movie data path.
/// </summary>
@@ -334,7 +334,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header));
}
- using var mainResponse = await GetMovieDbResponse(requestMessage);
+ using var mainResponse = await GetMovieDbResponse(requestMessage).ConfigureAwait(false);
if (mainResponse.StatusCode == HttpStatusCode.NotFound)
{
return null;
@@ -367,7 +367,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
langRequestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header));
}
- using var langResponse = await GetMovieDbResponse(langRequestMessage);
+ using var langResponse = await GetMovieDbResponse(langRequestMessage).ConfigureAwait(false);
await using var langStream = await langResponse.Content.ReadAsStreamAsync().ConfigureAwait(false);
var langResult = await _jsonSerializer.DeserializeFromStreamAsync<MovieResult>(stream).ConfigureAwait(false);
@@ -380,10 +380,10 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
/// <summary>
/// Gets the movie db response.
/// </summary>
- internal async Task<HttpResponseMessage> GetMovieDbResponse(HttpRequestMessage message)
+ internal Task<HttpResponseMessage> GetMovieDbResponse(HttpRequestMessage message)
{
- message.Headers.UserAgent.Add(new ProductInfoHeaderValue(_appHost.ApplicationUserAgent));
- return await _httpClientFactory.CreateClient().SendAsync(message);
+ message.Headers.UserAgent.ParseAdd(_appHost.ApplicationUserAgent);
+ return _httpClientFactory.CreateClient().SendAsync(message);
}
/// <inheritdoc />
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbSearch.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbSearch.cs
index 48f2a68a6..2a6c6d035 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbSearch.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbSearch.cs
@@ -22,11 +22,16 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
{
public class TmdbSearch
{
+ private const string SearchUrl = TmdbUtils.BaseTmdbApiUrl + @"3/search/{3}?api_key={1}&query={0}&language={2}";
+ private const string SearchUrlTvWithYear = TmdbUtils.BaseTmdbApiUrl + @"3/search/tv?api_key={1}&query={0}&language={2}&first_air_date_year={3}";
+ private const string SearchUrlMovieWithYear = TmdbUtils.BaseTmdbApiUrl + @"3/search/movie?api_key={1}&query={0}&language={2}&primary_release_year={3}";
+
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
+ 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
@@ -37,8 +42,6 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
).* # 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;
private readonly ILibraryManager _libraryManager;
@@ -124,7 +127,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
name2 = name2.Trim();
// Search again if the new name is different
- if (!string.Equals(name2, name) && !string.IsNullOrWhiteSpace(name2))
+ if (!string.Equals(name2, name, StringComparison.Ordinal) && !string.IsNullOrWhiteSpace(name2))
{
_logger.LogInformation("TmdbSearch: Finding id for item: {0} ({1})", name2, year);
results = await GetSearchResults(name2, searchType, year, language, tmdbImageUrl, cancellationToken).ConfigureAwait(false);
@@ -164,12 +167,32 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
{
if (string.IsNullOrWhiteSpace(name))
{
- throw new ArgumentException("name");
+ throw new ArgumentException("String can't be null or empty.", nameof(name));
}
- var url3 = string.Format(_searchURL, WebUtility.UrlEncode(name), TmdbUtils.ApiKey, language, type);
+ string url3;
+ if (year != null && string.Equals(type, "movie", StringComparison.OrdinalIgnoreCase))
+ {
+ url3 = string.Format(
+ CultureInfo.InvariantCulture,
+ SearchUrlMovieWithYear,
+ WebUtility.UrlEncode(name),
+ TmdbUtils.ApiKey,
+ language,
+ year);
+ }
+ else
+ {
+ url3 = string.Format(
+ CultureInfo.InvariantCulture,
+ SearchUrl,
+ WebUtility.UrlEncode(name),
+ TmdbUtils.ApiKey,
+ language,
+ type);
+ }
- var requestMessage = new HttpRequestMessage(HttpMethod.Get, url3);
+ using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url3);
foreach (var header in TmdbUtils.AcceptHeaders)
{
requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header));
@@ -207,12 +230,32 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
{
if (string.IsNullOrWhiteSpace(name))
{
- throw new ArgumentException("name");
+ throw new ArgumentException("String can't be null or empty.", nameof(name));
}
- var url3 = string.Format(_searchURL, WebUtility.UrlEncode(name), TmdbUtils.ApiKey, language, "tv");
+ string url3;
+ if (year == null)
+ {
+ url3 = string.Format(
+ CultureInfo.InvariantCulture,
+ SearchUrl,
+ WebUtility.UrlEncode(name),
+ TmdbUtils.ApiKey,
+ language,
+ "tv");
+ }
+ else
+ {
+ url3 = string.Format(
+ CultureInfo.InvariantCulture,
+ SearchUrlTvWithYear,
+ WebUtility.UrlEncode(name),
+ TmdbUtils.ApiKey,
+ language,
+ year);
+ }
- var requestMessage = new HttpRequestMessage(HttpMethod.Get, url3);
+ using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url3);
foreach (var header in TmdbUtils.AcceptHeaders)
{
requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header));
@@ -227,7 +270,12 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
return results
.Select(i =>
{
- var remoteResult = new RemoteSearchResult {SearchProviderName = TmdbMovieProvider.Current.Name, Name = i.Name ?? i.Original_Name, ImageUrl = string.IsNullOrWhiteSpace(i.Poster_Path) ? null : baseImageUrl + i.Poster_Path};
+ var remoteResult = new RemoteSearchResult
+ {
+ SearchProviderName = TmdbMovieProvider.Current.Name,
+ Name = i.Name ?? i.Original_Name,
+ ImageUrl = string.IsNullOrWhiteSpace(i.Poster_Path) ? null : baseImageUrl + i.Poster_Path
+ };
if (!string.IsNullOrWhiteSpace(i.First_Air_Date))
{
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs
index b4aef4542..90e3cea93 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs
@@ -131,7 +131,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
{
if (video.Site.Equals("youtube", System.StringComparison.OrdinalIgnoreCase))
{
- var videoUrl = string.Format("http://www.youtube.com/watch?v={0}", video.Key);
+ var videoUrl = string.Format(CultureInfo.InvariantCulture, "http://www.youtube.com/watch?v={0}", video.Key);
item.AddTrailerUrl(videoUrl);
}
}
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProviderBase.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProviderBase.cs
index 154664321..5705885b4 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProviderBase.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProviderBase.cs
@@ -92,7 +92,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
var path = TmdbSeriesProvider.GetSeriesDataPath(_configurationManager.ApplicationPaths, tmdbId);
- var filename = string.Format("season-{0}-episode-{1}-{2}.json",
+ var filename = string.Format(CultureInfo.InvariantCulture, "season-{0}-episode-{1}-{2}.json",
seasonNumber.ToString(CultureInfo.InvariantCulture),
episodeNumber.ToString(CultureInfo.InvariantCulture),
preferredLanguage);
@@ -116,7 +116,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
if (!string.IsNullOrEmpty(language))
{
- url += string.Format("&language={0}", language);
+ url += string.Format(CultureInfo.InvariantCulture, "&language={0}", language);
}
var includeImageLanguageParam = TmdbMovieProvider.GetImageLanguagesParam(language);
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs
index 2b9077f55..e59504cc6 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs
@@ -180,7 +180,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
var path = TmdbSeriesProvider.GetSeriesDataPath(_configurationManager.ApplicationPaths, tmdbId);
- var filename = string.Format("season-{0}-{1}.json",
+ var filename = string.Format(CultureInfo.InvariantCulture, "season-{0}-{1}.json",
seasonNumber.ToString(CultureInfo.InvariantCulture),
preferredLanguage);
@@ -203,7 +203,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
if (!string.IsNullOrEmpty(language))
{
- url += string.Format("&language={0}", TmdbMovieProvider.NormalizeLanguage(language));
+ url += string.Format(CultureInfo.InvariantCulture, "&language={0}", TmdbMovieProvider.NormalizeLanguage(language));
}
var includeImageLanguageParam = TmdbMovieProvider.GetImageLanguagesParam(language);
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs
index ac577b125..0eded3233 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs
@@ -496,7 +496,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
var path = GetSeriesDataPath(_configurationManager.ApplicationPaths, tmdbId);
- var filename = string.Format("series-{0}.json", preferredLanguage ?? string.Empty);
+ var filename = string.Format(CultureInfo.InvariantCulture, "series-{0}.json", preferredLanguage ?? string.Empty);
return Path.Combine(path, filename);
}
diff --git a/MediaBrowser.Providers/Studios/StudiosImageProvider.cs b/MediaBrowser.Providers/Studios/StudiosImageProvider.cs
index dad155c81..321153c6b 100644
--- a/MediaBrowser.Providers/Studios/StudiosImageProvider.cs
+++ b/MediaBrowser.Providers/Studios/StudiosImageProvider.cs
@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
+using System.Globalization;
using System.IO;
using System.Linq;
using System.Net.Http;
@@ -100,7 +101,7 @@ namespace MediaBrowser.Providers.Studios
private string GetUrl(string image, string filename)
{
- return string.Format("https://raw.github.com/MediaBrowser/MediaBrowser.Resources/master/images/imagesbyname/studios/{0}/{1}.jpg", image, filename);
+ return string.Format(CultureInfo.InvariantCulture, "https://raw.github.com/MediaBrowser/MediaBrowser.Resources/master/images/imagesbyname/studios/{0}/{1}.jpg", image, filename);
}
private Task<string> EnsureThumbsList(string file, CancellationToken cancellationToken)
diff --git a/MediaBrowser.Providers/TV/DummySeasonProvider.cs b/MediaBrowser.Providers/TV/DummySeasonProvider.cs
index df09d13dd..0c09cdef6 100644
--- a/MediaBrowser.Providers/TV/DummySeasonProvider.cs
+++ b/MediaBrowser.Providers/TV/DummySeasonProvider.cs
@@ -87,7 +87,7 @@ namespace MediaBrowser.Providers.TV
else if (existingSeason.IsVirtualItem)
{
existingSeason.IsVirtualItem = false;
- existingSeason.UpdateToRepository(ItemUpdateType.MetadataEdit, cancellationToken);
+ await existingSeason.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, cancellationToken).ConfigureAwait(false);
seasons = null;
}
}
@@ -113,7 +113,7 @@ namespace MediaBrowser.Providers.TV
else if (existingSeason.IsVirtualItem)
{
existingSeason.IsVirtualItem = false;
- existingSeason.UpdateToRepository(ItemUpdateType.MetadataEdit, cancellationToken);
+ await existingSeason.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, cancellationToken).ConfigureAwait(false);
seasons = null;
}
}
diff --git a/bump_version b/bump_version
index 1c943f691..d2de5a0bd 100755
--- a/bump_version
+++ b/bump_version
@@ -20,6 +20,8 @@ fi
shared_version_file="./SharedVersion.cs"
build_file="./build.yaml"
+# csproj files for nuget packages
+jellyfin_subprojects=( MediaBrowser.Common/MediaBrowser.Common.csproj Jellyfin.Data/Jellyfin.Data.csproj MediaBrowser.Controller/MediaBrowser.Controller.csproj MediaBrowser.Model/MediaBrowser.Model.csproj Emby.Naming/Emby.Naming.csproj )
new_version="$1"
@@ -45,6 +47,22 @@ echo $old_version
old_version_sed="$( sed 's/\./\\./g' <<<"${old_version}" )" # Escape the '.' chars
sed -i "s/${old_version_sed}/${new_version}/g" ${build_file}
+# update nuget package version
+for subproject in ${jellyfin_subprojects[@]}; do
+do
+ echo ${subproject}
+ # Parse the version from the *.csproj file
+ old_version="$(
+ grep "VersionPrefix" ${subproject} \
+ | awk '{$1=$1};1' \
+ | sed -E 's/<VersionPrefix>([0-9\.]+[-a-z0-9]*)<\/VersionPrefix>/\1/'
+ )"
+ echo old nuget version: $old_version
+
+ # Set the nuget version to the specified new_version
+ sed -i "s|${old_version}|${new_version}|g" ${subproject}
+done
+
if [[ ${new_version} == *"-"* ]]; then
new_version_deb="$( sed 's/-/~/g' <<<"${new_version}" )"
else
diff --git a/fedora/jellyfin.spec b/fedora/jellyfin.spec
index b0a729a9e..a1e0b5f14 100644
--- a/fedora/jellyfin.spec
+++ b/fedora/jellyfin.spec
@@ -74,6 +74,9 @@ EOF
%{__install} -D -m 0755 %{SOURCE14} %{buildroot}%{_libexecdir}/jellyfin/restart.sh
%{__install} -D -m 0644 %{SOURCE16} %{buildroot}%{_prefix}/lib/firewalld/services/jellyfin.xml
+%files
+# empty as this is just a meta-package
+
%files server
%attr(755,root,root) %{_bindir}/jellyfin
%{_libdir}/jellyfin/*.json
diff --git a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs
index 9eb601edf..39fd8afda 100644
--- a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs
+++ b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs
@@ -13,15 +13,18 @@ namespace Jellyfin.MediaEncoding.Tests
[ClassData(typeof(GetFFmpegVersionTestData))]
public void GetFFmpegVersionTest(string versionOutput, Version? version)
{
- Assert.Equal(version, EncoderValidator.GetFFmpegVersion(versionOutput));
+ var val = new EncoderValidator(new NullLogger<EncoderValidatorTests>());
+ Assert.Equal(version, val.GetFFmpegVersion(versionOutput));
}
[Theory]
+ [InlineData(EncoderValidatorTestsData.FFmpegV431Output, true)]
[InlineData(EncoderValidatorTestsData.FFmpegV43Output, true)]
[InlineData(EncoderValidatorTestsData.FFmpegV421Output, true)]
[InlineData(EncoderValidatorTestsData.FFmpegV42Output, true)]
[InlineData(EncoderValidatorTestsData.FFmpegV414Output, true)]
[InlineData(EncoderValidatorTestsData.FFmpegV404Output, true)]
+ [InlineData(EncoderValidatorTestsData.FFmpegGitUnknownOutput2, true)]
[InlineData(EncoderValidatorTestsData.FFmpegGitUnknownOutput, false)]
public void ValidateVersionInternalTest(string versionOutput, bool valid)
{
@@ -33,11 +36,13 @@ namespace Jellyfin.MediaEncoding.Tests
{
public IEnumerator<object?[]> GetEnumerator()
{
+ yield return new object?[] { EncoderValidatorTestsData.FFmpegV431Output, new Version(4, 3, 1) };
yield return new object?[] { EncoderValidatorTestsData.FFmpegV43Output, new Version(4, 3) };
yield return new object?[] { EncoderValidatorTestsData.FFmpegV421Output, new Version(4, 2, 1) };
yield return new object?[] { EncoderValidatorTestsData.FFmpegV42Output, new Version(4, 2) };
yield return new object?[] { EncoderValidatorTestsData.FFmpegV414Output, new Version(4, 1, 4) };
yield return new object?[] { EncoderValidatorTestsData.FFmpegV404Output, new Version(4, 0, 4) };
+ yield return new object?[] { EncoderValidatorTestsData.FFmpegGitUnknownOutput2, new Version(4, 0) };
yield return new object?[] { EncoderValidatorTestsData.FFmpegGitUnknownOutput, null };
}
diff --git a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs
index f5ff3d723..9f5bef9a8 100644
--- a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs
+++ b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs
@@ -2,6 +2,18 @@ namespace Jellyfin.MediaEncoding.Tests
{
internal static class EncoderValidatorTestsData
{
+ public const string FFmpegV431Output = @"ffmpeg version n4.3.1 Copyright (c) 2000-2020 the FFmpeg developers
+built with gcc 10.1.0 (GCC)
+configuration: --prefix=/usr --disable-debug --disable-static --disable-stripping --enable-avisynth --enable-fontconfig --enable-gmp --enable-gnutls --enable-gpl --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libdav1d --enable-libdrm --enable-libfreetype --enable-libfribidi --enable-libgsm --enable-libiec61883 --enable-libjack --enable-libmfx --enable-libmodplug --enable-libmp3lame --enable-libopencore_amrnb --enable-libopencore_amrwb --enable-libopenjpeg --enable-libopus --enable-libpulse --enable-librav1e --enable-libsoxr --enable-libspeex --enable-libsrt --enable-libssh --enable-libtheora --enable-libv4l2 --enable-libvidstab --enable-libvmaf --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxcb --enable-libxml2 --enable-libxvid --enable-nvdec --enable-nvenc --enable-omx --enable-shared --enable-version3
+libavutil 56. 51.100 / 56. 51.100
+libavcodec 58. 91.100 / 58. 91.100
+libavformat 58. 45.100 / 58. 45.100
+libavdevice 58. 10.100 / 58. 10.100
+libavfilter 7. 85.100 / 7. 85.100
+libswscale 5. 7.100 / 5. 7.100
+libswresample 3. 7.100 / 3. 7.100
+libpostproc 55. 7.100 / 55. 7.100";
+
public const string FFmpegV43Output = @"ffmpeg version 4.3 Copyright (c) 2000-2020 the FFmpeg developers
built with gcc 7 (Ubuntu 7.5.0-3ubuntu1~18.04)
configuration: --prefix=/usr/lib/jellyfin-ffmpeg --target-os=linux --disable-doc --disable-ffplay --disable-shared --disable-libxcb --disable-vdpau --disable-sdl2 --disable-xlib --enable-gpl --enable-version3 --enable-static --enable-libfontconfig --enable-fontconfig --enable-gmp --enable-gnutls --enable-libass --enable-libbluray --enable-libdrm --enable-libfreetype --enable-libfribidi --enable-libmp3lame --enable-libopus --enable-libtheora --enable-libvorbis --enable-libwebp --enable-libx264 --enable-libx265 --enable-libzvbi --arch=amd64 --enable-amf --enable-nvenc --enable-nvdec --enable-vaapi --enable-opencl
@@ -63,7 +75,7 @@ libswscale 5. 1.100 / 5. 1.100
libswresample 3. 1.100 / 3. 1.100
libpostproc 55. 1.100 / 55. 1.100";
- public const string FFmpegGitUnknownOutput = @"ffmpeg version N-94303-g7cb4f8c962 Copyright (c) 2000-2019 the FFmpeg developers
+ public const string FFmpegGitUnknownOutput2 = @"ffmpeg version N-94303-g7cb4f8c962 Copyright (c) 2000-2019 the FFmpeg developers
built with gcc 9.1.1 (GCC) 20190716
configuration: --enable-gpl --enable-version3 --enable-sdl2 --enable-fontconfig --enable-gnutls --enable-iconv --enable-libass --enable-libdav1d --enable-libbluray --enable-libfreetype --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libopus --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libtheora --enable-libtwolame --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libzimg --enable-lzma --enable-zlib --enable-gmp --enable-libvidstab --enable-libvorbis --enable-libvo-amrwbenc --enable-libmysofa --enable-libspeex --enable-libxvid --enable-libaom --enable-libmfx --enable-amf --enable-ffnvcodec --enable-cuvid --enable-d3d11va --enable-nvenc --enable-nvdec --enable-dxva2 --enable-avisynth --enable-libopenmpt
libavutil 56. 30.100 / 56. 30.100
@@ -74,5 +86,17 @@ libavfilter 7. 56.101 / 7. 56.101
libswscale 5. 4.101 / 5. 4.101
libswresample 3. 4.100 / 3. 4.100
libpostproc 55. 4.100 / 55. 4.100";
+
+ public const string FFmpegGitUnknownOutput = @"ffmpeg version N-45325-gb173e0353-static https://johnvansickle.com/ffmpeg/ Copyright (c) 2000-2018 the FFmpeg developers
+built with gcc 6.3.0 (Debian 6.3.0-18+deb9u1) 20170516
+configuration: --enable-gpl --enable-version3 --enable-static --disable-debug --disable-ffplay --disable-indev=sndio --disable-outdev=sndio --cc=gcc-6 --enable-fontconfig --enable-frei0r --enable-gnutls --enable-gray --enable-libfribidi --enable-libass --enable-libfreetype --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-librubberband --enable-libsoxr --enable-libspeex --enable-libvorbis --enable-libopus --enable-libtheora --enable-libvidstab --enable-libvo-amrwbenc --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxvid --enable-libzimg
+libavutil 56. 9.100 / 56. 9.100
+libavcodec 58. 14.100 / 58. 14.100
+libavformat 58. 10.100 / 58. 10.100
+libavdevice 58. 2.100 / 58. 2.100
+libavfilter 7. 13.100 / 7. 13.100
+libswscale 5. 0.102 / 5. 0.102
+libswresample 3. 0.101 / 3. 0.101
+libpostproc 55. 0.100 / 55. 0.100";
}
}
diff --git a/tests/Jellyfin.MediaEncoding.Tests/FFprobeParserTests.cs b/tests/Jellyfin.MediaEncoding.Tests/FFprobeParserTests.cs
index 2032f6cec..c39ef0ce9 100644
--- a/tests/Jellyfin.MediaEncoding.Tests/FFprobeParserTests.cs
+++ b/tests/Jellyfin.MediaEncoding.Tests/FFprobeParserTests.cs
@@ -1,6 +1,7 @@
using System.IO;
using System.Text.Json;
using System.Threading.Tasks;
+using MediaBrowser.Common.Json;
using MediaBrowser.MediaEncoding.Probing;
using Xunit;
@@ -15,7 +16,7 @@ namespace Jellyfin.MediaEncoding.Tests
var path = Path.Join("Test Data", fileName);
using (var stream = File.OpenRead(path))
{
- await JsonSerializer.DeserializeAsync<InternalMediaInfoResult>(stream).ConfigureAwait(false);
+ await JsonSerializer.DeserializeAsync<InternalMediaInfoResult>(stream, JsonDefaults.GetOptions()).ConfigureAwait(false);
}
}
}
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Library/IgnorePatternsTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Library/IgnorePatternsTests.cs
index b4e6db8f3..09eb22328 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Library/IgnorePatternsTests.cs
+++ b/tests/Jellyfin.Server.Implementations.Tests/Library/IgnorePatternsTests.cs
@@ -30,8 +30,7 @@ namespace Jellyfin.Server.Implementations.Tests.Library
[InlineData("/media/movies/.@__thumb/foo-bar-thumbnail.png", true)]
[InlineData("/media/music/Foo B.A.R./epic.flac", false)]
[InlineData("/media/music/Foo B.A.R", false)]
- // This test is pending an upstream fix: https://github.com/dazinator/DotNet.Glob/issues/78
- // [InlineData("/media/music/Foo B.A.R.", false)]
+ [InlineData("/media/music/Foo B.A.R.", false)]
public void PathIgnored(string path, bool expected)
{
Assert.Equal(expected, IgnorePatterns.ShouldIgnore(path));
diff --git a/tests/MediaBrowser.Api.Tests/JellyfinApplicationFactory.cs b/tests/MediaBrowser.Api.Tests/JellyfinApplicationFactory.cs
index c39ed07de..2029f88e9 100644
--- a/tests/MediaBrowser.Api.Tests/JellyfinApplicationFactory.cs
+++ b/tests/MediaBrowser.Api.Tests/JellyfinApplicationFactory.cs
@@ -72,6 +72,7 @@ namespace MediaBrowser.Api.Tests
var startupConfig = Program.CreateAppConfiguration(commandLineOpts, appPaths);
ILoggerFactory loggerFactory = new SerilogLoggerFactory();
+ var serviceCollection = new ServiceCollection();
_disposableComponents.Add(loggerFactory);
// Create the app host and initialize it
@@ -80,10 +81,10 @@ namespace MediaBrowser.Api.Tests
loggerFactory,
commandLineOpts,
new ManagedFileSystem(loggerFactory.CreateLogger<ManagedFileSystem>(), appPaths),
- new NetworkManager(loggerFactory.CreateLogger<NetworkManager>()));
+ new NetworkManager(loggerFactory.CreateLogger<NetworkManager>()),
+ serviceCollection);
_disposableComponents.Add(appHost);
- var serviceCollection = new ServiceCollection();
- appHost.Init(serviceCollection);
+ appHost.Init();
// Configure the web host builder
Program.ConfigureWebHostBuilder(builder, appHost, serviceCollection, commandLineOpts, startupConfig, appPaths);