aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Foust <luke@foust.com>2020-03-20 12:53:52 -0700
committerGitHub <noreply@github.com>2020-03-20 12:53:52 -0700
commitdcd0d93f44bf1befea5e61993bc868b5846102a0 (patch)
tree740f132fce52947fc8a73621588ca36c3447922a
parent80dfc78ba5ec3895fd374a5bd761d0d0e9e6a862 (diff)
parente028fb6fdefa6120fc6109c63d883d21412c31bd (diff)
Merge pull request #1 from jellyfin/master
merge with upstream master
-rw-r--r--.ci/azure-pipelines-compat.yml96
-rw-r--r--.ci/azure-pipelines-main.yml101
-rw-r--r--.ci/azure-pipelines-test.yml65
-rw-r--r--.ci/azure-pipelines-windows.yml82
-rw-r--r--.ci/azure-pipelines.yml324
-rw-r--r--.ci/publish-nightly.yml46
-rw-r--r--.ci/publish-release.yml48
-rw-r--r--.github/ISSUE_TEMPLATE/bug_report.md20
-rw-r--r--.github/ISSUE_TEMPLATE/media_playback.md6
-rw-r--r--CONTRIBUTORS.md146
-rw-r--r--Dockerfile26
-rw-r--r--Dockerfile.arm36
-rw-r--r--Dockerfile.arm6427
-rw-r--r--DvdLib/DvdLib.csproj2
-rw-r--r--DvdLib/Ifo/Dvd.cs4
-rw-r--r--Emby.Dlna/Api/DlnaServerService.cs18
-rw-r--r--Emby.Dlna/Api/DlnaService.cs2
-rw-r--r--Emby.Dlna/Common/Argument.cs1
-rw-r--r--Emby.Dlna/Common/DeviceIcon.cs10
-rw-r--r--Emby.Dlna/Common/DeviceService.cs6
-rw-r--r--Emby.Dlna/Common/ServiceAction.cs13
-rw-r--r--Emby.Dlna/Common/StateVariable.cs17
-rw-r--r--Emby.Dlna/Configuration/DlnaOptions.cs26
-rw-r--r--Emby.Dlna/ConfigurationExtension.cs2
-rw-r--r--Emby.Dlna/ConnectionManager/ConnectionManager.cs15
-rw-r--r--Emby.Dlna/ConnectionManager/ConnectionManagerXmlBuilder.cs2
-rw-r--r--Emby.Dlna/ConnectionManager/ControlHandler.cs30
-rw-r--r--Emby.Dlna/ConnectionManager/ServiceActionListBuilder.cs2
-rw-r--r--Emby.Dlna/ContentDirectory/ContentDirectory.cs11
-rw-r--r--Emby.Dlna/ContentDirectory/ContentDirectoryXmlBuilder.cs2
-rw-r--r--Emby.Dlna/ContentDirectory/ControlHandler.cs372
-rw-r--r--Emby.Dlna/ContentDirectory/ServiceActionListBuilder.cs2
-rw-r--r--Emby.Dlna/ControlRequest.cs2
-rw-r--r--Emby.Dlna/ControlResponse.cs12
-rw-r--r--Emby.Dlna/Didl/DidlBuilder.cs5
-rw-r--r--Emby.Dlna/Didl/Filter.cs4
-rw-r--r--Emby.Dlna/Didl/StringWriterWithEncoding.cs2
-rw-r--r--Emby.Dlna/DlnaManager.cs4
-rw-r--r--Emby.Dlna/Emby.Dlna.csproj13
-rw-r--r--Emby.Dlna/EventSubscriptionResponse.cs13
-rw-r--r--Emby.Dlna/Eventing/EventManager.cs5
-rw-r--r--Emby.Dlna/Eventing/EventSubscription.cs6
-rw-r--r--Emby.Dlna/IConnectionManager.cs1
-rw-r--r--Emby.Dlna/IContentDirectory.cs1
-rw-r--r--Emby.Dlna/IEventManager.cs1
-rw-r--r--Emby.Dlna/IMediaReceiverRegistrar.cs1
-rw-r--r--Emby.Dlna/IUpnpService.cs6
-rw-r--r--Emby.Dlna/Main/DlnaEntryPoint.cs21
-rw-r--r--Emby.Dlna/MediaReceiverRegistrar/ControlHandler.cs43
-rw-r--r--Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrar.cs14
-rw-r--r--Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs2
-rw-r--r--Emby.Dlna/MediaReceiverRegistrar/ServiceActionListBuilder.cs2
-rw-r--r--Emby.Dlna/PlayTo/Device.cs32
-rw-r--r--Emby.Dlna/PlayTo/DeviceInfo.cs2
-rw-r--r--Emby.Dlna/PlayTo/PlayToController.cs3
-rw-r--r--Emby.Dlna/PlayTo/PlayToManager.cs10
-rw-r--r--Emby.Dlna/PlayTo/PlaybackProgressEventArgs.cs2
-rw-r--r--Emby.Dlna/PlayTo/PlaybackStartEventArgs.cs2
-rw-r--r--Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs2
-rw-r--r--Emby.Dlna/PlayTo/PlaylistItem.cs2
-rw-r--r--Emby.Dlna/PlayTo/PlaylistItemFactory.cs3
-rw-r--r--Emby.Dlna/PlayTo/SsdpHttpClient.cs14
-rw-r--r--Emby.Dlna/PlayTo/TRANSPORTSTATE.cs2
-rw-r--r--Emby.Dlna/PlayTo/TransportCommands.cs2
-rw-r--r--Emby.Dlna/PlayTo/UpnpContainer.cs2
-rw-r--r--Emby.Dlna/PlayTo/uBaseObject.cs2
-rw-r--r--Emby.Dlna/PlayTo/uPnpNamespaces.cs2
-rw-r--r--Emby.Dlna/Profiles/DefaultProfile.cs2
-rw-r--r--Emby.Dlna/Profiles/DenonAvrProfile.cs2
-rw-r--r--Emby.Dlna/Profiles/DirectTvProfile.cs2
-rw-r--r--Emby.Dlna/Profiles/DishHopperJoeyProfile.cs2
-rw-r--r--Emby.Dlna/Profiles/Foobar2000Profile.cs2
-rw-r--r--Emby.Dlna/Profiles/LgTvProfile.cs2
-rw-r--r--Emby.Dlna/Profiles/LinksysDMA2100Profile.cs2
-rw-r--r--Emby.Dlna/Profiles/MarantzProfile.cs2
-rw-r--r--Emby.Dlna/Profiles/MediaMonkeyProfile.cs2
-rw-r--r--Emby.Dlna/Profiles/PanasonicVieraProfile.cs2
-rw-r--r--Emby.Dlna/Profiles/PopcornHourProfile.cs2
-rw-r--r--Emby.Dlna/Profiles/SamsungSmartTvProfile.cs2
-rw-r--r--Emby.Dlna/Profiles/SharpSmartTvProfile.cs2
-rw-r--r--Emby.Dlna/Profiles/SonyBlurayPlayer2013.cs2
-rw-r--r--Emby.Dlna/Profiles/SonyBlurayPlayer2014.cs2
-rw-r--r--Emby.Dlna/Profiles/SonyBlurayPlayer2015.cs2
-rw-r--r--Emby.Dlna/Profiles/SonyBlurayPlayer2016.cs2
-rw-r--r--Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs2
-rw-r--r--Emby.Dlna/Profiles/SonyBravia2010Profile.cs2
-rw-r--r--Emby.Dlna/Profiles/SonyBravia2011Profile.cs2
-rw-r--r--Emby.Dlna/Profiles/SonyBravia2012Profile.cs2
-rw-r--r--Emby.Dlna/Profiles/SonyBravia2013Profile.cs2
-rw-r--r--Emby.Dlna/Profiles/SonyBravia2014Profile.cs2
-rw-r--r--Emby.Dlna/Profiles/SonyPs3Profile.cs2
-rw-r--r--Emby.Dlna/Profiles/SonyPs4Profile.cs2
-rw-r--r--Emby.Dlna/Profiles/WdtvLiveProfile.cs2
-rw-r--r--Emby.Dlna/Profiles/XboxOneProfile.cs2
-rw-r--r--Emby.Dlna/Server/DescriptionXmlBuilder.cs3
-rw-r--r--Emby.Dlna/Service/BaseControlHandler.cs112
-rw-r--r--Emby.Dlna/Service/BaseService.cs4
-rw-r--r--Emby.Dlna/Service/ControlErrorHandler.cs6
-rw-r--r--Emby.Dlna/Service/ServiceXmlBuilder.cs2
-rw-r--r--Emby.Dlna/Ssdp/DeviceDiscovery.cs14
-rw-r--r--Emby.Dlna/Ssdp/Extensions.cs2
-rw-r--r--Emby.Drawing/Emby.Drawing.csproj12
-rw-r--r--Emby.Drawing/ImageProcessor.cs351
-rw-r--r--Emby.Naming/Audio/AlbumParser.cs12
-rw-r--r--Emby.Naming/Audio/AudioFileParser.cs14
-rw-r--r--Emby.Naming/Audio/MultiPartResult.cs26
-rw-r--r--Emby.Naming/AudioBook/AudioBookFileInfo.cs2
-rw-r--r--Emby.Naming/AudioBook/AudioBookFilePathParser.cs1
-rw-r--r--Emby.Naming/AudioBook/AudioBookFilePathParserResult.cs1
-rw-r--r--Emby.Naming/AudioBook/AudioBookListResolver.cs20
-rw-r--r--Emby.Naming/AudioBook/AudioBookResolver.cs1
-rw-r--r--Emby.Naming/Common/EpisodeExpression.cs37
-rw-r--r--Emby.Naming/Common/MediaType.cs1
-rw-r--r--Emby.Naming/Common/NamingOptions.cs130
-rw-r--r--Emby.Naming/Emby.Naming.csproj5
-rw-r--r--Emby.Naming/Subtitles/SubtitleInfo.cs1
-rw-r--r--Emby.Naming/Subtitles/SubtitleParser.cs4
-rw-r--r--Emby.Naming/TV/EpisodeInfo.cs1
-rw-r--r--Emby.Naming/TV/EpisodePathParser.cs8
-rw-r--r--Emby.Naming/TV/EpisodePathParserResult.cs1
-rw-r--r--Emby.Naming/TV/EpisodeResolver.cs21
-rw-r--r--Emby.Naming/TV/SeasonPathParser.cs80
-rw-r--r--Emby.Naming/TV/SeasonPathParserResult.cs1
-rw-r--r--Emby.Naming/Video/CleanDateTimeParser.cs72
-rw-r--r--Emby.Naming/Video/CleanDateTimeResult.cs30
-rw-r--r--Emby.Naming/Video/CleanStringParser.cs44
-rw-r--r--Emby.Naming/Video/CleanStringResult.cs20
-rw-r--r--Emby.Naming/Video/ExtraResolver.cs5
-rw-r--r--Emby.Naming/Video/ExtraResult.cs5
-rw-r--r--Emby.Naming/Video/ExtraRule.cs6
-rw-r--r--Emby.Naming/Video/ExtraRuleType.cs1
-rw-r--r--Emby.Naming/Video/FileStack.cs1
-rw-r--r--Emby.Naming/Video/FlagParser.cs1
-rw-r--r--Emby.Naming/Video/Format3DParser.cs1
-rw-r--r--Emby.Naming/Video/Format3DResult.cs1
-rw-r--r--Emby.Naming/Video/Format3DRule.cs1
-rw-r--r--Emby.Naming/Video/StackResolver.cs26
-rw-r--r--Emby.Naming/Video/StackResult.cs17
-rw-r--r--Emby.Naming/Video/StubResolver.cs21
-rw-r--r--Emby.Naming/Video/StubResult.cs1
-rw-r--r--Emby.Naming/Video/StubTypeRule.cs1
-rw-r--r--Emby.Naming/Video/VideoFileInfo.cs8
-rw-r--r--Emby.Naming/Video/VideoInfo.cs18
-rw-r--r--Emby.Naming/Video/VideoListResolver.cs53
-rw-r--r--Emby.Naming/Video/VideoResolver.cs33
-rw-r--r--Emby.Notifications/Api/NotificationsService.cs52
-rw-r--r--Emby.Notifications/CoreNotificationTypes.cs4
-rw-r--r--Emby.Notifications/Emby.Notifications.csproj14
-rw-r--r--Emby.Notifications/NotificationConfigurationFactory.cs5
-rw-r--r--Emby.Notifications/NotificationEntryPoint.cs (renamed from Emby.Notifications/Notifications.cs)120
-rw-r--r--Emby.Notifications/NotificationManager.cs39
-rw-r--r--Emby.Photos/Emby.Photos.csproj13
-rw-r--r--Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs10
-rw-r--r--Emby.Server.Implementations/Activity/ActivityManager.cs1
-rw-r--r--Emby.Server.Implementations/ApplicationHost.cs179
-rw-r--r--Emby.Server.Implementations/Channels/ChannelPostScanTask.cs32
-rw-r--r--Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs30
-rw-r--r--Emby.Server.Implementations/Collections/CollectionImageProvider.cs1
-rw-r--r--Emby.Server.Implementations/Collections/CollectionManager.cs5
-rw-r--r--Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs1
-rw-r--r--Emby.Server.Implementations/ConfigurationOptions.cs4
-rw-r--r--Emby.Server.Implementations/Cryptography/CryptographyProvider.cs2
-rw-r--r--Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs2
-rw-r--r--Emby.Server.Implementations/Data/SqliteItemRepository.cs250
-rw-r--r--Emby.Server.Implementations/Devices/DeviceManager.cs22
-rw-r--r--Emby.Server.Implementations/Dto/DtoService.cs47
-rw-r--r--Emby.Server.Implementations/Emby.Server.Implementations.csproj18
-rw-r--r--Emby.Server.Implementations/EntryPoints/AutomaticRestartEntryPoint.cs127
-rw-r--r--Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs8
-rw-r--r--Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs40
-rw-r--r--Emby.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs9
-rw-r--r--Emby.Server.Implementations/EntryPoints/StartupWizard.cs15
-rw-r--r--Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs63
-rw-r--r--Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs44
-rw-r--r--Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs6
-rw-r--r--Emby.Server.Implementations/HttpServer/FileWriter.cs14
-rw-r--r--Emby.Server.Implementations/HttpServer/HttpListenerHost.cs28
-rw-r--r--Emby.Server.Implementations/HttpServer/HttpResultFactory.cs8
-rw-r--r--Emby.Server.Implementations/IO/ExtendedFileSystemInfo.cs2
-rw-r--r--Emby.Server.Implementations/IO/FileRefresher.cs40
-rw-r--r--Emby.Server.Implementations/IO/ManagedFileSystem.cs122
-rw-r--r--Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs13
-rw-r--r--Emby.Server.Implementations/Library/LibraryManager.cs171
-rw-r--r--Emby.Server.Implementations/Library/MediaSourceManager.cs19
-rw-r--r--Emby.Server.Implementations/Library/ResolverHelper.cs2
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs6
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs32
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs13
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs3
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs87
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/PhotoAlbumResolver.cs3
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs12
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs60
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs27
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs22
-rw-r--r--Emby.Server.Implementations/Library/UserManager.cs9
-rw-r--r--Emby.Server.Implementations/Library/Validators/ArtistsPostScanTask.cs5
-rw-r--r--Emby.Server.Implementations/Library/Validators/GenresPostScanTask.cs5
-rw-r--r--Emby.Server.Implementations/Library/Validators/MusicGenresPostScanTask.cs5
-rw-r--r--Emby.Server.Implementations/Library/Validators/StudiosPostScanTask.cs5
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs11
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs515
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs10
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs5
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EpgChannelData.cs67
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs3
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs3
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/NfoConfigurationExtensions.cs19
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs18
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs4
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs3
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs9
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs9
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs3
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs3
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvManager.cs5
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs48
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs3
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs5
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs3
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs3
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs7
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs3
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs4
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs7
-rw-r--r--Emby.Server.Implementations/Localization/Core/ar.json34
-rw-r--r--Emby.Server.Implementations/Localization/Core/bg-BG.json30
-rw-r--r--Emby.Server.Implementations/Localization/Core/bn.json96
-rw-r--r--Emby.Server.Implementations/Localization/Core/ca.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/da.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/de.json12
-rw-r--r--Emby.Server.Implementations/Localization/Core/es_DO.json21
-rw-r--r--Emby.Server.Implementations/Localization/Core/fi.json84
-rw-r--r--Emby.Server.Implementations/Localization/Core/fil.json95
-rw-r--r--Emby.Server.Implementations/Localization/Core/fr.json10
-rw-r--r--Emby.Server.Implementations/Localization/Core/gl.json3
-rw-r--r--Emby.Server.Implementations/Localization/Core/he.json60
-rw-r--r--Emby.Server.Implementations/Localization/Core/hu.json16
-rw-r--r--Emby.Server.Implementations/Localization/Core/id.json68
-rw-r--r--Emby.Server.Implementations/Localization/Core/is.json36
-rw-r--r--Emby.Server.Implementations/Localization/Core/it.json6
-rw-r--r--Emby.Server.Implementations/Localization/Core/lv.json96
-rw-r--r--Emby.Server.Implementations/Localization/Core/mk.json96
-rw-r--r--Emby.Server.Implementations/Localization/Core/ms.json38
-rw-r--r--Emby.Server.Implementations/Localization/Core/nb.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/nl.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/nn.json40
-rw-r--r--Emby.Server.Implementations/Localization/Core/pt.json50
-rw-r--r--Emby.Server.Implementations/Localization/Core/ro.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/sk.json36
-rw-r--r--Emby.Server.Implementations/Localization/Core/sl-SI.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/sr.json96
-rw-r--r--Emby.Server.Implementations/Localization/Core/sv.json32
-rw-r--r--Emby.Server.Implementations/Localization/Core/tr.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/zh-CN.json18
-rw-r--r--Emby.Server.Implementations/Localization/Core/zh-HK.json174
-rw-r--r--Emby.Server.Implementations/Localization/Core/zh-TW.json8
-rw-r--r--Emby.Server.Implementations/Net/SocketFactory.cs30
-rw-r--r--Emby.Server.Implementations/Net/UdpSocket.cs9
-rw-r--r--Emby.Server.Implementations/Net/WebSocketConnectEventArgs.cs2
-rw-r--r--Emby.Server.Implementations/Playlists/PlaylistImageProvider.cs1
-rw-r--r--Emby.Server.Implementations/Playlists/PlaylistManager.cs61
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs5
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs9
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs10
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs9
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs11
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs4
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs2
-rw-r--r--Emby.Server.Implementations/Serialization/JsonSerializer.cs32
-rw-r--r--Emby.Server.Implementations/Session/SessionManager.cs291
-rw-r--r--Emby.Server.Implementations/SocketSharp/HttpPostedFile.cs6
-rw-r--r--Emby.Server.Implementations/SocketSharp/WebSocketSharpListener.cs7
-rw-r--r--Emby.Server.Implementations/Sorting/AlphanumComparator.cs99
-rw-r--r--Emby.Server.Implementations/Udp/UdpServer.cs262
-rw-r--r--Emby.Server.Implementations/WebSockets/WebSocketManager.cs2
-rw-r--r--Jellyfin.Api/Controllers/StartupController.cs1
-rw-r--r--Jellyfin.Api/Jellyfin.Api.csproj6
-rw-r--r--Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj6
-rw-r--r--Jellyfin.Drawing.Skia/SkiaCodecException.cs1
-rw-r--r--Jellyfin.Drawing.Skia/SkiaEncoder.cs20
-rw-r--r--Jellyfin.Server/CoreAppHost.cs7
-rw-r--r--Jellyfin.Server/Jellyfin.Server.csproj20
-rw-r--r--Jellyfin.Server/Migrations/IMigrationRoutine.cs28
-rw-r--r--Jellyfin.Server/Migrations/MigrationOptions.cs24
-rw-r--r--Jellyfin.Server/Migrations/MigrationRunner.cs73
-rw-r--r--Jellyfin.Server/Migrations/MigrationsFactory.cs20
-rw-r--r--Jellyfin.Server/Migrations/MigrationsListStore.cs24
-rw-r--r--Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs73
-rw-r--r--Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs35
-rw-r--r--Jellyfin.Server/Program.cs115
-rw-r--r--Jellyfin.Server/Resources/Configuration/logging.json15
-rw-r--r--Jellyfin.Server/Startup.cs1
-rw-r--r--MediaBrowser.Api/ApiEntryPoint.cs3
-rw-r--r--MediaBrowser.Api/Attachments/AttachmentService.cs63
-rw-r--r--MediaBrowser.Api/EnvironmentService.cs1
-rw-r--r--MediaBrowser.Api/Images/ImageService.cs28
-rw-r--r--MediaBrowser.Api/Images/RemoteImageService.cs6
-rw-r--r--MediaBrowser.Api/ItemLookupService.cs2
-rw-r--r--MediaBrowser.Api/ItemUpdateService.cs1
-rw-r--r--MediaBrowser.Api/Library/LibraryService.cs6
-rw-r--r--MediaBrowser.Api/Playback/BaseStreamingService.cs28
-rw-r--r--MediaBrowser.Api/Playback/Hls/BaseHlsService.cs8
-rw-r--r--MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs10
-rw-r--r--MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs4
-rw-r--r--MediaBrowser.Api/Playback/MediaInfoService.cs233
-rw-r--r--MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs2
-rw-r--r--MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs12
-rw-r--r--MediaBrowser.Api/Playback/StreamRequest.cs1
-rw-r--r--MediaBrowser.Api/Playback/StreamState.cs2
-rw-r--r--MediaBrowser.Api/Playback/UniversalAudioService.cs9
-rw-r--r--MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs2
-rw-r--r--MediaBrowser.Api/Sessions/ApiKeyService.cs85
-rw-r--r--MediaBrowser.Api/Sessions/SessionInfoWebSocketListener.cs (renamed from MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs)4
-rw-r--r--MediaBrowser.Api/Sessions/SessionService.cs (renamed from MediaBrowser.Api/Session/SessionsService.cs)104
-rw-r--r--MediaBrowser.Api/System/ActivityLogWebSocketListener.cs3
-rw-r--r--MediaBrowser.Api/System/SystemService.cs4
-rw-r--r--MediaBrowser.Api/UserLibrary/GenresService.cs1
-rw-r--r--MediaBrowser.Api/UserLibrary/PersonsService.cs1
-rw-r--r--MediaBrowser.Api/UserLibrary/PlaystateService.cs4
-rw-r--r--MediaBrowser.Api/UserLibrary/YearsService.cs1
-rw-r--r--MediaBrowser.Api/UserService.cs6
-rw-r--r--MediaBrowser.Common/Configuration/IConfigurationFactory.cs27
-rw-r--r--MediaBrowser.Common/Cryptography/PasswordHash.cs24
-rw-r--r--MediaBrowser.Common/Events/EventHelper.cs8
-rw-r--r--MediaBrowser.Common/Extensions/BaseExtensions.cs7
-rw-r--r--MediaBrowser.Common/Extensions/CopyToExtensions.cs4
-rw-r--r--MediaBrowser.Common/Extensions/MethodNotAllowedException.cs26
-rw-r--r--MediaBrowser.Common/Extensions/RateLimitExceededException.cs25
-rw-r--r--MediaBrowser.Common/Extensions/ResourceNotFoundException.cs65
-rw-r--r--MediaBrowser.Common/Extensions/ShuffleExtensions.cs13
-rw-r--r--MediaBrowser.Common/Hex.cs12
-rw-r--r--MediaBrowser.Common/IApplicationHost.cs61
-rw-r--r--MediaBrowser.Common/Json/Converters/JsonGuidConverter.cs (renamed from MediaBrowser.Common/Json/Converters/GuidConverter.cs)2
-rw-r--r--MediaBrowser.Common/Json/Converters/JsonInt32Converter.cs53
-rw-r--r--MediaBrowser.Common/Json/JsonDefaults.cs2
-rw-r--r--MediaBrowser.Common/MediaBrowser.Common.csproj18
-rw-r--r--MediaBrowser.Common/Net/HttpRequestOptions.cs27
-rw-r--r--MediaBrowser.Common/Net/HttpResponseInfo.cs31
-rw-r--r--MediaBrowser.Common/Net/IHttpClient.cs4
-rw-r--r--MediaBrowser.Common/Net/INetworkManager.cs10
-rw-r--r--MediaBrowser.Common/Plugins/BasePlugin.cs150
-rw-r--r--MediaBrowser.Common/Plugins/IPlugin.cs28
-rw-r--r--MediaBrowser.Common/Plugins/IPluginAssembly.cs13
-rw-r--r--MediaBrowser.Common/Updates/IInstallationManager.cs9
-rw-r--r--MediaBrowser.Controller/Authentication/AuthenticationResult.cs6
-rw-r--r--MediaBrowser.Controller/Channels/IChannel.cs2
-rw-r--r--MediaBrowser.Controller/Drawing/IImageProcessor.cs35
-rw-r--r--MediaBrowser.Controller/Drawing/ImageHelper.cs2
-rw-r--r--MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs3
-rw-r--r--MediaBrowser.Controller/Entities/Audio/MusicArtist.cs1
-rw-r--r--MediaBrowser.Controller/Entities/BaseItem.cs49
-rw-r--r--MediaBrowser.Controller/Entities/Book.cs4
-rw-r--r--MediaBrowser.Controller/Entities/Folder.cs53
-rw-r--r--MediaBrowser.Controller/Entities/Person.cs53
-rw-r--r--MediaBrowser.Controller/Entities/PersonInfo.cs63
-rw-r--r--MediaBrowser.Controller/Entities/Photo.cs8
-rw-r--r--MediaBrowser.Controller/Entities/UserRootFolder.cs9
-rw-r--r--MediaBrowser.Controller/Entities/Video.cs1
-rw-r--r--MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs25
-rw-r--r--MediaBrowser.Controller/IServerApplicationHost.cs8
-rw-r--r--MediaBrowser.Controller/Library/ILibraryManager.cs6
-rw-r--r--MediaBrowser.Controller/Library/IMediaSourceManager.cs16
-rw-r--r--MediaBrowser.Controller/Library/IMediaSourceProvider.cs3
-rw-r--r--MediaBrowser.Controller/Library/Profiler.cs2
-rw-r--r--MediaBrowser.Controller/MediaBrowser.Controller.csproj15
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs52
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs14
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs1
-rw-r--r--MediaBrowser.Controller/MediaEncoding/IAttachmentExtractor.cs17
-rw-r--r--MediaBrowser.Controller/MediaEncoding/JobLogger.cs12
-rw-r--r--MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs22
-rw-r--r--MediaBrowser.Controller/Net/IAuthService.cs4
-rw-r--r--MediaBrowser.Controller/Net/IHttpResultFactory.cs4
-rw-r--r--MediaBrowser.Controller/Net/IWebSocketConnection.cs1
-rw-r--r--MediaBrowser.Controller/Net/StaticResultOptions.cs6
-rw-r--r--MediaBrowser.Controller/Persistence/IItemRepository.cs15
-rw-r--r--MediaBrowser.Controller/Persistence/MediaAttachmentQuery.cs19
-rw-r--r--MediaBrowser.Controller/Playlists/IPlaylistManager.cs2
-rw-r--r--MediaBrowser.Controller/Plugins/IServerEntryPoint.cs10
-rw-r--r--MediaBrowser.Controller/Providers/AlbumInfo.cs1
-rw-r--r--MediaBrowser.Controller/Providers/BoxSetInfo.cs1
-rw-r--r--MediaBrowser.Controller/Providers/DirectoryService.cs3
-rw-r--r--MediaBrowser.Controller/Providers/DynamicImageInfo.cs10
-rw-r--r--MediaBrowser.Controller/Providers/DynamicImageResponse.cs4
-rw-r--r--MediaBrowser.Controller/Providers/EpisodeInfo.cs1
-rw-r--r--MediaBrowser.Controller/Providers/ExtraInfo.cs15
-rw-r--r--MediaBrowser.Controller/Providers/ExtraSource.cs9
-rw-r--r--MediaBrowser.Controller/Providers/ICustomMetadataProvider.cs2
-rw-r--r--MediaBrowser.Controller/Providers/IDirectoryService.cs3
-rw-r--r--MediaBrowser.Controller/Providers/IExtrasProvider.cs20
-rw-r--r--MediaBrowser.Controller/Providers/IForcedProvider.cs2
-rw-r--r--MediaBrowser.Controller/Providers/IImageEnhancer.cs61
-rw-r--r--MediaBrowser.Controller/Providers/IImageProvider.cs6
-rw-r--r--MediaBrowser.Controller/Providers/ILocalImageFileProvider.cs10
-rw-r--r--MediaBrowser.Controller/Providers/ILocalImageProvider.cs6
-rw-r--r--MediaBrowser.Controller/Providers/ILocalMetadataProvider.cs5
-rw-r--r--MediaBrowser.Controller/Providers/IMetadataService.cs3
-rw-r--r--MediaBrowser.Controller/Providers/IPreRefreshProvider.cs1
-rw-r--r--MediaBrowser.Controller/Providers/IProviderManager.cs6
-rw-r--r--MediaBrowser.Controller/Providers/IRemoteImageProvider.cs2
-rw-r--r--MediaBrowser.Controller/Providers/ItemInfo.cs5
-rw-r--r--MediaBrowser.Controller/Providers/ItemLookupInfo.cs8
-rw-r--r--MediaBrowser.Controller/Providers/LocalImageInfo.cs1
-rw-r--r--MediaBrowser.Controller/Providers/MetadataProviderPriority.cs39
-rw-r--r--MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs2
-rw-r--r--MediaBrowser.Controller/Providers/MetadataResult.cs6
-rw-r--r--MediaBrowser.Controller/Providers/MovieInfo.cs1
-rw-r--r--MediaBrowser.Controller/Providers/PersonLookupInfo.cs1
-rw-r--r--MediaBrowser.Controller/Providers/RemoteSearchQuery.cs6
-rw-r--r--MediaBrowser.Controller/Session/SessionInfo.cs17
-rw-r--r--MediaBrowser.Controller/Sorting/AlphanumComparator.cs135
-rw-r--r--MediaBrowser.Controller/Sorting/SortExtensions.cs122
-rw-r--r--MediaBrowser.Controller/Sorting/SortHelper.cs25
-rw-r--r--MediaBrowser.LocalMetadata/Images/CollectionFolderImageProvider.cs2
-rw-r--r--MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs2
-rw-r--r--MediaBrowser.LocalMetadata/Images/InternalMetadataFolderImageProvider.cs2
-rw-r--r--MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs4
-rw-r--r--MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs1
-rw-r--r--MediaBrowser.LocalMetadata/Providers/BoxSetXmlProvider.cs2
-rw-r--r--MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs5
-rw-r--r--MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs3
-rw-r--r--MediaBrowser.LocalMetadata/Savers/BoxSetXmlSaver.cs2
-rw-r--r--MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs9
-rw-r--r--MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs281
-rw-r--r--MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs38
-rw-r--r--MediaBrowser.MediaEncoding/BdInfo/BdInfoFileInfo.cs14
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs4
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs110
-rw-r--r--MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj4
-rw-r--r--MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs21
-rw-r--r--MediaBrowser.MediaEncoding/Probing/InternalMediaInfoResult.cs325
-rw-r--r--MediaBrowser.MediaEncoding/Probing/MediaChapter.cs32
-rw-r--r--MediaBrowser.MediaEncoding/Probing/MediaFormatInfo.cs81
-rw-r--r--MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs282
-rw-r--r--MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs234
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/AssParser.cs1
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/JsonWriter.cs4
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs1
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs1
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs15
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/TtmlWriter.cs1
-rw-r--r--MediaBrowser.MediaEncoding/packages.config3
-rw-r--r--MediaBrowser.Model/Activity/ActivityLogEntry.cs2
-rw-r--r--MediaBrowser.Model/Activity/IActivityManager.cs2
-rw-r--r--MediaBrowser.Model/Activity/IActivityRepository.cs2
-rw-r--r--MediaBrowser.Model/ApiClient/ServerDiscoveryInfo.cs5
-rw-r--r--MediaBrowser.Model/Branding/BrandingOptions.cs3
-rw-r--r--MediaBrowser.Model/Channels/ChannelFeatures.cs2
-rw-r--r--MediaBrowser.Model/Channels/ChannelFolderType.cs2
-rw-r--r--MediaBrowser.Model/Channels/ChannelInfo.cs2
-rw-r--r--MediaBrowser.Model/Channels/ChannelItemSortField.cs2
-rw-r--r--MediaBrowser.Model/Channels/ChannelMediaContentType.cs2
-rw-r--r--MediaBrowser.Model/Channels/ChannelMediaType.cs2
-rw-r--r--MediaBrowser.Model/Channels/ChannelQuery.cs2
-rw-r--r--MediaBrowser.Model/Collections/CollectionCreationResult.cs2
-rw-r--r--MediaBrowser.Model/Configuration/AccessSchedule.cs4
-rw-r--r--MediaBrowser.Model/Configuration/BaseApplicationConfiguration.cs21
-rw-r--r--MediaBrowser.Model/Configuration/DynamicDayOfWeek.cs2
-rw-r--r--MediaBrowser.Model/Configuration/EncodingOptions.cs10
-rw-r--r--MediaBrowser.Model/Configuration/ImageOption.cs3
-rw-r--r--MediaBrowser.Model/Configuration/ImageSavingConvention.cs2
-rw-r--r--MediaBrowser.Model/Configuration/LibraryOptions.cs3
-rw-r--r--MediaBrowser.Model/Configuration/MetadataConfiguration.cs2
-rw-r--r--MediaBrowser.Model/Configuration/MetadataOptions.cs2
-rw-r--r--MediaBrowser.Model/Configuration/MetadataPlugin.cs2
-rw-r--r--MediaBrowser.Model/Configuration/MetadataPluginSummary.cs2
-rw-r--r--MediaBrowser.Model/Configuration/MetadataPluginType.cs2
-rw-r--r--MediaBrowser.Model/Configuration/ServerConfiguration.cs9
-rw-r--r--MediaBrowser.Model/Configuration/SubtitlePlaybackMode.cs2
-rw-r--r--MediaBrowser.Model/Configuration/UnratedItem.cs2
-rw-r--r--MediaBrowser.Model/Configuration/UserConfiguration.cs2
-rw-r--r--MediaBrowser.Model/Configuration/XbmcMetadataOptions.cs2
-rw-r--r--MediaBrowser.Model/Cryptography/ICryptoProvider.cs2
-rw-r--r--MediaBrowser.Model/Devices/ContentUploadHistory.cs2
-rw-r--r--MediaBrowser.Model/Devices/DeviceInfo.cs18
-rw-r--r--MediaBrowser.Model/Devices/DeviceQuery.cs3
-rw-r--r--MediaBrowser.Model/Devices/DevicesOptions.cs2
-rw-r--r--MediaBrowser.Model/Devices/LocalFileInfo.cs2
-rw-r--r--MediaBrowser.Model/Diagnostics/IProcess.cs2
-rw-r--r--MediaBrowser.Model/Diagnostics/IProcessFactory.cs2
-rw-r--r--MediaBrowser.Model/Dlna/AudioOptions.cs2
-rw-r--r--MediaBrowser.Model/Dlna/CodecProfile.cs7
-rw-r--r--MediaBrowser.Model/Dlna/CodecType.cs2
-rw-r--r--MediaBrowser.Model/Dlna/ConditionProcessor.cs6
-rw-r--r--MediaBrowser.Model/Dlna/ContainerProfile.cs2
-rw-r--r--MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs2
-rw-r--r--MediaBrowser.Model/Dlna/DeviceIdentification.cs15
-rw-r--r--MediaBrowser.Model/Dlna/DeviceProfile.cs8
-rw-r--r--MediaBrowser.Model/Dlna/DeviceProfileInfo.cs2
-rw-r--r--MediaBrowser.Model/Dlna/DeviceProfileType.cs2
-rw-r--r--MediaBrowser.Model/Dlna/DirectPlayProfile.cs2
-rw-r--r--MediaBrowser.Model/Dlna/DlnaFlags.cs2
-rw-r--r--MediaBrowser.Model/Dlna/DlnaMaps.cs4
-rw-r--r--MediaBrowser.Model/Dlna/DlnaProfileType.cs2
-rw-r--r--MediaBrowser.Model/Dlna/EncodingContext.cs2
-rw-r--r--MediaBrowser.Model/Dlna/HeaderMatchType.cs2
-rw-r--r--MediaBrowser.Model/Dlna/HttpHeaderInfo.cs2
-rw-r--r--MediaBrowser.Model/Dlna/IDeviceDiscovery.cs2
-rw-r--r--MediaBrowser.Model/Dlna/ITranscoderSupport.cs2
-rw-r--r--MediaBrowser.Model/Dlna/MediaFormatProfile.cs2
-rw-r--r--MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs147
-rw-r--r--MediaBrowser.Model/Dlna/PlaybackErrorCode.cs2
-rw-r--r--MediaBrowser.Model/Dlna/ProfileCondition.cs2
-rw-r--r--MediaBrowser.Model/Dlna/ProfileConditionType.cs2
-rw-r--r--MediaBrowser.Model/Dlna/ProfileConditionValue.cs2
-rw-r--r--MediaBrowser.Model/Dlna/ResolutionConfiguration.cs2
-rw-r--r--MediaBrowser.Model/Dlna/ResolutionNormalizer.cs8
-rw-r--r--MediaBrowser.Model/Dlna/ResolutionOptions.cs2
-rw-r--r--MediaBrowser.Model/Dlna/ResponseProfile.cs2
-rw-r--r--MediaBrowser.Model/Dlna/SearchCriteria.cs14
-rw-r--r--MediaBrowser.Model/Dlna/SearchType.cs2
-rw-r--r--MediaBrowser.Model/Dlna/SortCriteria.cs2
-rw-r--r--MediaBrowser.Model/Dlna/StreamBuilder.cs15
-rw-r--r--MediaBrowser.Model/Dlna/StreamInfo.cs31
-rw-r--r--MediaBrowser.Model/Dlna/SubtitleDeliveryMethod.cs5
-rw-r--r--MediaBrowser.Model/Dlna/SubtitleProfile.cs2
-rw-r--r--MediaBrowser.Model/Dlna/SubtitleStreamInfo.cs2
-rw-r--r--MediaBrowser.Model/Dlna/TranscodeSeekInfo.cs2
-rw-r--r--MediaBrowser.Model/Dlna/TranscodingProfile.cs2
-rw-r--r--MediaBrowser.Model/Dlna/UpnpDeviceInfo.cs2
-rw-r--r--MediaBrowser.Model/Dlna/VideoOptions.cs2
-rw-r--r--MediaBrowser.Model/Dlna/XmlAttribute.cs2
-rw-r--r--MediaBrowser.Model/Drawing/DrawingUtils.cs16
-rw-r--r--MediaBrowser.Model/Drawing/ImageDimensions.cs35
-rw-r--r--MediaBrowser.Model/Drawing/ImageFormat.cs16
-rw-r--r--MediaBrowser.Model/Drawing/ImageOrientation.cs2
-rw-r--r--MediaBrowser.Model/Dto/BaseItemDto.cs2
-rw-r--r--MediaBrowser.Model/Dto/BaseItemPerson.cs2
-rw-r--r--MediaBrowser.Model/Dto/IHasServerId.cs2
-rw-r--r--MediaBrowser.Model/Dto/IItemDto.cs2
-rw-r--r--MediaBrowser.Model/Dto/ImageByNameInfo.cs5
-rw-r--r--MediaBrowser.Model/Dto/ImageInfo.cs2
-rw-r--r--MediaBrowser.Model/Dto/ImageOptions.cs2
-rw-r--r--MediaBrowser.Model/Dto/ItemCounts.cs25
-rw-r--r--MediaBrowser.Model/Dto/ItemIndex.cs2
-rw-r--r--MediaBrowser.Model/Dto/MediaSourceInfo.cs10
-rw-r--r--MediaBrowser.Model/Dto/MediaSourceType.cs2
-rw-r--r--MediaBrowser.Model/Dto/MetadataEditorInfo.cs2
-rw-r--r--MediaBrowser.Model/Dto/NameIdPair.cs3
-rw-r--r--MediaBrowser.Model/Dto/NameValuePair.cs3
-rw-r--r--MediaBrowser.Model/Dto/RatingType.cs2
-rw-r--r--MediaBrowser.Model/Dto/RecommendationDto.cs2
-rw-r--r--MediaBrowser.Model/Dto/RecommendationType.cs2
-rw-r--r--MediaBrowser.Model/Dto/UserDto.cs6
-rw-r--r--MediaBrowser.Model/Dto/UserItemDataDto.cs2
-rw-r--r--MediaBrowser.Model/Entities/ChapterInfo.cs4
-rw-r--r--MediaBrowser.Model/Entities/CollectionType.cs2
-rw-r--r--MediaBrowser.Model/Entities/DisplayPreferences.cs17
-rw-r--r--MediaBrowser.Model/Entities/EmptyRequestResult.cs6
-rw-r--r--MediaBrowser.Model/Entities/ExtraType.cs2
-rw-r--r--MediaBrowser.Model/Entities/IHasProviderIds.cs2
-rw-r--r--MediaBrowser.Model/Entities/ImageType.cs37
-rw-r--r--MediaBrowser.Model/Entities/IsoType.cs7
-rw-r--r--MediaBrowser.Model/Entities/LibraryUpdateInfo.cs2
-rw-r--r--MediaBrowser.Model/Entities/LocationType.cs13
-rw-r--r--MediaBrowser.Model/Entities/MediaAttachment.cs50
-rw-r--r--MediaBrowser.Model/Entities/MediaStream.cs18
-rw-r--r--MediaBrowser.Model/Entities/MediaStreamType.cs13
-rw-r--r--MediaBrowser.Model/Entities/MediaType.cs15
-rw-r--r--MediaBrowser.Model/Entities/MediaUrl.cs2
-rw-r--r--MediaBrowser.Model/Entities/MetadataFields.cs28
-rw-r--r--MediaBrowser.Model/Entities/MetadataProviders.cs2
-rw-r--r--MediaBrowser.Model/Entities/PackageReviewInfo.cs2
-rw-r--r--MediaBrowser.Model/Entities/ParentalRating.cs2
-rw-r--r--MediaBrowser.Model/Entities/PersonType.cs25
-rw-r--r--MediaBrowser.Model/Entities/ProviderIdsExtensions.cs2
-rw-r--r--MediaBrowser.Model/Entities/ScrollDirection.cs7
-rw-r--r--MediaBrowser.Model/Entities/SeriesStatus.cs7
-rw-r--r--MediaBrowser.Model/Entities/SortOrder.cs7
-rw-r--r--MediaBrowser.Model/Entities/TrailerType.cs2
-rw-r--r--MediaBrowser.Model/Entities/UserDataSaveReason.cs19
-rw-r--r--MediaBrowser.Model/Entities/Video3DFormat.cs2
-rw-r--r--MediaBrowser.Model/Entities/VideoType.cs13
-rw-r--r--MediaBrowser.Model/Entities/VirtualFolderInfo.cs2
-rw-r--r--MediaBrowser.Model/Events/GenericEventArgs.cs2
-rw-r--r--MediaBrowser.Model/Extensions/ListHelper.cs3
-rw-r--r--MediaBrowser.Model/Extensions/StringHelper.cs63
-rw-r--r--MediaBrowser.Model/Globalization/CountryInfo.cs2
-rw-r--r--MediaBrowser.Model/Globalization/CultureDto.cs6
-rw-r--r--MediaBrowser.Model/Globalization/ILocalizationManager.cs2
-rw-r--r--MediaBrowser.Model/Globalization/LocalizationOption.cs2
-rw-r--r--MediaBrowser.Model/IO/FileSystemEntryType.cs13
-rw-r--r--MediaBrowser.Model/IO/FileSystemMetadata.cs9
-rw-r--r--MediaBrowser.Model/IO/IFileSystem.cs143
-rw-r--r--MediaBrowser.Model/IO/IIsoManager.cs2
-rw-r--r--MediaBrowser.Model/IO/IIsoMount.cs2
-rw-r--r--MediaBrowser.Model/IO/IIsoMounter.cs2
-rw-r--r--MediaBrowser.Model/IO/IODefaults.cs (renamed from MediaBrowser.Model/IO/StreamDefaults.cs)8
-rw-r--r--MediaBrowser.Model/IO/IShortcutHandler.cs4
-rw-r--r--MediaBrowser.Model/IO/IStreamHelper.cs2
-rw-r--r--MediaBrowser.Model/IO/IZipClient.cs2
-rw-r--r--MediaBrowser.Model/Library/PlayAccess.cs2
-rw-r--r--MediaBrowser.Model/Library/UserViewQuery.cs2
-rw-r--r--MediaBrowser.Model/LiveTv/BaseTimerInfoDto.cs2
-rw-r--r--MediaBrowser.Model/LiveTv/ChannelType.cs4
-rw-r--r--MediaBrowser.Model/LiveTv/DayPattern.cs2
-rw-r--r--MediaBrowser.Model/LiveTv/GuideInfo.cs2
-rw-r--r--MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs2
-rw-r--r--MediaBrowser.Model/LiveTv/LiveTvInfo.cs2
-rw-r--r--MediaBrowser.Model/LiveTv/LiveTvOptions.cs2
-rw-r--r--MediaBrowser.Model/LiveTv/LiveTvServiceInfo.cs5
-rw-r--r--MediaBrowser.Model/LiveTv/LiveTvServiceStatus.cs2
-rw-r--r--MediaBrowser.Model/LiveTv/LiveTvTunerStatus.cs2
-rw-r--r--MediaBrowser.Model/LiveTv/ProgramAudio.cs2
-rw-r--r--MediaBrowser.Model/LiveTv/RecordingQuery.cs2
-rw-r--r--MediaBrowser.Model/LiveTv/RecordingStatus.cs2
-rw-r--r--MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs2
-rw-r--r--MediaBrowser.Model/LiveTv/SeriesTimerQuery.cs2
-rw-r--r--MediaBrowser.Model/LiveTv/TimerInfoDto.cs2
-rw-r--r--MediaBrowser.Model/LiveTv/TimerQuery.cs2
-rw-r--r--MediaBrowser.Model/MediaBrowser.Model.csproj19
-rw-r--r--MediaBrowser.Model/MediaInfo/AudioCodec.cs4
-rw-r--r--MediaBrowser.Model/MediaInfo/BlurayDiscInfo.cs4
-rw-r--r--MediaBrowser.Model/MediaInfo/Container.cs8
-rw-r--r--MediaBrowser.Model/MediaInfo/IBlurayExaminer.cs2
-rw-r--r--MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs2
-rw-r--r--MediaBrowser.Model/MediaInfo/LiveStreamResponse.cs2
-rw-r--r--MediaBrowser.Model/MediaInfo/MediaInfo.cs9
-rw-r--r--MediaBrowser.Model/MediaInfo/MediaProtocol.cs2
-rw-r--r--MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs2
-rw-r--r--MediaBrowser.Model/MediaInfo/PlaybackInfoResponse.cs12
-rw-r--r--MediaBrowser.Model/MediaInfo/SubtitleFormat.cs4
-rw-r--r--MediaBrowser.Model/MediaInfo/SubtitleTrackEvent.cs2
-rw-r--r--MediaBrowser.Model/MediaInfo/SubtitleTrackInfo.cs2
-rw-r--r--MediaBrowser.Model/MediaInfo/TransportStreamTimestamp.cs2
-rw-r--r--MediaBrowser.Model/MediaInfo/VideoCodec.cs14
-rw-r--r--MediaBrowser.Model/Net/EndPointInfo.cs2
-rw-r--r--MediaBrowser.Model/Net/HttpException.cs2
-rw-r--r--MediaBrowser.Model/Net/ISocket.cs4
-rw-r--r--MediaBrowser.Model/Net/ISocketFactory.cs12
-rw-r--r--MediaBrowser.Model/Net/MimeTypes.cs14
-rw-r--r--MediaBrowser.Model/Net/NetworkShare.cs2
-rw-r--r--MediaBrowser.Model/Net/NetworkShareType.cs16
-rw-r--r--MediaBrowser.Model/Net/SocketReceiveResult.cs2
-rw-r--r--MediaBrowser.Model/Net/WebSocketMessage.cs5
-rw-r--r--MediaBrowser.Model/Notifications/NotificationLevel.cs2
-rw-r--r--MediaBrowser.Model/Notifications/NotificationOption.cs2
-rw-r--r--MediaBrowser.Model/Notifications/NotificationOptions.cs4
-rw-r--r--MediaBrowser.Model/Notifications/NotificationRequest.cs2
-rw-r--r--MediaBrowser.Model/Notifications/NotificationType.cs2
-rw-r--r--MediaBrowser.Model/Notifications/NotificationTypeInfo.cs2
-rw-r--r--MediaBrowser.Model/Notifications/SendToUserType.cs2
-rw-r--r--MediaBrowser.Model/Playlists/PlaylistCreationRequest.cs2
-rw-r--r--MediaBrowser.Model/Playlists/PlaylistCreationResult.cs2
-rw-r--r--MediaBrowser.Model/Playlists/PlaylistItemQuery.cs2
-rw-r--r--MediaBrowser.Model/Plugins/IHasWebPages.cs2
-rw-r--r--MediaBrowser.Model/Plugins/PluginPageInfo.cs2
-rw-r--r--MediaBrowser.Model/Providers/ExternalIdInfo.cs2
-rw-r--r--MediaBrowser.Model/Providers/ExternalUrl.cs2
-rw-r--r--MediaBrowser.Model/Providers/ImageProviderInfo.cs5
-rw-r--r--MediaBrowser.Model/Providers/RemoteImageInfo.cs4
-rw-r--r--MediaBrowser.Model/Providers/RemoteImageQuery.cs2
-rw-r--r--MediaBrowser.Model/Providers/RemoteSearchResult.cs2
-rw-r--r--MediaBrowser.Model/Providers/RemoteSubtitleInfo.cs2
-rw-r--r--MediaBrowser.Model/Providers/SubtitleOptions.cs2
-rw-r--r--MediaBrowser.Model/Providers/SubtitleProviderInfo.cs2
-rw-r--r--MediaBrowser.Model/Querying/AllThemeMediaResult.cs2
-rw-r--r--MediaBrowser.Model/Querying/EpisodeQuery.cs15
-rw-r--r--MediaBrowser.Model/Querying/ItemCountsQuery.cs2
-rw-r--r--MediaBrowser.Model/Querying/ItemFields.cs4
-rw-r--r--MediaBrowser.Model/Querying/ItemFilter.cs28
-rw-r--r--MediaBrowser.Model/Querying/ItemSortBy.cs2
-rw-r--r--MediaBrowser.Model/Querying/LatestItemsQuery.cs2
-rw-r--r--MediaBrowser.Model/Querying/MovieRecommendationQuery.cs10
-rw-r--r--MediaBrowser.Model/Querying/NextUpQuery.cs7
-rw-r--r--MediaBrowser.Model/Querying/QueryFilters.cs2
-rw-r--r--MediaBrowser.Model/Querying/QueryResult.cs8
-rw-r--r--MediaBrowser.Model/Querying/SessionQuery.cs13
-rw-r--r--MediaBrowser.Model/Querying/SimilarItemsQuery.cs29
-rw-r--r--MediaBrowser.Model/Querying/UpcomingEpisodesQuery.cs2
-rw-r--r--MediaBrowser.Model/Querying/UserQuery.cs8
-rw-r--r--MediaBrowser.Model/Search/SearchHint.cs4
-rw-r--r--MediaBrowser.Model/Search/SearchHintResult.cs2
-rw-r--r--MediaBrowser.Model/Search/SearchQuery.cs2
-rw-r--r--MediaBrowser.Model/Serialization/IJsonSerializer.cs2
-rw-r--r--MediaBrowser.Model/Serialization/IXmlSerializer.cs2
-rw-r--r--MediaBrowser.Model/Services/ApiMemberAttribute.cs3
-rw-r--r--MediaBrowser.Model/Services/IAsyncStreamWriter.cs2
-rw-r--r--MediaBrowser.Model/Services/IHasHeaders.cs2
-rw-r--r--MediaBrowser.Model/Services/IHasRequestFilter.cs2
-rw-r--r--MediaBrowser.Model/Services/IHttpRequest.cs2
-rw-r--r--MediaBrowser.Model/Services/IHttpResult.cs2
-rw-r--r--MediaBrowser.Model/Services/IRequest.cs2
-rw-r--r--MediaBrowser.Model/Services/IRequiresRequestStream.cs2
-rw-r--r--MediaBrowser.Model/Services/IService.cs2
-rw-r--r--MediaBrowser.Model/Services/IStreamWriter.cs2
-rw-r--r--MediaBrowser.Model/Services/QueryParamCollection.cs2
-rw-r--r--MediaBrowser.Model/Services/RouteAttribute.cs2
-rw-r--r--MediaBrowser.Model/Session/BrowseRequest.cs4
-rw-r--r--MediaBrowser.Model/Session/ClientCapabilities.cs2
-rw-r--r--MediaBrowser.Model/Session/GeneralCommand.cs2
-rw-r--r--MediaBrowser.Model/Session/GeneralCommandType.cs2
-rw-r--r--MediaBrowser.Model/Session/MessageCommand.cs2
-rw-r--r--MediaBrowser.Model/Session/PlayCommand.cs16
-rw-r--r--MediaBrowser.Model/Session/PlayMethod.cs2
-rw-r--r--MediaBrowser.Model/Session/PlayRequest.cs2
-rw-r--r--MediaBrowser.Model/Session/PlaybackProgressInfo.cs2
-rw-r--r--MediaBrowser.Model/Session/PlaybackStopInfo.cs9
-rw-r--r--MediaBrowser.Model/Session/PlayerStateInfo.cs2
-rw-r--r--MediaBrowser.Model/Session/PlaystateCommand.cs27
-rw-r--r--MediaBrowser.Model/Session/PlaystateRequest.cs2
-rw-r--r--MediaBrowser.Model/Session/TranscodingInfo.cs2
-rw-r--r--MediaBrowser.Model/Session/UserDataChangeInfo.cs2
-rw-r--r--MediaBrowser.Model/Sync/SyncCategory.cs2
-rw-r--r--MediaBrowser.Model/Sync/SyncJob.cs20
-rw-r--r--MediaBrowser.Model/Sync/SyncJobStatus.cs2
-rw-r--r--MediaBrowser.Model/Sync/SyncTarget.cs3
-rw-r--r--MediaBrowser.Model/System/LogFile.cs2
-rw-r--r--MediaBrowser.Model/System/OperatingSystemId.cs2
-rw-r--r--MediaBrowser.Model/System/PublicSystemInfo.cs2
-rw-r--r--MediaBrowser.Model/System/SystemInfo.cs2
-rw-r--r--MediaBrowser.Model/Tasks/IConfigurableScheduledTask.cs2
-rw-r--r--MediaBrowser.Model/Tasks/IScheduledTask.cs6
-rw-r--r--MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs2
-rw-r--r--MediaBrowser.Model/Tasks/ITaskManager.cs2
-rw-r--r--MediaBrowser.Model/Tasks/ITaskTrigger.cs8
-rw-r--r--MediaBrowser.Model/Tasks/ScheduledTaskHelpers.cs2
-rw-r--r--MediaBrowser.Model/Tasks/TaskCompletionEventArgs.cs2
-rw-r--r--MediaBrowser.Model/Tasks/TaskCompletionStatus.cs10
-rw-r--r--MediaBrowser.Model/Tasks/TaskInfo.cs6
-rw-r--r--MediaBrowser.Model/Tasks/TaskOptions.cs2
-rw-r--r--MediaBrowser.Model/Tasks/TaskResult.cs2
-rw-r--r--MediaBrowser.Model/Tasks/TaskState.cs10
-rw-r--r--MediaBrowser.Model/Tasks/TaskTriggerInfo.cs4
-rw-r--r--MediaBrowser.Model/Updates/CheckForUpdateResult.cs2
-rw-r--r--MediaBrowser.Model/Updates/InstallationInfo.cs2
-rw-r--r--MediaBrowser.Model/Updates/PackageInfo.cs4
-rw-r--r--MediaBrowser.Model/Updates/PackageTargetSystem.cs10
-rw-r--r--MediaBrowser.Model/Updates/PackageVersionClass.cs10
-rw-r--r--MediaBrowser.Model/Updates/PackageVersionInfo.cs4
-rw-r--r--MediaBrowser.Model/Users/ForgotPasswordAction.cs2
-rw-r--r--MediaBrowser.Model/Users/ForgotPasswordResult.cs4
-rw-r--r--MediaBrowser.Model/Users/PinRedeemResult.cs3
-rw-r--r--MediaBrowser.Model/Users/UserAction.cs2
-rw-r--r--MediaBrowser.Model/Users/UserActionType.cs2
-rw-r--r--MediaBrowser.Model/Users/UserPolicy.cs7
-rw-r--r--MediaBrowser.Providers/Books/AudioBookMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Books/BookMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Channels/ChannelMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Folders/FolderMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Folders/UserViewMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Genres/GenreMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/LiveTv/ProgramMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Manager/ImageSaver.cs4
-rw-r--r--MediaBrowser.Providers/Manager/ItemImageProvider.cs9
-rw-r--r--MediaBrowser.Providers/Manager/MetadataService.cs3
-rw-r--r--MediaBrowser.Providers/Manager/ProviderManager.cs2
-rw-r--r--MediaBrowser.Providers/MediaBrowser.Providers.csproj16
-rw-r--r--MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs19
-rw-r--r--MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs10
-rw-r--r--MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs1
-rw-r--r--MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs8
-rw-r--r--MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs2
-rw-r--r--MediaBrowser.Providers/Movies/MovieMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Movies/TrailerMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Music/AlbumMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Music/ArtistMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Music/AudioMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Music/MusicExternalIds.cs96
-rw-r--r--MediaBrowser.Providers/Music/MusicVideoMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/MusicGenres/MusicGenreMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/People/PersonMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Photos/PhotoAlbumMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Photos/PhotoMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Playlists/PlaylistItemsProvider.cs2
-rw-r--r--MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs (renamed from MediaBrowser.Providers/Music/AudioDbAlbumImageProvider.cs)5
-rw-r--r--MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs (renamed from MediaBrowser.Providers/Music/AudioDbAlbumProvider.cs)20
-rw-r--r--MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs (renamed from MediaBrowser.Providers/Music/AudioDbArtistImageProvider.cs)5
-rw-r--r--MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs (renamed from MediaBrowser.Providers/Music/AudioDbArtistProvider.cs)14
-rw-r--r--MediaBrowser.Providers/Plugins/AudioDb/Configuration/PluginConfiguration.cs11
-rw-r--r--MediaBrowser.Providers/Plugins/AudioDb/Configuration/config.html57
-rw-r--r--MediaBrowser.Providers/Plugins/AudioDb/ExternalIds.cs (renamed from MediaBrowser.Providers/Music/AudioDbExternalIds.cs)8
-rw-r--r--MediaBrowser.Providers/Plugins/AudioDb/Plugin.cs35
-rw-r--r--MediaBrowser.Providers/Plugins/MusicBrainz/AlbumProvider.cs (renamed from MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs)45
-rw-r--r--MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs (renamed from MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs)19
-rw-r--r--MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/PluginConfiguration.cs44
-rw-r--r--MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/config.html69
-rw-r--r--MediaBrowser.Providers/Plugins/MusicBrainz/ExternalIds.cs98
-rw-r--r--MediaBrowser.Providers/Plugins/MusicBrainz/Plugin.cs39
-rw-r--r--MediaBrowser.Providers/Plugins/Omdb/OmdbEpisodeProvider.cs (renamed from MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs)3
-rw-r--r--MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs (renamed from MediaBrowser.Providers/Omdb/OmdbImageProvider.cs)2
-rw-r--r--MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs (renamed from MediaBrowser.Providers/Omdb/OmdbItemProvider.cs)2
-rw-r--r--MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs (renamed from MediaBrowser.Providers/Omdb/OmdbProvider.cs)6
-rw-r--r--MediaBrowser.Providers/Plugins/TheTvdb/TvdbClientManager.cs (renamed from MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs)6
-rw-r--r--MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeImageProvider.cs (renamed from MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs)12
-rw-r--r--MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeProvider.cs (renamed from MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs)12
-rw-r--r--MediaBrowser.Providers/Plugins/TheTvdb/TvdbPersonImageProvider.cs (renamed from MediaBrowser.Providers/People/TvdbPersonImageProvider.cs)11
-rw-r--r--MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeasonImageProvider.cs (renamed from MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs)12
-rw-r--r--MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesImageProvider.cs (renamed from MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs)12
-rw-r--r--MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesProvider.cs (renamed from MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs)25
-rw-r--r--MediaBrowser.Providers/Plugins/TheTvdb/TvdbUtils.cs (renamed from MediaBrowser.Providers/TV/TheTVDB/TvdbUtils.cs)3
-rw-r--r--MediaBrowser.Providers/Studios/StudioMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Studios/StudiosImageProvider.cs6
-rw-r--r--MediaBrowser.Providers/Subtitles/SubtitleManager.cs4
-rw-r--r--MediaBrowser.Providers/TV/DummySeasonProvider.cs1
-rw-r--r--MediaBrowser.Providers/TV/EpisodeMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/TV/MissingEpisodeProvider.cs10
-rw-r--r--MediaBrowser.Providers/TV/SeasonMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/TV/SeriesMetadataService.cs12
-rw-r--r--MediaBrowser.Providers/TV/TvExternalIds.cs2
-rw-r--r--MediaBrowser.Providers/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs1
-rw-r--r--MediaBrowser.Providers/Tmdb/BoxSets/TmdbBoxSetProvider.cs9
-rw-r--r--MediaBrowser.Providers/Tmdb/Models/Search/ExternalIdLookupResult.cs1
-rw-r--r--MediaBrowser.Providers/Tmdb/Movies/GenericTmdbMovieInfo.cs2
-rw-r--r--MediaBrowser.Providers/Tmdb/Movies/TmdbImageProvider.cs1
-rw-r--r--MediaBrowser.Providers/Tmdb/Movies/TmdbMovieProvider.cs11
-rw-r--r--MediaBrowser.Providers/Tmdb/Music/TmdbMusicVideoProvider.cs1
-rw-r--r--MediaBrowser.Providers/Tmdb/People/TmdbPersonImageProvider.cs1
-rw-r--r--MediaBrowser.Providers/Tmdb/People/TmdbPersonProvider.cs9
-rw-r--r--MediaBrowser.Providers/Tmdb/TV/TmdbEpisodeImageProvider.cs1
-rw-r--r--MediaBrowser.Providers/Tmdb/TV/TmdbEpisodeProviderBase.cs1
-rw-r--r--MediaBrowser.Providers/Tmdb/TV/TmdbSeasonImageProvider.cs2
-rw-r--r--MediaBrowser.Providers/Tmdb/TV/TmdbSeasonProvider.cs1
-rw-r--r--MediaBrowser.Providers/Tmdb/TV/TmdbSeriesImageProvider.cs1
-rw-r--r--MediaBrowser.Providers/Tmdb/TV/TmdbSeriesProvider.cs10
-rw-r--r--MediaBrowser.Providers/Tmdb/Trailers/TmdbTrailerProvider.cs1
-rw-r--r--MediaBrowser.Providers/Users/UserMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Videos/VideoMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Years/YearMetadataService.cs2
-rw-r--r--MediaBrowser.WebDashboard/Api/ConfigurationPageInfo.cs51
-rw-r--r--MediaBrowser.WebDashboard/Api/DashboardService.cs85
-rw-r--r--MediaBrowser.WebDashboard/Api/PackageCreator.cs23
-rw-r--r--MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj13
-rw-r--r--MediaBrowser.WebDashboard/ServerEntryPoint.cs22
-rw-r--r--MediaBrowser.XbmcMetadata/Configuration/NfoConfigurationExtension.cs15
-rw-r--r--MediaBrowser.XbmcMetadata/Configuration/NfoConfigurationFactory.cs (renamed from MediaBrowser.XbmcMetadata/Configuration/NfoOptions.cs)12
-rw-r--r--MediaBrowser.XbmcMetadata/EntryPoint.cs6
-rw-r--r--MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj13
-rw-r--r--MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs68
-rw-r--r--MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs14
-rw-r--r--MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs16
-rw-r--r--MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs10
-rw-r--r--MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs15
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs16
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs16
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/BaseNfoProvider.cs15
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs10
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs16
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/MovieNfoProvider.cs35
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/MusicVideoNfoProvider.cs26
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/SeasonNfoProvider.cs17
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/SeriesNfoProvider.cs16
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/VideoNfoProvider.cs26
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/AlbumNfoSaver.cs16
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/ArtistNfoSaver.cs22
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs53
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/EpisodeNfoSaver.cs26
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs30
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/SeasonNfoSaver.cs14
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs16
-rw-r--r--MediaBrowser.sln16
-rw-r--r--README.md2
-rw-r--r--RSSDP/DeviceEventArgs.cs2
-rw-r--r--RSSDP/DeviceUnavailableEventArgs.cs3
-rw-r--r--RSSDP/DiscoveredSsdpDevice.cs4
-rw-r--r--RSSDP/DisposableManagedObjectBase.cs1
-rw-r--r--RSSDP/HttpParserBase.cs2
-rw-r--r--RSSDP/HttpRequestParser.cs4
-rw-r--r--RSSDP/HttpResponseParser.cs3
-rw-r--r--RSSDP/IEnumerableExtensions.cs1
-rw-r--r--RSSDP/ISsdpDevicePublisher.cs1
-rw-r--r--RSSDP/SsdpConstants.cs5
-rw-r--r--RSSDP/SsdpEmbeddedDevice.cs4
-rw-r--r--deployment/centos-package-x64/Dockerfile2
-rw-r--r--deployment/fedora-package-x64/Dockerfile8
-rw-r--r--deployment/fedora-package-x64/pkg-src/jellyfin.spec8
-rwxr-xr-xdeployment/linux-x64/docker-build.sh2
-rwxr-xr-xdeployment/macos/docker-build.sh2
-rwxr-xr-xdeployment/portable/docker-build.sh2
-rw-r--r--deployment/ubuntu-package-arm64/Dockerfile.amd642
-rw-r--r--deployment/ubuntu-package-arm64/Dockerfile.arm642
-rw-r--r--deployment/ubuntu-package-armhf/Dockerfile.amd642
-rw-r--r--deployment/ubuntu-package-armhf/Dockerfile.armhf2
-rw-r--r--deployment/ubuntu-package-x64/Dockerfile2
-rwxr-xr-xdeployment/win-x64/docker-build.sh4
-rwxr-xr-xdeployment/win-x86/docker-build.sh4
-rw-r--r--deployment/windows/build-jellyfin.ps19
-rw-r--r--deployment/windows/jellyfin.nsi10
-rw-r--r--jellyfin.ruleset16
-rw-r--r--tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs175
-rw-r--r--tests/Jellyfin.Api.Tests/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandlerTests.cs77
-rw-r--r--tests/Jellyfin.Api.Tests/Auth/RequiresElevationPolicy/RequiresElevationHandlerTests.cs38
-rw-r--r--tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj11
-rw-r--r--tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj4
-rw-r--r--tests/Jellyfin.Controller.Tests/AlphanumComparatorTests.cs44
-rw-r--r--tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj21
-rw-r--r--tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs16
-rw-r--r--tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs6
-rw-r--r--tests/Jellyfin.MediaEncoding.Tests/FFprobeParserTests.cs22
-rw-r--r--tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj10
-rw-r--r--tests/Jellyfin.MediaEncoding.Tests/Test Data/ffprobe1.json105
-rw-r--r--tests/Jellyfin.Naming.Tests/EpisodePathParserTest.cs55
-rw-r--r--tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj3
-rw-r--r--tests/Jellyfin.Naming.Tests/Music/MultiDiscAlbumTests.cs66
-rw-r--r--tests/Jellyfin.Naming.Tests/Subtitles/SubtitleParserTests.cs39
-rw-r--r--tests/Jellyfin.Naming.Tests/TV/AbsoluteEpisodeNumberTests.cs27
-rw-r--r--tests/Jellyfin.Naming.Tests/TV/DailyEpisodeTests.cs34
-rw-r--r--tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs83
-rw-r--r--tests/Jellyfin.Naming.Tests/TV/EpisodeNumberWithoutSeasonTests.cs36
-rw-r--r--tests/Jellyfin.Naming.Tests/TV/EpisodePathParserTest.cs50
-rw-r--r--tests/Jellyfin.Naming.Tests/TV/EpisodeWithoutSeasonTests.cs27
-rw-r--r--tests/Jellyfin.Naming.Tests/TV/MultiEpisodeTests.cs80
-rw-r--r--tests/Jellyfin.Naming.Tests/TV/SeasonFolderTests.cs31
-rw-r--r--tests/Jellyfin.Naming.Tests/TV/SeasonNumberTests.cs65
-rw-r--r--tests/Jellyfin.Naming.Tests/TV/SimpleEpisodeTests.cs39
-rw-r--r--tests/Jellyfin.Naming.Tests/Video/BaseVideoTest.cs13
-rw-r--r--tests/Jellyfin.Naming.Tests/Video/CleanDateTimeTests.cs59
-rw-r--r--tests/Jellyfin.Naming.Tests/Video/CleanStringTests.cs45
-rw-r--r--tests/Jellyfin.Naming.Tests/Video/ExtraTests.cs77
-rw-r--r--tests/Jellyfin.Naming.Tests/Video/Format3DTests.cs78
-rw-r--r--tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs437
-rw-r--r--tests/Jellyfin.Naming.Tests/Video/StackTests.cs452
-rw-r--r--tests/Jellyfin.Naming.Tests/Video/StubTests.cs53
-rw-r--r--tests/Jellyfin.Naming.Tests/Video/VideoListResolverTests.cs457
-rw-r--r--tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs275
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs42
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj24
-rw-r--r--tests/coverletArgs.runsettings17
922 files changed, 12281 insertions, 6631 deletions
diff --git a/.ci/azure-pipelines-compat.yml b/.ci/azure-pipelines-compat.yml
new file mode 100644
index 000000000..762bbdcb2
--- /dev/null
+++ b/.ci/azure-pipelines-compat.yml
@@ -0,0 +1,96 @@
+parameters:
+ - name: Packages
+ type: object
+ default: {}
+ - name: LinuxImage
+ type: string
+ default: "ubuntu-latest"
+ - name: DotNetSdkVersion
+ type: string
+ default: 3.1.100
+
+jobs:
+ - job: CompatibilityCheck
+ displayName: Compatibility Check
+ pool:
+ vmImage: "${{ parameters.LinuxImage }}"
+ # only execute for pull requests
+ condition: and(succeeded(), variables['System.PullRequest.PullRequestNumber'])
+ strategy:
+ matrix:
+ ${{ each Package in parameters.Packages }}:
+ ${{ Package.key }}:
+ NugetPackageName: ${{ Package.value.NugetPackageName }}
+ AssemblyFileName: ${{ Package.value.AssemblyFileName }}
+ maxParallel: 2
+ dependsOn: MainBuild
+ steps:
+ - checkout: none
+
+ - task: UseDotNet@2
+ displayName: "Update DotNet"
+ inputs:
+ packageType: sdk
+ version: ${{ parameters.DotNetSdkVersion }}
+
+ - task: DownloadPipelineArtifact@2
+ displayName: "Download New Assembly Build Artifact"
+ inputs:
+ source: "current"
+ artifact: "$(NugetPackageName)"
+ path: "$(System.ArtifactsDirectory)/new-artifacts"
+ runVersion: "latest"
+
+ - task: CopyFiles@2
+ displayName: "Copy New Assembly Build Artifact"
+ inputs:
+ sourceFolder: $(System.ArtifactsDirectory)/new-artifacts
+ contents: "**/*.dll"
+ targetFolder: $(System.ArtifactsDirectory)/new-release
+ cleanTargetFolder: true
+ overWrite: true
+ flattenFolders: true
+
+ - task: DownloadPipelineArtifact@2
+ displayName: "Download Reference Assembly Build Artifact"
+ inputs:
+ source: "specific"
+ artifact: "$(NugetPackageName)"
+ path: "$(System.ArtifactsDirectory)/current-artifacts"
+ project: "$(System.TeamProjectId)"
+ pipeline: "$(System.DefinitionId)"
+ runVersion: "latestFromBranch"
+ runBranch: "refs/heads/$(System.PullRequest.TargetBranch)"
+
+ - task: CopyFiles@2
+ displayName: "Copy Reference Assembly Build Artifact"
+ inputs:
+ sourceFolder: $(System.ArtifactsDirectory)/current-artifacts
+ contents: "**/*.dll"
+ targetFolder: $(System.ArtifactsDirectory)/current-release
+ cleanTargetFolder: true
+ overWrite: true
+ flattenFolders: true
+
+ - task: DownloadGitHubRelease@0
+ displayName: "Download ABI Compatibility Check Tool"
+ inputs:
+ connection: Jellyfin Release Download
+ userRepository: EraYaN/dotnet-compatibility
+ defaultVersionType: "latest"
+ itemPattern: "**-ci.zip"
+ downloadPath: "$(System.ArtifactsDirectory)"
+
+ - task: ExtractFiles@1
+ displayName: "Extract ABI Compatibility Check Tool"
+ inputs:
+ archiveFilePatterns: "$(System.ArtifactsDirectory)/*-ci.zip"
+ destinationFolder: $(System.ArtifactsDirectory)/tools
+ cleanDestinationFolder: true
+
+ # The `--warnings-only` switch will swallow the return code and not emit any errors.
+ - task: CmdLine@2
+ displayName: "Execute ABI Compatibility Check Tool"
+ inputs:
+ script: "dotnet tools/CompatibilityCheckerCLI.dll current-release/$(AssemblyFileName) new-release/$(AssemblyFileName) --azure-pipelines --warnings-only"
+ workingDirectory: $(System.ArtifactsDirectory)
diff --git a/.ci/azure-pipelines-main.yml b/.ci/azure-pipelines-main.yml
new file mode 100644
index 000000000..e33ab72f2
--- /dev/null
+++ b/.ci/azure-pipelines-main.yml
@@ -0,0 +1,101 @@
+parameters:
+ LinuxImage: "ubuntu-latest"
+ RestoreBuildProjects: "Jellyfin.Server/Jellyfin.Server.csproj"
+ DotNetSdkVersion: 3.1.100
+
+jobs:
+ - job: MainBuild
+ displayName: Main Build
+ strategy:
+ matrix:
+ Release:
+ BuildConfiguration: Release
+ Debug:
+ BuildConfiguration: Debug
+ maxParallel: 2
+ pool:
+ vmImage: "${{ parameters.LinuxImage }}"
+ steps:
+ - checkout: self
+ clean: true
+ submodules: true
+ persistCredentials: true
+
+ - task: CmdLine@2
+ displayName: "Clone Web Client (Master, Release, or Tag)"
+ condition: and(succeeded(), or(contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')), eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
+ inputs:
+ script: "git clone --single-branch --branch $(Build.SourceBranchName) --depth=1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web"
+
+ - task: CmdLine@2
+ displayName: "Clone Web Client (PR)"
+ condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master')), eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest'))
+ inputs:
+ script: "git clone --single-branch --branch $(System.PullRequest.TargetBranch) --depth 1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web"
+
+ - task: NodeTool@0
+ displayName: "Install Node"
+ condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')), eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
+ inputs:
+ versionSpec: "10.x"
+
+ - task: CmdLine@2
+ displayName: "Build Web Client"
+ condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')), eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
+ inputs:
+ script: yarn install && yarn build
+ workingDirectory: $(Agent.TempDirectory)/jellyfin-web
+
+ - task: CopyFiles@2
+ displayName: "Copy Web Client"
+ condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')), eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
+ inputs:
+ sourceFolder: $(Agent.TempDirectory)/jellyfin-web/dist
+ contents: "**"
+ targetFolder: $(Build.SourcesDirectory)/MediaBrowser.WebDashboard/jellyfin-web
+ cleanTargetFolder: true
+ overWrite: true
+ flattenFolders: false
+
+ - task: UseDotNet@2
+ displayName: "Update DotNet"
+ inputs:
+ packageType: sdk
+ version: ${{ parameters.DotNetSdkVersion }}
+
+ - task: DotNetCoreCLI@2
+ displayName: "Publish Server"
+ inputs:
+ command: publish
+ publishWebProjects: false
+ projects: "${{ parameters.RestoreBuildProjects }}"
+ arguments: "--configuration $(BuildConfiguration) --output $(build.artifactstagingdirectory)"
+ zipAfterPublish: false
+
+ - task: PublishPipelineArtifact@0
+ displayName: "Publish Artifact Naming"
+ condition: and(succeeded(), eq(variables['BuildConfiguration'], 'Release'))
+ inputs:
+ targetPath: "$(build.artifactstagingdirectory)/Jellyfin.Server/Emby.Naming.dll"
+ artifactName: "Jellyfin.Naming"
+
+ - task: PublishPipelineArtifact@0
+ displayName: "Publish Artifact Controller"
+ condition: and(succeeded(), eq(variables['BuildConfiguration'], 'Release'))
+ inputs:
+ targetPath: "$(build.artifactstagingdirectory)/Jellyfin.Server/MediaBrowser.Controller.dll"
+ artifactName: "Jellyfin.Controller"
+
+ - task: PublishPipelineArtifact@0
+ displayName: "Publish Artifact Model"
+ condition: and(succeeded(), eq(variables['BuildConfiguration'], 'Release'))
+ inputs:
+ targetPath: "$(build.artifactstagingdirectory)/Jellyfin.Server/MediaBrowser.Model.dll"
+ artifactName: "Jellyfin.Model"
+
+ - task: PublishPipelineArtifact@0
+ displayName: "Publish Artifact Common"
+ condition: and(succeeded(), eq(variables['BuildConfiguration'], 'Release'))
+ inputs:
+ targetPath: "$(build.artifactstagingdirectory)/Jellyfin.Server/MediaBrowser.Common.dll"
+ artifactName: "Jellyfin.Common"
diff --git a/.ci/azure-pipelines-test.yml b/.ci/azure-pipelines-test.yml
new file mode 100644
index 000000000..4455632e1
--- /dev/null
+++ b/.ci/azure-pipelines-test.yml
@@ -0,0 +1,65 @@
+parameters:
+ - name: ImageNames
+ type: object
+ default:
+ Linux: "ubuntu-latest"
+ Windows: "windows-latest"
+ macOS: "macos-latest"
+ - name: TestProjects
+ type: string
+ default: "tests/**/*Tests.csproj"
+ - name: DotNetSdkVersion
+ type: string
+ default: 3.1.100
+
+jobs:
+ - job: MainTest
+ displayName: Main Test
+ strategy:
+ matrix:
+ ${{ each imageName in parameters.ImageNames }}:
+ ${{ imageName.key }}:
+ ImageName: ${{ imageName.value }}
+ maxParallel: 3
+ pool:
+ vmImage: "$(ImageName)"
+ steps:
+ - checkout: self
+ clean: true
+ submodules: true
+ persistCredentials: false
+
+ - task: UseDotNet@2
+ displayName: "Update DotNet"
+ inputs:
+ packageType: sdk
+ version: ${{ parameters.DotNetSdkVersion }}
+
+ - task: DotNetCoreCLI@2
+ displayName: Run .NET Core CLI tests
+ inputs:
+ command: "test"
+ projects: ${{ parameters.TestProjects }}
+ arguments: '--configuration Release --collect:"XPlat Code Coverage" --settings tests/coverletArgs.runsettings --verbosity minimal "-p:GenerateDocumentationFile=False"'
+ publishTestResults: true
+ testRunTitle: $(Agent.JobName)
+ workingDirectory: "$(Build.SourcesDirectory)"
+
+ - task: Palmmedia.reportgenerator.reportgenerator-build-release-task.reportgenerator@4
+ condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) # !! THIS is for V1 only V2 will/should support merging
+ displayName: ReportGenerator (merge)
+ inputs:
+ reports: "$(Agent.TempDirectory)/**/coverage.cobertura.xml"
+ targetdir: "$(Agent.TempDirectory)/merged/"
+ reporttypes: "Cobertura"
+
+ ## V2 is already in the repository but it does not work "wrong number of segments" YAML error.
+ - task: PublishCodeCoverageResults@1
+ condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) # !! THIS is for V1 only V2 will/should support merging
+ displayName: Publish Code Coverage
+ inputs:
+ codeCoverageTool: "cobertura"
+ #summaryFileLocation: '$(Agent.TempDirectory)/**/coverage.cobertura.xml' # !!THIS IS FOR V2
+ summaryFileLocation: "$(Agent.TempDirectory)/merged/**.xml"
+ pathToSources: $(Build.SourcesDirectory)
+ failIfCoverageEmpty: true
diff --git a/.ci/azure-pipelines-windows.yml b/.ci/azure-pipelines-windows.yml
new file mode 100644
index 000000000..11856f9c8
--- /dev/null
+++ b/.ci/azure-pipelines-windows.yml
@@ -0,0 +1,82 @@
+parameters:
+ WindowsImage: "windows-latest"
+ TestProjects: "tests/**/*Tests.csproj"
+ DotNetSdkVersion: 3.1.100
+
+jobs:
+ - job: PublishWindows
+ displayName: Publish Windows
+ pool:
+ vmImage: ${{ parameters.WindowsImage }}
+ steps:
+ - checkout: self
+ clean: true
+ submodules: true
+ persistCredentials: true
+
+ - task: CmdLine@2
+ displayName: "Clone Web Client (Master, Release, or Tag)"
+ condition: and(succeeded(), or(contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master'), contains(variables['Build.SourceBranch'], 'tag')), in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
+ inputs:
+ script: "git clone --single-branch --branch $(Build.SourceBranchName) --depth=1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web"
+
+ - task: CmdLine@2
+ displayName: "Clone Web Client (PR)"
+ condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master')), in(variables['Build.Reason'], 'PullRequest'))
+ inputs:
+ script: "git clone --single-branch --branch $(System.PullRequest.TargetBranch) --depth 1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web"
+
+ - task: NodeTool@0
+ displayName: "Install Node"
+ condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
+ inputs:
+ versionSpec: "10.x"
+
+ - task: CmdLine@2
+ displayName: "Build Web Client"
+ condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
+ inputs:
+ script: yarn install && yarn build
+ workingDirectory: $(Agent.TempDirectory)/jellyfin-web
+
+ - task: CopyFiles@2
+ displayName: "Copy Web Client"
+ condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
+ inputs:
+ sourceFolder: $(Agent.TempDirectory)/jellyfin-web/dist
+ contents: "**"
+ targetFolder: $(Build.SourcesDirectory)/MediaBrowser.WebDashboard/jellyfin-web
+ cleanTargetFolder: true
+ overWrite: true
+ flattenFolders: false
+
+ - task: CmdLine@2
+ displayName: "Clone UX Repository"
+ inputs:
+ script: git clone --depth=1 https://github.com/jellyfin/jellyfin-ux $(Agent.TempDirectory)\jellyfin-ux
+
+ - task: PowerShell@2
+ displayName: "Build NSIS Installer"
+ inputs:
+ targetType: "filePath"
+ filePath: ./deployment/windows/build-jellyfin.ps1
+ arguments: -InstallFFMPEG -InstallNSSM -MakeNSIS -InstallTrayApp -UXLocation $(Agent.TempDirectory)\jellyfin-ux -InstallLocation $(build.artifactstagingdirectory)
+ errorActionPreference: "stop"
+ workingDirectory: $(Build.SourcesDirectory)
+
+ - task: CopyFiles@2
+ displayName: "Copy NSIS Installer"
+ inputs:
+ sourceFolder: $(Build.SourcesDirectory)/deployment/windows/
+ contents: "jellyfin*.exe"
+ targetFolder: $(System.ArtifactsDirectory)/setup
+ cleanTargetFolder: true
+ overWrite: true
+ flattenFolders: true
+
+ - task: PublishPipelineArtifact@0
+ displayName: "Publish Artifact Setup"
+ condition: succeeded()
+ inputs:
+ targetPath: "$(build.artifactstagingdirectory)/setup"
+ artifactName: "Jellyfin Server Setup"
diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml
index 143873266..f79a85b21 100644
--- a/.ci/azure-pipelines.yml
+++ b/.ci/azure-pipelines.yml
@@ -2,9 +2,11 @@ name: $(Date:yyyyMMdd)$(Rev:.r)
variables:
- name: TestProjects
- value: 'tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj'
+ value: "tests/**/*Tests.csproj"
- name: RestoreBuildProjects
- value: 'Jellyfin.Server/Jellyfin.Server.csproj'
+ value: "Jellyfin.Server/Jellyfin.Server.csproj"
+ - name: DotNetSdkVersion
+ value: 3.1.100
pr:
autoCancel: true
@@ -13,232 +15,26 @@ trigger:
batch: true
jobs:
- - job: main_build
- displayName: Main Build
- pool:
- vmImage: ubuntu-latest
- strategy:
- matrix:
- Release:
- BuildConfiguration: Release
- Debug:
- BuildConfiguration: Debug
- maxParallel: 2
- steps:
- - checkout: self
- clean: true
- submodules: true
- persistCredentials: true
-
- - task: CmdLine@2
- displayName: "Clone Web Client (Master, Release, or Tag)"
- condition: and(succeeded(), or(contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
- inputs:
- script: 'git clone --single-branch --branch $(Build.SourceBranchName) --depth=1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web'
-
- - task: CmdLine@2
- displayName: "Clone Web Client (PR)"
- condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest'))
- inputs:
- script: 'git clone --single-branch --branch $(System.PullRequest.TargetBranch) --depth 1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web'
-
- - task: NodeTool@0
- displayName: 'Install Node'
- condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
- inputs:
- versionSpec: '10.x'
-
- - task: CmdLine@2
- displayName: "Build Web Client"
- condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
- inputs:
- script: yarn install
- workingDirectory: $(Agent.TempDirectory)/jellyfin-web
-
- - task: CopyFiles@2
- displayName: 'Copy Web Client'
- condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
- inputs:
- sourceFolder: $(Agent.TempDirectory)/jellyfin-web/dist # Optional
- contents: '**'
- targetFolder: $(Build.SourcesDirectory)/MediaBrowser.WebDashboard/jellyfin-web
- cleanTargetFolder: true # Optional
- overWrite: true # Optional
- flattenFolders: false # Optional
-
- - task: UseDotNet@2
- displayName: 'Update DotNet'
- inputs:
- packageType: sdk
- version: 3.1.100
-
- - task: DotNetCoreCLI@2
- displayName: 'Publish Server'
- inputs:
- command: publish
- publishWebProjects: false
- projects: '$(RestoreBuildProjects)'
- arguments: '--configuration $(BuildConfiguration) --output $(build.artifactstagingdirectory)'
- zipAfterPublish: false
-
- - task: PublishPipelineArtifact@0
- displayName: 'Publish Artifact Naming'
- condition: and(eq(variables['BuildConfiguration'], 'Release'), succeeded())
- inputs:
- targetPath: '$(build.artifactstagingdirectory)/Jellyfin.Server/Emby.Naming.dll'
- artifactName: 'Jellyfin.Naming'
-
- - task: PublishPipelineArtifact@0
- displayName: 'Publish Artifact Controller'
- condition: and(eq(variables['BuildConfiguration'], 'Release'), succeeded())
- inputs:
- targetPath: '$(build.artifactstagingdirectory)/Jellyfin.Server/MediaBrowser.Controller.dll'
- artifactName: 'Jellyfin.Controller'
-
- - task: PublishPipelineArtifact@0
- displayName: 'Publish Artifact Model'
- condition: and(eq(variables['BuildConfiguration'], 'Release'), succeeded())
- inputs:
- targetPath: '$(build.artifactstagingdirectory)/Jellyfin.Server/MediaBrowser.Model.dll'
- artifactName: 'Jellyfin.Model'
-
- - task: PublishPipelineArtifact@0
- displayName: 'Publish Artifact Common'
- condition: and(eq(variables['BuildConfiguration'], 'Release'), succeeded())
- inputs:
- targetPath: '$(build.artifactstagingdirectory)/Jellyfin.Server/MediaBrowser.Common.dll'
- artifactName: 'Jellyfin.Common'
-
- - job: main_test
- displayName: Main Test
- pool:
- vmImage: windows-latest
- steps:
- - checkout: self
- clean: true
- submodules: true
- persistCredentials: false
-
- - task: DotNetCoreCLI@2
- displayName: Build
- inputs:
- command: build
- publishWebProjects: false
- projects: '$(TestProjects)'
- arguments: '--configuration $(BuildConfiguration)'
- zipAfterPublish: false
-
- - task: VisualStudioTestPlatformInstaller@1
- inputs:
- packageFeedSelector: 'nugetOrg' # Options: nugetOrg, customFeed, netShare
- versionSelector: 'latestPreRelease' # Required when packageFeedSelector == NugetOrg || PackageFeedSelector == CustomFeed# Options: latestPreRelease, latestStable, specificVersion
-
- - task: VSTest@2
- inputs:
- testSelector: 'testAssemblies' # Options: testAssemblies, testPlan, testRun
- testAssemblyVer2: | # Required when testSelector == TestAssemblies
- **\bin\$(BuildConfiguration)\**\*test*.dll
- !**\obj\**
- !**\xunit.runner.visualstudio.testadapter.dll
- !**\xunit.runner.visualstudio.dotnetcore.testadapter.dll
- searchFolder: '$(System.DefaultWorkingDirectory)'
- runInParallel: True # Optional
- runTestsInIsolation: True # Optional
- codeCoverageEnabled: True # Optional
- configuration: 'Debug' # Optional
- publishRunAttachments: true # Optional
-
- - job: main_build_win
- displayName: Publish Windows
- pool:
- vmImage: windows-latest
- strategy:
- matrix:
- Release:
- BuildConfiguration: Release
- maxParallel: 2
- steps:
- - checkout: self
- clean: true
- submodules: true
- persistCredentials: true
-
- - task: CmdLine@2
- displayName: "Clone Web Client (Master, Release, or Tag)"
- condition: and(succeeded(), or(contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master'), contains(variables['Build.SourceBranch'], 'tag')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
- inputs:
- script: 'git clone --single-branch --branch $(Build.SourceBranchName) --depth=1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web'
-
- - task: CmdLine@2
- displayName: "Clone Web Client (PR)"
- condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest'))
- inputs:
- script: 'git clone --single-branch --branch $(System.PullRequest.TargetBranch) --depth 1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web'
-
- - task: NodeTool@0
- displayName: 'Install Node'
- condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
- inputs:
- versionSpec: '10.x'
-
- - task: CmdLine@2
- displayName: "Build Web Client"
- condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
- inputs:
- script: yarn install
- workingDirectory: $(Agent.TempDirectory)/jellyfin-web
-
- - task: CopyFiles@2
- displayName: 'Copy Web Client'
- condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
- inputs:
- sourceFolder: $(Agent.TempDirectory)/jellyfin-web/dist # Optional
- contents: '**'
- targetFolder: $(Build.SourcesDirectory)/MediaBrowser.WebDashboard/jellyfin-web
- cleanTargetFolder: true # Optional
- overWrite: true # Optional
- flattenFolders: false # Optional
-
- - task: CmdLine@2
- displayName: 'Clone UX Repository'
- inputs:
- script: git clone --depth=1 https://github.com/jellyfin/jellyfin-ux $(Agent.TempDirectory)\jellyfin-ux
-
- - task: PowerShell@2
- displayName: 'Build NSIS Installer'
- inputs:
- targetType: 'filePath' # Optional. Options: filePath, inline
- filePath: ./deployment/windows/build-jellyfin.ps1 # Required when targetType == FilePath
- arguments: -InstallFFMPEG -InstallNSSM -MakeNSIS -InstallTrayApp -UXLocation $(Agent.TempDirectory)\jellyfin-ux -InstallLocation $(build.artifactstagingdirectory)
- errorActionPreference: 'stop' # Optional. Options: stop, continue, silentlyContinue
- workingDirectory: $(Build.SourcesDirectory) # Optional
-
- - task: CopyFiles@2
- displayName: 'Copy NSIS Installer'
- inputs:
- sourceFolder: $(Build.SourcesDirectory)/deployment/windows/ # Optional
- contents: 'jellyfin*.exe'
- targetFolder: $(System.ArtifactsDirectory)/setup
- cleanTargetFolder: true # Optional
- overWrite: true # Optional
- flattenFolders: true # Optional
-
- - task: PublishPipelineArtifact@0
- displayName: 'Publish Artifact Setup'
- condition: and(eq(variables['BuildConfiguration'], 'Release'), succeeded())
- inputs:
- targetPath: '$(build.artifactstagingdirectory)/setup'
- artifactName: 'Jellyfin Server Setup'
-
- - job: dotnet_compat
- displayName: Compatibility Check
- pool:
- vmImage: ubuntu-latest
- dependsOn: main_build
- # only execute for pull requests
- condition: and(succeeded(), variables['System.PullRequest.PullRequestNumber'])
- strategy:
- matrix:
+ - template: azure-pipelines-main.yml
+ parameters:
+ LinuxImage: "ubuntu-latest"
+ RestoreBuildProjects: $(RestoreBuildProjects)
+
+ - template: azure-pipelines-test.yml
+ parameters:
+ ImageNames:
+ Linux: "ubuntu-latest"
+ Windows: "windows-latest"
+ macOS: "macos-latest"
+
+ - template: azure-pipelines-windows.yml
+ parameters:
+ WindowsImage: "windows-latest"
+ TestProjects: $(TestProjects)
+
+ - template: azure-pipelines-compat.yml
+ parameters:
+ Packages:
Naming:
NugetPackageName: Jellyfin.Naming
AssemblyFileName: Emby.Naming.dll
@@ -251,74 +47,4 @@ jobs:
Common:
NugetPackageName: Jellyfin.Common
AssemblyFileName: MediaBrowser.Common.dll
- maxParallel: 2
- steps:
- - checkout: none
-
- - task: UseDotNet@2
- displayName: 'Update DotNet'
- inputs:
- packageType: sdk
- version: 3.1.100
-
- - task: DownloadPipelineArtifact@2
- displayName: 'Download New Assembly Build Artifact'
- inputs:
- source: 'current' # Options: current, specific
- artifact: '$(NugetPackageName)' # Optional
- path: '$(System.ArtifactsDirectory)/new-artifacts'
- runVersion: 'latest' # Required when source == Specific. Options: latest, latestFromBranch, specific
-
- - task: CopyFiles@2
- displayName: 'Copy New Assembly Build Artifact'
- inputs:
- sourceFolder: $(System.ArtifactsDirectory)/new-artifacts # Optional
- contents: '**/*.dll'
- targetFolder: $(System.ArtifactsDirectory)/new-release
- cleanTargetFolder: true # Optional
- overWrite: true # Optional
- flattenFolders: true # Optional
-
- - task: DownloadPipelineArtifact@2
- displayName: 'Download Reference Assembly Build Artifact'
- inputs:
- source: 'specific' # Options: current, specific
- artifact: '$(NugetPackageName)' # Optional
- path: '$(System.ArtifactsDirectory)/current-artifacts'
- project: '$(System.TeamProjectId)' # Required when source == Specific
- pipeline: '$(System.DefinitionId)' # Required when source == Specific
- runVersion: 'latestFromBranch' # Required when source == Specific. Options: latest, latestFromBranch, specific
- runBranch: 'refs/heads/$(System.PullRequest.TargetBranch)' # Required when source == Specific && runVersion == LatestFromBranch
-
- - task: CopyFiles@2
- displayName: 'Copy Reference Assembly Build Artifact'
- inputs:
- sourceFolder: $(System.ArtifactsDirectory)/current-artifacts # Optional
- contents: '**/*.dll'
- targetFolder: $(System.ArtifactsDirectory)/current-release
- cleanTargetFolder: true # Optional
- overWrite: true # Optional
- flattenFolders: true # Optional
-
- - task: DownloadGitHubRelease@0
- displayName: 'Download ABI Compatibility Check Tool'
- inputs:
- connection: Jellyfin Release Download
- userRepository: EraYaN/dotnet-compatibility
- defaultVersionType: 'latest' # Options: latest, specificVersion, specificTag
- itemPattern: '**-ci.zip' # Optional
- downloadPath: '$(System.ArtifactsDirectory)'
-
- - task: ExtractFiles@1
- displayName: 'Extract ABI Compatibility Check Tool'
- inputs:
- archiveFilePatterns: '$(System.ArtifactsDirectory)/*-ci.zip'
- destinationFolder: $(System.ArtifactsDirectory)/tools
- cleanDestinationFolder: true
-
- # The `--warnings-only` switch will swallow the return code and not emit any errors.
- - task: CmdLine@2
- displayName: 'Execute ABI Compatibility Check Tool'
- inputs:
- script: 'dotnet tools/CompatibilityCheckerCLI.dll current-release/$(AssemblyFileName) new-release/$(AssemblyFileName) --azure-pipelines --warnings-only'
- workingDirectory: $(System.ArtifactsDirectory) # Optional
+ LinuxImage: "ubuntu-latest"
diff --git a/.ci/publish-nightly.yml b/.ci/publish-nightly.yml
deleted file mode 100644
index a693e10f6..000000000
--- a/.ci/publish-nightly.yml
+++ /dev/null
@@ -1,46 +0,0 @@
-name: Nightly-$(date:yyyyMMdd).$(rev:r)
-
-variables:
- - name: Version
- value: '1.0.0'
-
-trigger: none
-pr: none
-
-jobs:
- - job: publish_artifacts_nightly
- displayName: Publish Artifacts Nightly
- pool:
- vmImage: ubuntu-latest
- steps:
- - checkout: none
- - task: DownloadPipelineArtifact@2
- displayName: Download the Windows Setup Artifact
- inputs:
- source: 'specific' # Options: current, specific
- artifact: 'Jellyfin Server Setup' # Optional
- path: '$(System.ArtifactsDirectory)/win-installer'
- project: '$(System.TeamProjectId)' # Required when source == Specific
- pipelineId: 1 # Required when source == Specific
- runVersion: 'latestFromBranch' # Required when source == Specific. Options: latest, latestFromBranch, specific
- runBranch: 'refs/heads/master' # Required when source == Specific && runVersion == LatestFromBranch
-
- - task: SSH@0
- displayName: 'Create Drop directory'
- inputs:
- sshEndpoint: 'Jellyfin Build Server'
- commands: 'mkdir -p /srv/incoming/jellyfin_$(Version)/win-installer && ln -s /srv/incoming/jellyfin_$(Version) /srv/incoming/jellyfin_nightly_azure_upload'
-
- - task: CopyFilesOverSSH@0
- displayName: 'Copy the Windows Setup to the Repo'
- inputs:
- sshEndpoint: 'Jellyfin Build Server'
- sourceFolder: '$(System.ArtifactsDirectory)/win-installer'
- contents: 'jellyfin_*.exe'
- targetFolder: '/srv/incoming/jellyfin_nightly_azure_upload/win-installer'
-
- - task: SSH@0
- displayName: 'Clean up SCP symlink'
- inputs:
- sshEndpoint: 'Jellyfin Build Server'
- commands: 'rm -f /srv/incoming/jellyfin_nightly_azure_upload'
diff --git a/.ci/publish-release.yml b/.ci/publish-release.yml
deleted file mode 100644
index 57e77ae5a..000000000
--- a/.ci/publish-release.yml
+++ /dev/null
@@ -1,48 +0,0 @@
-name: Release-$(Version)-$(date:yyyyMMdd).$(rev:r)
-
-variables:
- - name: Version
- value: '1.0.0'
- - name: UsedRunId
- value: 0
-
-trigger: none
-pr: none
-
-jobs:
- - job: publish_artifacts_release
- displayName: Publish Artifacts Release
- pool:
- vmImage: ubuntu-latest
- steps:
- - checkout: none
- - task: DownloadPipelineArtifact@2
- displayName: Download the Windows Setup Artifact
- inputs:
- source: 'specific' # Options: current, specific
- artifact: 'Jellyfin Server Setup' # Optional
- path: '$(System.ArtifactsDirectory)/win-installer'
- project: '$(System.TeamProjectId)' # Required when source == Specific
- pipelineId: 1 # Required when source == Specific
- runVersion: 'specific' # Required when source == Specific. Options: latest, latestFromBranch, specific
- runId: $(UsedRunId)
-
- - task: SSH@0
- displayName: 'Create Drop directory'
- inputs:
- sshEndpoint: 'Jellyfin Build Server'
- commands: 'mkdir -p /srv/incoming/jellyfin_$(Version)/win-installer && ln -s /srv/incoming/jellyfin_$(Version) /srv/incoming/jellyfin_release_azure_upload'
-
- - task: CopyFilesOverSSH@0
- displayName: 'Copy the Windows Setup to the Repo'
- inputs:
- sshEndpoint: 'Jellyfin Build Server'
- sourceFolder: '$(System.ArtifactsDirectory)/win-installer'
- contents: 'jellyfin_*.exe'
- targetFolder: '/srv/incoming/jellyfin_release_azure_upload/win-installer'
-
- - task: SSH@0
- displayName: 'Clean up SCP symlink'
- inputs:
- sshEndpoint: 'Jellyfin Build Server'
- commands: 'rm -f /srv/incoming/jellyfin_release_azure_upload'
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index bd13d4b00..d67e1c98b 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -10,6 +10,19 @@ assignees: ''
**Describe the bug**
<!-- A clear and concise description of what the bug is. -->
+**System (please complete the following information):**
+ - OS: [e.g. Debian, Windows]
+ - Virtualization: [e.g. Docker, KVM, LXC]
+ - Clients: [Browser, Android, Fire Stick, etc.]
+ - Browser: [e.g. Firefox 72, Chrome 80, Safari 13]
+ - Jellyfin Version: [e.g. 10.4.3, nightly 20191231]
+ - Playback: [Direct Play, Remux, Direct Stream, Transcode]
+ - Installed Plugins: [e.g. none, Fanart, Anime, etc.]
+ - Reverse Proxy: [e.g. none, nginx, apache, etc.]
+ - Base URL: [e.g. none, yes: /example]
+ - Networking: [e.g. Host, Bridge/NAT]
+ - Storage: [e.g. local, NFS, cloud]
+
**To Reproduce**
<!-- Steps to reproduce the behavior: -->
1. Go to '...'
@@ -26,12 +39,5 @@ assignees: ''
**Screenshots**
<!-- If applicable, add screenshots to help explain your problem. -->
-**System (please complete the following information):**
- - OS: [e.g. Docker, Debian, Windows]
- - Browser: [e.g. Firefox, Chrome, Safari]
- - Jellyfin Version: [e.g. 10.0.1]
- - Installed Plugins: [e.g. none, Fanart, Anime, etc.]
- - Reverse proxy: [e.g. no, nginx, apache, etc.]
-
**Additional context**
<!-- Add any other context about the problem here. -->
diff --git a/.github/ISSUE_TEMPLATE/media_playback.md b/.github/ISSUE_TEMPLATE/media_playback.md
index 93af33fbf..b51500f87 100644
--- a/.github/ISSUE_TEMPLATE/media_playback.md
+++ b/.github/ISSUE_TEMPLATE/media_playback.md
@@ -11,7 +11,10 @@ assignees: ''
<!-- Use the Media Info tool (set to text format, download here: https://mediaarea.net/en/MediaInfo) or copy the info from the web ui for the file with the playback issue. -->
**Logs**
-<!-- Please paste any log message from during the playback issue, for example the ffmpeg command line can be very useful. -->
+<!-- Please paste any log messages from during the playback issue. -->
+
+**FFmpeg Logs**
+<!-- Please paste any FFmpeg logs if remuxing or transcoding appears to be part of the issue. -->
**Stats for Nerds Screenshots**
<!-- If available, add screenshots of the stats for nerds screen to help show the issue problem. -->
@@ -29,4 +32,3 @@ assignees: ''
- Client: [e.g. Web/Browser, webOS, Android, Android TV, Electron]
- Browser (if Web client): [e.g. Firefox, Chrome, Safari]
- Client and Browser Version: [e.g. 10.3.4 and 68.0]
-
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index 458944778..f195c125f 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -1,37 +1,133 @@
# Jellyfin Contributors
- - [JoshuaBoniface](https://github.com/joshuaboniface)
- - [nvllsvm](https://github.com/nvllsvm)
- - [JustAMan](https://github.com/JustAMan)
+ - [97carmine](https://github.com/97carmine)
+ - [Abbe98](https://github.com/Abbe98)
+ - [agrenott](https://github.com/agrenott)
+ - [AndreCarvalho](https://github.com/AndreCarvalho)
+ - [anthonylavado](https://github.com/anthonylavado)
+ - [Artiume](https://github.com/Artiume)
+ - [AThomsen](https://github.com/AThomsen)
+ - [bilde2910](https://github.com/bilde2910)
+ - [bfayers](https://github.com/bfayers)
+ - [BnMcG](https://github.com/BnMcG)
+ - [Bond-009](https://github.com/Bond-009)
+ - [brianjmurrell](https://github.com/brianjmurrell)
+ - [bugfixin](https://github.com/bugfixin)
+ - [chaosinnovator](https://github.com/chaosinnovator)
+ - [ckcr4lyf](https://github.com/ckcr4lyf)
+ - [crankdoofus](https://github.com/crankdoofus)
+ - [crobibero](https://github.com/crobibero)
+ - [cromefire](https://github.com/cromefire)
+ - [cryptobank](https://github.com/cryptobank)
+ - [cvium](https://github.com/cvium)
+ - [dannymichel](https://github.com/dannymichel)
+ - [DaveChild](https://github.com/DaveChild)
- [dcrdev](https://github.com/dcrdev)
+ - [dhartung](https://github.com/dhartung)
+ - [dinki](https://github.com/dinki)
+ - [dkanada](https://github.com/dkanada)
+ - [dlahoti](https://github.com/dlahoti)
+ - [dmitrylyzo](https://github.com/dmitrylyzo)
+ - [DMouse10462](https://github.com/DMouse10462)
+ - [DrPandemic](https://github.com/DrPandemic)
- [EraYaN](https://github.com/EraYaN)
+ - [escabe](https://github.com/escabe)
+ - [excelite](https://github.com/excelite)
+ - [fasheng](https://github.com/fasheng)
+ - [ferferga](https://github.com/ferferga)
+ - [fhriley](https://github.com/fhriley)
- [flemse](https://github.com/flemse)
- - [bfayers](https://github.com/bfayers)
- - [Bond_009](https://github.com/Bond-009)
- - [AnthonyLavado](https://github.com/anthonylavado)
- - [sparky8251](https://github.com/sparky8251)
- - [LeoVerto](https://github.com/LeoVerto)
+ - [Froghut](https://github.com/Froghut)
+ - [fruhnow](https://github.com/fruhnow)
+ - [geilername](https://github.com/geilername)
+ - [gnattu](https://github.com/gnattu)
- [grafixeyehero](https://github.com/grafixeyehero)
- - [cvium](https://github.com/cvium)
- - [wtayl0r](https://github.com/wtayl0r)
- - [TtheCreator](https://github.com/Tthecreator)
- - [dkanada](https://github.com/dkanada)
- - [LogicalPhallacy](https://github.com/LogicalPhallacy/)
- - [RazeLighter777](https://github.com/RazeLighter777)
- - [WillWill56](https://github.com/WillWill56)
+ - [h1nk](https://github.com/h1nk)
+ - [hawken93](https://github.com/hawken93)
+ - [HelloWorld017](https://github.com/HelloWorld017)
+ - [jftuga](https://github.com/jftuga)
+ - [joern-h](https://github.com/joern-h)
+ - [joshuaboniface](https://github.com/joshuaboniface)
+ - [JustAMan](https://github.com/JustAMan)
+ - [justinfenn](https://github.com/justinfenn)
+ - [KerryRJ](https://github.com/KerryRJ)
+ - [Larvitar](https://github.com/Larvitar)
+ - [LeoVerto](https://github.com/LeoVerto)
- [Liggy](https://github.com/Liggy)
- - [fruhnow](https://github.com/fruhnow)
+ - [LogicalPhallacy](https://github.com/LogicalPhallacy)
+ - [loli10K](https://github.com/loli10K)
+ - [lostmypillow](https://github.com/lostmypillow)
- [Lynxy](https://github.com/Lynxy)
- - [fasheng](https://github.com/fasheng)
- - [ploughpuff](https://github.com/ploughpuff)
- - [pjeanjean](https://github.com/pjeanjean)
- - [DrPandemic](https://github.com/drpandemic)
- - [joern-h](https://github.com/joern-h)
- - [Khinenw](https://github.com/HelloWorld017)
- - [fhriley](https://github.com/fhriley)
- - [nevado](https://github.com/nevado)
+ - [ManfredRichthofen](https://github.com/ManfredRichthofen)
+ - [Marenz](https://github.com/Marenz)
+ - [marius-luca-87](https://github.com/marius-luca-87)
- [mark-monteiro](https://github.com/mark-monteiro)
- - [ullmie02](https://github.com/ullmie02)
+ - [Matt07211](https://github.com/Matt07211)
+ - [mcarlton00](https://github.com/mcarlton00)
+ - [mitchfizz05](https://github.com/mitchfizz05)
+ - [MrTimscampi](https://github.com/MrTimscampi)
+ - [n8225](https://github.com/n8225)
+ - [Narfinger](https://github.com/Narfinger)
+ - [NathanPickard](https://github.com/NathanPickard)
+ - [neilsb](https://github.com/neilsb)
+ - [nevado](https://github.com/nevado)
+ - [Nickbert7](https://github.com/Nickbert7)
+ - [nvllsvm](https://github.com/nvllsvm)
+ - [nyanmisaka](https://github.com/nyanmisaka)
+ - [oddstr13](https://github.com/oddstr13)
+ - [petermcneil](https://github.com/petermcneil)
+ - [Phlogi](https://github.com/Phlogi)
+ - [pjeanjean](https://github.com/pjeanjean)
+ - [ploughpuff](https://github.com/ploughpuff)
+ - [pR0Ps](https://github.com/pR0Ps)
+ - [PrplHaz4](https://github.com/PrplHaz4)
+ - [RazeLighter777](https://github.com/RazeLighter777)
+ - [redSpoutnik](https://github.com/redSpoutnik)
+ - [ringmatter](https://github.com/ringmatter)
+ - [ryan-hartzell](https://github.com/ryan-hartzell)
+ - [s0urcelab](https://github.com/s0urcelab)
+ - [sachk](https://github.com/sachk)
+ - [sammyrc34](https://github.com/sammyrc34)
+ - [samuel9554](https://github.com/samuel9554)
+ - [scheidleon](https://github.com/scheidleon)
+ - [sebPomme](https://github.com/sebPomme)
+ - [SegiH](https://github.com/SegiH)
+ - [SenorSmartyPants](https://github.com/SenorSmartyPants)
+ - [shemanaev](https://github.com/shemanaev)
+ - [skaro13](https://github.com/skaro13)
+ - [sl1288](https://github.com/sl1288)
+ - [sorinyo2004](https://github.com/sorinyo2004)
+ - [sparky8251](https://github.com/sparky8251)
+ - [stanionascu](https://github.com/stanionascu)
+ - [stevehayles](https://github.com/stevehayles)
+ - [SuperSandro2000](https://github.com/SuperSandro2000)
+ - [tbraeutigam](https://github.com/tbraeutigam)
+ - [teacupx](https://github.com/teacupx)
+ - [Terror-Gene](https://github.com/Terror-Gene)
+ - [ThatNerdyPikachu](https://github.com/ThatNerdyPikachu)
+ - [ThibaultNocchi](https://github.com/ThibaultNocchi)
+ - [thornbill](https://github.com/thornbill)
+ - [ThreeFive-O](https://github.com/ThreeFive-O)
+ - [TrisMcC](https://github.com/TrisMcC)
+ - [trumblejoe](https://github.com/trumblejoe)
+ - [TtheCreator](https://github.com/TtheCreator)
+ - [twinkybot](https://github.com/twinkybot)
+ - [Ullmie02](https://github.com/Ullmie02)
+ - [Unhelpful](https://github.com/Unhelpful)
+ - [viaregio](https://github.com/viaregio)
+ - [vitorsemeano](https://github.com/vitorsemeano)
+ - [voodoos](https://github.com/voodoos)
+ - [whooo](https://github.com/whooo)
+ - [WiiPlayer2](https://github.com/WiiPlayer2)
+ - [WillWill56](https://github.com/WillWill56)
+ - [wtayl0r](https://github.com/wtayl0r)
+ - [Wuerfelbecher](https://github.com/Wuerfelbecher)
+ - [Wunax](https://github.com/Wunax)
+ - [WWWesten](https://github.com/WWWesten)
+ - [WX9yMOXWId](https://github.com/WX9yMOXWId)
+ - [xosdy](https://github.com/xosdy)
+ - [XVicarious](https://github.com/XVicarious)
+ - [YouKnowBlom](https://github.com/YouKnowBlom)
# Emby Contributors
diff --git a/Dockerfile b/Dockerfile
index 53a425262..73ab3d790 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -3,7 +3,7 @@ ARG FFMPEG_VERSION=latest
FROM node:alpine as web-builder
ARG JELLYFIN_WEB_VERSION=master
-RUN apk add curl \
+RUN apk add curl git \
&& curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
&& cd jellyfin-web-* \
&& yarn install \
@@ -14,11 +14,20 @@ FROM mcr.microsoft.com/dotnet/core/sdk:${DOTNET_VERSION}-buster as builder
WORKDIR /repo
COPY . .
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
-RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-x64 "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none"
+# because of changes in docker and systemd we need to not build in parallel at the moment
+# see https://success.docker.com/article/how-to-reserve-resource-temporarily-unavailable-errors-due-to-tasksmax-setting
+RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="/jellyfin" --self-contained --runtime linux-x64 "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none"
FROM jellyfin/ffmpeg:${FFMPEG_VERSION} as ffmpeg
FROM debian:buster-slim
+# https://askubuntu.com/questions/972516/debian-frontend-environment-variable
+ARG DEBIAN_FRONTEND="noninteractive"
+# http://stackoverflow.com/questions/48162574/ddg#49462622
+ARG APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=DontWarn
+# https://github.com/NVIDIA/nvidia-docker/wiki/Installation-(Native-GPU-Support)
+ENV NVIDIA_DRIVER_CAPABILITIES="compute,video,utility"
+
COPY --from=ffmpeg /opt/ffmpeg /opt/ffmpeg
COPY --from=builder /jellyfin /jellyfin
COPY --from=web-builder /dist /jellyfin/jellyfin-web
@@ -29,9 +38,16 @@ COPY --from=web-builder /dist /jellyfin/jellyfin-web
# mesa-va-drivers: needed for VAAPI
RUN apt-get update \
&& apt-get install --no-install-recommends --no-install-suggests -y \
- libfontconfig1 libgomp1 libva-drm2 mesa-va-drivers openssl \
- && apt-get clean autoclean \
- && apt-get autoremove \
+ libfontconfig1 \
+ libgomp1 \
+ libva-drm2 \
+ mesa-va-drivers \
+ openssl \
+ ca-certificates \
+ vainfo \
+ i965-va-driver \
+ && apt-get clean autoclean -y\
+ && apt-get autoremove -y\
&& rm -rf /var/lib/apt/lists/* \
&& mkdir -p /cache /config /media \
&& chmod 777 /cache /config /media \
diff --git a/Dockerfile.arm b/Dockerfile.arm
index 4d8fbb77d..07780e27b 100644
--- a/Dockerfile.arm
+++ b/Dockerfile.arm
@@ -1,3 +1,5 @@
+# DESIGNED FOR BUILDING ON AMD64 ONLY
+#####################################
# Requires binfm_misc registration
# https://github.com/multiarch/qemu-user-static#binfmt_misc-register
ARG DOTNET_VERSION=3.1
@@ -5,7 +7,7 @@ ARG DOTNET_VERSION=3.1
FROM node:alpine as web-builder
ARG JELLYFIN_WEB_VERSION=master
-RUN apk add curl \
+RUN apk add curl git \
&& curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
&& cd jellyfin-web-* \
&& yarn install \
@@ -24,10 +26,36 @@ RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin"
FROM multiarch/qemu-user-static:x86_64-arm as qemu
-FROM debian:stretch-slim-arm32v7
+FROM arm32v7/debian:buster-slim
+
+# https://askubuntu.com/questions/972516/debian-frontend-environment-variable
+ARG DEBIAN_FRONTEND="noninteractive"
+# http://stackoverflow.com/questions/48162574/ddg#49462622
+ARG APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=DontWarn
+# https://github.com/NVIDIA/nvidia-docker/wiki/Installation-(Native-GPU-Support)
+ENV NVIDIA_DRIVER_CAPABILITIES="compute,video,utility"
+
COPY --from=qemu /usr/bin/qemu-arm-static /usr/bin
RUN apt-get update \
- && apt-get install --no-install-recommends --no-install-suggests -y ffmpeg \
+ && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates gnupg curl && \
+ curl -ks https://repo.jellyfin.org/debian/jellyfin_team.gpg.key | apt-key add - && \
+ curl -s https://keyserver.ubuntu.com/pks/lookup?op=get\&search=0x6587ffd6536b8826e88a62547876ae518cbcf2f2 | apt-key add - && \
+ echo 'deb [arch=armhf] https://repo.jellyfin.org/debian buster main' > /etc/apt/sources.list.d/jellyfin.list && \
+ echo "deb http://ppa.launchpad.net/ubuntu-raspi2/ppa/ubuntu bionic main">> /etc/apt/sources.list.d/raspbins.list && \
+ apt-get update && \
+ apt-get install --no-install-recommends --no-install-suggests -y \
+ jellyfin-ffmpeg \
+ libssl-dev \
+ libfontconfig1 \
+ libfreetype6 \
+ libomxil-bellagio0 \
+ libomxil-bellagio-bin \
+ libraspberrypi0 \
+ vainfo \
+ libva2 \
+ && apt-get remove curl gnupg -y \
+ && apt-get clean autoclean -y \
+ && apt-get autoremove -y \
&& rm -rf /var/lib/apt/lists/* \
&& mkdir -p /cache /config /media \
&& chmod 777 /cache /config /media
@@ -41,4 +69,4 @@ VOLUME /cache /config /media
ENTRYPOINT ["./jellyfin/jellyfin", \
"--datadir", "/config", \
"--cachedir", "/cache", \
- "--ffmpeg", "/usr/bin/ffmpeg"]
+ "--ffmpeg", "/usr/lib/jellyfin-ffmpeg"]
diff --git a/Dockerfile.arm64 b/Dockerfile.arm64
index d41268f13..9dc6fa7ed 100644
--- a/Dockerfile.arm64
+++ b/Dockerfile.arm64
@@ -1,3 +1,5 @@
+# DESIGNED FOR BUILDING ON AMD64 ONLY
+#####################################
# Requires binfm_misc registration
# https://github.com/multiarch/qemu-user-static#binfmt_misc-register
ARG DOTNET_VERSION=3.1
@@ -5,7 +7,7 @@ ARG DOTNET_VERSION=3.1
FROM node:alpine as web-builder
ARG JELLYFIN_WEB_VERSION=master
-RUN apk add curl \
+RUN apk add curl git \
&& curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
&& cd jellyfin-web-* \
&& yarn install \
@@ -22,12 +24,27 @@ RUN find . -type d -name obj | xargs -r rm -r
# Build
RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-arm64 "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none"
-
FROM multiarch/qemu-user-static:x86_64-aarch64 as qemu
-FROM debian:stretch-slim-arm64v8
+FROM arm64v8/debian:buster-slim
+
+# https://askubuntu.com/questions/972516/debian-frontend-environment-variable
+ARG DEBIAN_FRONTEND="noninteractive"
+# http://stackoverflow.com/questions/48162574/ddg#49462622
+ARG APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=DontWarn
+# https://github.com/NVIDIA/nvidia-docker/wiki/Installation-(Native-GPU-Support)
+ENV NVIDIA_DRIVER_CAPABILITIES="compute,video,utility"
+
COPY --from=qemu /usr/bin/qemu-aarch64-static /usr/bin
-RUN apt-get update \
- && apt-get install --no-install-recommends --no-install-suggests -y ffmpeg \
+RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y \
+ ffmpeg \
+ libssl-dev \
+ ca-certificates \
+ libfontconfig1 \
+ libfreetype6 \
+ libomxil-bellagio0 \
+ libomxil-bellagio-bin \
+ && apt-get clean autoclean -y \
+ && apt-get autoremove -y \
&& rm -rf /var/lib/apt/lists/* \
&& mkdir -p /cache /config /media \
&& chmod 777 /cache /config /media
diff --git a/DvdLib/DvdLib.csproj b/DvdLib/DvdLib.csproj
index 9dbaa9e2f..f4df6a9f5 100644
--- a/DvdLib/DvdLib.csproj
+++ b/DvdLib/DvdLib.csproj
@@ -9,7 +9,7 @@
</ItemGroup>
<PropertyGroup>
- <TargetFramework>netstandard2.0</TargetFramework>
+ <TargetFramework>netstandard2.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
diff --git a/DvdLib/Ifo/Dvd.cs b/DvdLib/Ifo/Dvd.cs
index 90125fa3e..157b2e197 100644
--- a/DvdLib/Ifo/Dvd.cs
+++ b/DvdLib/Ifo/Dvd.cs
@@ -42,7 +42,7 @@ namespace DvdLib.Ifo
}
else
{
- using (var vmgFs = _fileSystem.GetFileStream(vmgPath.FullName, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read))
+ using (var vmgFs = new FileStream(vmgPath.FullName, FileMode.Open, FileAccess.Read, FileShare.Read))
{
using (var vmgRead = new BigEndianBinaryReader(vmgFs))
{
@@ -95,7 +95,7 @@ namespace DvdLib.Ifo
{
VTSPaths[vtsNum] = vtsPath;
- using (var vtsFs = _fileSystem.GetFileStream(vtsPath, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read))
+ using (var vtsFs = new FileStream(vtsPath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
using (var vtsRead = new BigEndianBinaryReader(vtsFs))
{
diff --git a/Emby.Dlna/Api/DlnaServerService.cs b/Emby.Dlna/Api/DlnaServerService.cs
index a451bbcf9..b7d018921 100644
--- a/Emby.Dlna/Api/DlnaServerService.cs
+++ b/Emby.Dlna/Api/DlnaServerService.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.IO;
using System.Text;
@@ -170,32 +172,32 @@ namespace Emby.Dlna.Api
return _resultFactory.GetResult(Request, xml, XMLContentType);
}
- public object Post(ProcessMediaReceiverRegistrarControlRequest request)
+ public async Task<object> Post(ProcessMediaReceiverRegistrarControlRequest request)
{
- var response = PostAsync(request.RequestStream, MediaReceiverRegistrar);
+ var response = await PostAsync(request.RequestStream, MediaReceiverRegistrar).ConfigureAwait(false);
return _resultFactory.GetResult(Request, response.Xml, XMLContentType);
}
- public object Post(ProcessContentDirectoryControlRequest request)
+ public async Task<object> Post(ProcessContentDirectoryControlRequest request)
{
- var response = PostAsync(request.RequestStream, ContentDirectory);
+ var response = await PostAsync(request.RequestStream, ContentDirectory).ConfigureAwait(false);
return _resultFactory.GetResult(Request, response.Xml, XMLContentType);
}
- public object Post(ProcessConnectionManagerControlRequest request)
+ public async Task<object> Post(ProcessConnectionManagerControlRequest request)
{
- var response = PostAsync(request.RequestStream, ConnectionManager);
+ var response = await PostAsync(request.RequestStream, ConnectionManager).ConfigureAwait(false);
return _resultFactory.GetResult(Request, response.Xml, XMLContentType);
}
- private ControlResponse PostAsync(Stream requestStream, IUpnpService service)
+ private Task<ControlResponse> PostAsync(Stream requestStream, IUpnpService service)
{
var id = GetPathValue(2).ToString();
- return service.ProcessControlRequest(new ControlRequest
+ return service.ProcessControlRequestAsync(new ControlRequest
{
Headers = Request.Headers,
InputXml = requestStream,
diff --git a/Emby.Dlna/Api/DlnaService.cs b/Emby.Dlna/Api/DlnaService.cs
index 7f51f477a..7d6b8f78e 100644
--- a/Emby.Dlna/Api/DlnaService.cs
+++ b/Emby.Dlna/Api/DlnaService.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Linq;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Net;
diff --git a/Emby.Dlna/Common/Argument.cs b/Emby.Dlna/Common/Argument.cs
index 3e325c41c..f375e6049 100644
--- a/Emby.Dlna/Common/Argument.cs
+++ b/Emby.Dlna/Common/Argument.cs
@@ -1,3 +1,4 @@
+#pragma warning disable CS1591
namespace Emby.Dlna.Common
{
diff --git a/Emby.Dlna/Common/DeviceIcon.cs b/Emby.Dlna/Common/DeviceIcon.cs
index 3a91b952e..c3f7fa8aa 100644
--- a/Emby.Dlna/Common/DeviceIcon.cs
+++ b/Emby.Dlna/Common/DeviceIcon.cs
@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+
+using System.Globalization;
namespace Emby.Dlna.Common
{
@@ -13,9 +16,14 @@ namespace Emby.Dlna.Common
public string Depth { get; set; }
+ /// <inheritdoc />
public override string ToString()
{
- return string.Format("{0}x{1}", Height, Width);
+ return string.Format(
+ CultureInfo.InvariantCulture,
+ "{0}x{1}",
+ Height,
+ Width);
}
}
}
diff --git a/Emby.Dlna/Common/DeviceService.cs b/Emby.Dlna/Common/DeviceService.cs
index c60d65291..44c0a0412 100644
--- a/Emby.Dlna/Common/DeviceService.cs
+++ b/Emby.Dlna/Common/DeviceService.cs
@@ -1,3 +1,4 @@
+#pragma warning disable CS1591
namespace Emby.Dlna.Common
{
@@ -13,9 +14,8 @@ namespace Emby.Dlna.Common
public string EventSubUrl { get; set; }
+ /// <inheritdoc />
public override string ToString()
- {
- return string.Format("{0}", ServiceId);
- }
+ => ServiceId;
}
}
diff --git a/Emby.Dlna/Common/ServiceAction.cs b/Emby.Dlna/Common/ServiceAction.cs
index 5e030d396..db4f27063 100644
--- a/Emby.Dlna/Common/ServiceAction.cs
+++ b/Emby.Dlna/Common/ServiceAction.cs
@@ -1,21 +1,24 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
namespace Emby.Dlna.Common
{
public class ServiceAction
{
+ public ServiceAction()
+ {
+ ArgumentList = new List<Argument>();
+ }
+
public string Name { get; set; }
public List<Argument> ArgumentList { get; set; }
+ /// <inheritdoc />
public override string ToString()
{
return Name;
}
-
- public ServiceAction()
- {
- ArgumentList = new List<Argument>();
- }
}
}
diff --git a/Emby.Dlna/Common/StateVariable.cs b/Emby.Dlna/Common/StateVariable.cs
index 4ca84bf51..a2c2bf5dd 100644
--- a/Emby.Dlna/Common/StateVariable.cs
+++ b/Emby.Dlna/Common/StateVariable.cs
@@ -1,9 +1,16 @@
+#pragma warning disable CS1591
+
using System;
namespace Emby.Dlna.Common
{
public class StateVariable
{
+ public StateVariable()
+ {
+ AllowedValues = Array.Empty<string>();
+ }
+
public string Name { get; set; }
public string DataType { get; set; }
@@ -12,14 +19,8 @@ namespace Emby.Dlna.Common
public string[] AllowedValues { get; set; }
+ /// <inheritdoc />
public override string ToString()
- {
- return Name;
- }
-
- public StateVariable()
- {
- AllowedValues = Array.Empty<string>();
- }
+ => Name;
}
}
diff --git a/Emby.Dlna/Configuration/DlnaOptions.cs b/Emby.Dlna/Configuration/DlnaOptions.cs
index c7cb364a8..6dd9a445a 100644
--- a/Emby.Dlna/Configuration/DlnaOptions.cs
+++ b/Emby.Dlna/Configuration/DlnaOptions.cs
@@ -1,17 +1,9 @@
+#pragma warning disable CS1591
namespace Emby.Dlna.Configuration
{
public class DlnaOptions
{
- public bool EnablePlayTo { get; set; }
- public bool EnableServer { get; set; }
- public bool EnableDebugLog { get; set; }
- public bool BlastAliveMessages { get; set; }
- public bool SendOnlyMatchedHost { get; set; }
- public int ClientDiscoveryIntervalSeconds { get; set; }
- public int BlastAliveMessageIntervalSeconds { get; set; }
- public string DefaultUserId { get; set; }
-
public DlnaOptions()
{
EnablePlayTo = true;
@@ -21,5 +13,21 @@ namespace Emby.Dlna.Configuration
ClientDiscoveryIntervalSeconds = 60;
BlastAliveMessageIntervalSeconds = 1800;
}
+
+ public bool EnablePlayTo { get; set; }
+
+ public bool EnableServer { get; set; }
+
+ public bool EnableDebugLog { get; set; }
+
+ public bool BlastAliveMessages { get; set; }
+
+ public bool SendOnlyMatchedHost { get; set; }
+
+ public int ClientDiscoveryIntervalSeconds { get; set; }
+
+ public int BlastAliveMessageIntervalSeconds { get; set; }
+
+ public string DefaultUserId { get; set; }
}
}
diff --git a/Emby.Dlna/ConfigurationExtension.cs b/Emby.Dlna/ConfigurationExtension.cs
index 82d726e01..e224d10bd 100644
--- a/Emby.Dlna/ConfigurationExtension.cs
+++ b/Emby.Dlna/ConfigurationExtension.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
using Emby.Dlna.Configuration;
using MediaBrowser.Common.Configuration;
diff --git a/Emby.Dlna/ConnectionManager/ConnectionManager.cs b/Emby.Dlna/ConnectionManager/ConnectionManager.cs
index 83011fbab..e32cc11bf 100644
--- a/Emby.Dlna/ConnectionManager/ConnectionManager.cs
+++ b/Emby.Dlna/ConnectionManager/ConnectionManager.cs
@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+
+using System.Threading.Tasks;
using Emby.Dlna.Service;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
@@ -12,7 +15,11 @@ namespace Emby.Dlna.ConnectionManager
private readonly ILogger _logger;
private readonly IServerConfigurationManager _config;
- public ConnectionManager(IDlnaManager dlna, IServerConfigurationManager config, ILogger logger, IHttpClient httpClient)
+ public ConnectionManager(
+ IDlnaManager dlna,
+ IServerConfigurationManager config,
+ ILogger<ConnectionManager> logger,
+ IHttpClient httpClient)
: base(logger, httpClient)
{
_dlna = dlna;
@@ -20,17 +27,19 @@ namespace Emby.Dlna.ConnectionManager
_logger = logger;
}
+ /// <inheritdoc />
public string GetServiceXml()
{
return new ConnectionManagerXmlBuilder().GetXml();
}
- public ControlResponse ProcessControlRequest(ControlRequest request)
+ /// <inheritdoc />
+ public Task<ControlResponse> ProcessControlRequestAsync(ControlRequest request)
{
var profile = _dlna.GetProfile(request.Headers) ??
_dlna.GetDefaultProfile();
- return new ControlHandler(_config, _logger, profile).ProcessControlRequest(request);
+ return new ControlHandler(_config, _logger, profile).ProcessControlRequestAsync(request);
}
}
}
diff --git a/Emby.Dlna/ConnectionManager/ConnectionManagerXmlBuilder.cs b/Emby.Dlna/ConnectionManager/ConnectionManagerXmlBuilder.cs
index f5873455a..b31d437c3 100644
--- a/Emby.Dlna/ConnectionManager/ConnectionManagerXmlBuilder.cs
+++ b/Emby.Dlna/ConnectionManager/ConnectionManagerXmlBuilder.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
using Emby.Dlna.Common;
using Emby.Dlna.Service;
diff --git a/Emby.Dlna/ConnectionManager/ControlHandler.cs b/Emby.Dlna/ConnectionManager/ControlHandler.cs
index 2e1104748..d4cc65394 100644
--- a/Emby.Dlna/ConnectionManager/ControlHandler.cs
+++ b/Emby.Dlna/ConnectionManager/ControlHandler.cs
@@ -1,5 +1,8 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
+using System.Xml;
using Emby.Dlna.Service;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration;
@@ -12,29 +15,28 @@ namespace Emby.Dlna.ConnectionManager
{
private readonly DeviceProfile _profile;
- protected override IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, IDictionary<string, string> methodParams)
+ public ControlHandler(IServerConfigurationManager config, ILogger logger, DeviceProfile profile)
+ : base(config, logger)
+ {
+ _profile = profile;
+ }
+
+ /// <inheritdoc />
+ protected override void WriteResult(string methodName, IDictionary<string, string> methodParams, XmlWriter xmlWriter)
{
if (string.Equals(methodName, "GetProtocolInfo", StringComparison.OrdinalIgnoreCase))
{
- return HandleGetProtocolInfo();
+ HandleGetProtocolInfo(xmlWriter);
+ return;
}
throw new ResourceNotFoundException("Unexpected control request name: " + methodName);
}
- private IEnumerable<KeyValuePair<string, string>> HandleGetProtocolInfo()
+ private void HandleGetProtocolInfo(XmlWriter xmlWriter)
{
- return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
- {
- { "Source", _profile.ProtocolInfo },
- { "Sink", "" }
- };
- }
-
- public ControlHandler(IServerConfigurationManager config, ILogger logger, DeviceProfile profile)
- : base(config, logger)
- {
- _profile = profile;
+ xmlWriter.WriteElementString("Source", _profile.ProtocolInfo);
+ xmlWriter.WriteElementString("Sink", string.Empty);
}
}
}
diff --git a/Emby.Dlna/ConnectionManager/ServiceActionListBuilder.cs b/Emby.Dlna/ConnectionManager/ServiceActionListBuilder.cs
index b7727b558..b853e7eab 100644
--- a/Emby.Dlna/ConnectionManager/ServiceActionListBuilder.cs
+++ b/Emby.Dlna/ConnectionManager/ServiceActionListBuilder.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
using Emby.Dlna.Common;
diff --git a/Emby.Dlna/ContentDirectory/ContentDirectory.cs b/Emby.Dlna/ContentDirectory/ContentDirectory.cs
index 78d69b338..64cd308a2 100644
--- a/Emby.Dlna/ContentDirectory/ContentDirectory.cs
+++ b/Emby.Dlna/ContentDirectory/ContentDirectory.cs
@@ -1,4 +1,7 @@
+#pragma warning disable CS1591
+
using System;
+using System.Threading.Tasks;
using Emby.Dlna.Service;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
@@ -34,7 +37,7 @@ namespace Emby.Dlna.ContentDirectory
ILibraryManager libraryManager,
IServerConfigurationManager config,
IUserManager userManager,
- ILogger logger,
+ ILogger<ContentDirectory> logger,
IHttpClient httpClient,
ILocalizationManager localization,
IMediaSourceManager mediaSourceManager,
@@ -66,12 +69,14 @@ namespace Emby.Dlna.ContentDirectory
}
}
+ /// <inheritdoc />
public string GetServiceXml()
{
return new ContentDirectoryXmlBuilder().GetXml();
}
- public ControlResponse ProcessControlRequest(ControlRequest request)
+ /// <inheritdoc />
+ public Task<ControlResponse> ProcessControlRequestAsync(ControlRequest request)
{
var profile = _dlna.GetProfile(request.Headers) ??
_dlna.GetDefaultProfile();
@@ -96,7 +101,7 @@ namespace Emby.Dlna.ContentDirectory
_userViewManager,
_mediaEncoder,
_tvSeriesManager)
- .ProcessControlRequest(request);
+ .ProcessControlRequestAsync(request);
}
private User GetUser(DeviceProfile profile)
diff --git a/Emby.Dlna/ContentDirectory/ContentDirectoryXmlBuilder.cs b/Emby.Dlna/ContentDirectory/ContentDirectoryXmlBuilder.cs
index 15fdb36c4..6db4d7cb6 100644
--- a/Emby.Dlna/ContentDirectory/ContentDirectoryXmlBuilder.cs
+++ b/Emby.Dlna/ContentDirectory/ContentDirectoryXmlBuilder.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
using Emby.Dlna.Common;
using Emby.Dlna.Service;
diff --git a/Emby.Dlna/ContentDirectory/ControlHandler.cs b/Emby.Dlna/ContentDirectory/ControlHandler.cs
index 4f74bb222..41f4fbbd3 100644
--- a/Emby.Dlna/ContentDirectory/ControlHandler.cs
+++ b/Emby.Dlna/ContentDirectory/ControlHandler.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -44,7 +46,6 @@ namespace Emby.Dlna.ContentDirectory
private const string NS_UPNP = "urn:schemas-upnp-org:metadata-1-0/upnp/";
private readonly int _systemUpdateId;
- private readonly CultureInfo _usCulture = new CultureInfo("en-US");
private readonly DidlBuilder _didlBuilder;
@@ -58,7 +59,8 @@ namespace Emby.Dlna.ContentDirectory
string accessToken,
IImageProcessor imageProcessor,
IUserDataManager userDataManager,
- User user, int systemUpdateId,
+ User user,
+ int systemUpdateId,
IServerConfigurationManager config,
ILocalizationManager localization,
IMediaSourceManager mediaSourceManager,
@@ -76,117 +78,132 @@ namespace Emby.Dlna.ContentDirectory
_profile = profile;
_config = config;
- _didlBuilder = new DidlBuilder(profile, user, imageProcessor, serverAddress, accessToken, userDataManager, localization, mediaSourceManager, _logger, mediaEncoder);
+ _didlBuilder = new DidlBuilder(profile, user, imageProcessor, serverAddress, accessToken, userDataManager, localization, mediaSourceManager, Logger, mediaEncoder);
}
- protected override IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, IDictionary<string, string> methodParams)
+ /// <inheritdoc />
+ protected override void WriteResult(string methodName, IDictionary<string, string> methodParams, XmlWriter xmlWriter)
{
- var deviceId = "test";
-
- var user = _user;
+ const string DeviceId = "test";
if (string.Equals(methodName, "GetSearchCapabilities", StringComparison.OrdinalIgnoreCase))
- return HandleGetSearchCapabilities();
+ {
+ HandleGetSearchCapabilities(xmlWriter);
+ return;
+ }
if (string.Equals(methodName, "GetSortCapabilities", StringComparison.OrdinalIgnoreCase))
- return HandleGetSortCapabilities();
+ {
+ HandleGetSortCapabilities(xmlWriter);
+ return;
+ }
if (string.Equals(methodName, "GetSortExtensionCapabilities", StringComparison.OrdinalIgnoreCase))
- return HandleGetSortExtensionCapabilities();
+ {
+ HandleGetSortExtensionCapabilities(xmlWriter);
+ return;
+ }
if (string.Equals(methodName, "GetSystemUpdateID", StringComparison.OrdinalIgnoreCase))
- return HandleGetSystemUpdateID();
+ {
+ HandleGetSystemUpdateID(xmlWriter);
+ return;
+ }
if (string.Equals(methodName, "Browse", StringComparison.OrdinalIgnoreCase))
- return HandleBrowse(methodParams, user, deviceId);
+ {
+ HandleBrowse(xmlWriter, methodParams, DeviceId);
+ return;
+ }
if (string.Equals(methodName, "X_GetFeatureList", StringComparison.OrdinalIgnoreCase))
- return HandleXGetFeatureList();
+ {
+ HandleXGetFeatureList(xmlWriter);
+ return;
+ }
if (string.Equals(methodName, "GetFeatureList", StringComparison.OrdinalIgnoreCase))
- return HandleGetFeatureList();
+ {
+ HandleGetFeatureList(xmlWriter);
+ return;
+ }
if (string.Equals(methodName, "X_SetBookmark", StringComparison.OrdinalIgnoreCase))
- return HandleXSetBookmark(methodParams, user);
+ {
+ HandleXSetBookmark(methodParams);
+ return;
+ }
if (string.Equals(methodName, "Search", StringComparison.OrdinalIgnoreCase))
- return HandleSearch(methodParams, user, deviceId);
+ {
+ HandleSearch(xmlWriter, methodParams, DeviceId);
+ return;
+ }
if (string.Equals(methodName, "X_BrowseByLetter", StringComparison.OrdinalIgnoreCase))
- return HandleX_BrowseByLetter(methodParams, user, deviceId);
+ {
+ HandleXBrowseByLetter(xmlWriter, methodParams, DeviceId);
+ return;
+ }
throw new ResourceNotFoundException("Unexpected control request name: " + methodName);
}
- private IEnumerable<KeyValuePair<string, string>> HandleXSetBookmark(IDictionary<string, string> sparams, User user)
+ private void HandleXSetBookmark(IDictionary<string, string> sparams)
{
var id = sparams["ObjectID"];
- var serverItem = GetItemFromObjectId(id, user);
+ var serverItem = GetItemFromObjectId(id, _user);
var item = serverItem.Item;
- var newbookmark = int.Parse(sparams["PosSecond"], _usCulture);
+ var newbookmark = int.Parse(sparams["PosSecond"], CultureInfo.InvariantCulture);
- var userdata = _userDataManager.GetUserData(user, item);
+ var userdata = _userDataManager.GetUserData(_user, item);
userdata.PlaybackPositionTicks = TimeSpan.FromSeconds(newbookmark).Ticks;
- _userDataManager.SaveUserData(user, item, userdata, UserDataSaveReason.TogglePlayed,
+ _userDataManager.SaveUserData(_user, item, userdata, UserDataSaveReason.TogglePlayed,
CancellationToken.None);
-
- return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
}
- private IEnumerable<KeyValuePair<string, string>> HandleGetSearchCapabilities()
+ private void HandleGetSearchCapabilities(XmlWriter xmlWriter)
{
- return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
- {
- { "SearchCaps", "res@resolution,res@size,res@duration,dc:title,dc:creator,upnp:actor,upnp:artist,upnp:genre,upnp:album,dc:date,upnp:class,@id,@refID,@protocolInfo,upnp:author,dc:description,pv:avKeywords" }
- };
+ xmlWriter.WriteElementString(
+ "SearchCaps",
+ "res@resolution,res@size,res@duration,dc:title,dc:creator,upnp:actor,upnp:artist,upnp:genre,upnp:album,dc:date,upnp:class,@id,@refID,@protocolInfo,upnp:author,dc:description,pv:avKeywords");
}
- private IEnumerable<KeyValuePair<string, string>> HandleGetSortCapabilities()
+ private void HandleGetSortCapabilities(XmlWriter xmlWriter)
{
- return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
- {
- { "SortCaps", "res@duration,res@size,res@bitrate,dc:date,dc:title,dc:size,upnp:album,upnp:artist,upnp:albumArtist,upnp:episodeNumber,upnp:genre,upnp:originalTrackNumber,upnp:rating" }
- };
+ xmlWriter.WriteElementString(
+ "SortCaps",
+ "res@duration,res@size,res@bitrate,dc:date,dc:title,dc:size,upnp:album,upnp:artist,upnp:albumArtist,upnp:episodeNumber,upnp:genre,upnp:originalTrackNumber,upnp:rating");
}
- private IEnumerable<KeyValuePair<string, string>> HandleGetSortExtensionCapabilities()
+ private void HandleGetSortExtensionCapabilities(XmlWriter xmlWriter)
{
- return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
- {
- { "SortExtensionCaps", "res@duration,res@size,res@bitrate,dc:date,dc:title,dc:size,upnp:album,upnp:artist,upnp:albumArtist,upnp:episodeNumber,upnp:genre,upnp:originalTrackNumber,upnp:rating" }
- };
+ xmlWriter.WriteElementString(
+ "SortExtensionCaps",
+ "res@duration,res@size,res@bitrate,dc:date,dc:title,dc:size,upnp:album,upnp:artist,upnp:albumArtist,upnp:episodeNumber,upnp:genre,upnp:originalTrackNumber,upnp:rating");
}
- private IEnumerable<KeyValuePair<string, string>> HandleGetSystemUpdateID()
+ private void HandleGetSystemUpdateID(XmlWriter xmlWriter)
{
- var headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- headers.Add("Id", _systemUpdateId.ToString(_usCulture));
- return headers;
+ xmlWriter.WriteElementString("Id", _systemUpdateId.ToString(CultureInfo.InvariantCulture));
}
- private IEnumerable<KeyValuePair<string, string>> HandleGetFeatureList()
+ private void HandleGetFeatureList(XmlWriter xmlWriter)
{
- return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
- {
- { "FeatureList", GetFeatureListXml() }
- };
+ xmlWriter.WriteElementString("FeatureList", WriteFeatureListXml());
}
- private IEnumerable<KeyValuePair<string, string>> HandleXGetFeatureList()
- {
- return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
- {
- { "FeatureList", GetFeatureListXml() }
- };
- }
+ private void HandleXGetFeatureList(XmlWriter xmlWriter)
+ => HandleGetFeatureList(xmlWriter);
- private string GetFeatureListXml()
+ private string WriteFeatureListXml()
{
+ // TODO: clean this up
var builder = new StringBuilder();
builder.Append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
@@ -213,7 +230,7 @@ namespace Emby.Dlna.ContentDirectory
return defaultValue;
}
- private IEnumerable<KeyValuePair<string, string>> HandleBrowse(IDictionary<string, string> sparams, User user, string deviceId)
+ private void HandleBrowse(XmlWriter xmlWriter, IDictionary<string, string> sparams, string deviceId)
{
var id = sparams["ObjectID"];
var flag = sparams["BrowseFlag"];
@@ -237,101 +254,95 @@ namespace Emby.Dlna.ContentDirectory
start = startVal;
}
- var settings = new XmlWriterSettings
- {
- Encoding = Encoding.UTF8,
- CloseOutput = false,
- OmitXmlDeclaration = true,
- ConformanceLevel = ConformanceLevel.Fragment
- };
-
- StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8);
-
int totalCount;
- var dlnaOptions = _config.GetDlnaConfiguration();
-
- using (var writer = XmlWriter.Create(builder, settings))
+ using (StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8))
{
- //writer.WriteStartDocument();
-
- writer.WriteStartElement(string.Empty, "DIDL-Lite", NS_DIDL);
-
- writer.WriteAttributeString("xmlns", "dc", null, NS_DC);
- writer.WriteAttributeString("xmlns", "dlna", null, NS_DLNA);
- writer.WriteAttributeString("xmlns", "upnp", null, NS_UPNP);
- //didl.SetAttribute("xmlns:sec", NS_SEC);
-
- DidlBuilder.WriteXmlRootAttributes(_profile, writer);
-
- var serverItem = GetItemFromObjectId(id, user);
- var item = serverItem.Item;
+ var settings = new XmlWriterSettings()
+ {
+ Encoding = Encoding.UTF8,
+ CloseOutput = false,
+ OmitXmlDeclaration = true,
+ ConformanceLevel = ConformanceLevel.Fragment
+ };
- if (string.Equals(flag, "BrowseMetadata"))
+ using (var writer = XmlWriter.Create(builder, settings))
{
- totalCount = 1;
+ writer.WriteStartElement(string.Empty, "DIDL-Lite", NS_DIDL);
- if (item.IsDisplayedAsFolder || serverItem.StubType.HasValue)
- {
- var childrenResult = GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requestedCount);
+ writer.WriteAttributeString("xmlns", "dc", null, NS_DC);
+ writer.WriteAttributeString("xmlns", "dlna", null, NS_DLNA);
+ writer.WriteAttributeString("xmlns", "upnp", null, NS_UPNP);
- _didlBuilder.WriteFolderElement(writer, item, serverItem.StubType, null, childrenResult.TotalRecordCount, filter, id);
- }
- else
- {
- _didlBuilder.WriteItemElement(dlnaOptions, writer, item, user, null, null, deviceId, filter);
- }
+ DidlBuilder.WriteXmlRootAttributes(_profile, writer);
- provided++;
- }
- else
- {
- var childrenResult = GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requestedCount);
- totalCount = childrenResult.TotalRecordCount;
+ var serverItem = GetItemFromObjectId(id, _user);
+ var item = serverItem.Item;
- provided = childrenResult.Items.Count;
- foreach (var i in childrenResult.Items)
+ if (string.Equals(flag, "BrowseMetadata", StringComparison.Ordinal))
{
- var childItem = i.Item;
- var displayStubType = i.StubType;
+ totalCount = 1;
- if (childItem.IsDisplayedAsFolder || displayStubType.HasValue)
+ if (item.IsDisplayedAsFolder || serverItem.StubType.HasValue)
{
- var childCount = (GetUserItems(childItem, displayStubType, user, sortCriteria, null, 0))
- .TotalRecordCount;
+ var childrenResult = GetUserItems(item, serverItem.StubType, _user, sortCriteria, start, requestedCount);
- _didlBuilder.WriteFolderElement(writer, childItem, displayStubType, item, childCount, filter);
+ _didlBuilder.WriteFolderElement(writer, item, serverItem.StubType, null, childrenResult.TotalRecordCount, filter, id);
}
else
{
- _didlBuilder.WriteItemElement(dlnaOptions, writer, childItem, user, item, serverItem.StubType, deviceId, filter);
+ var dlnaOptions = _config.GetDlnaConfiguration();
+ _didlBuilder.WriteItemElement(dlnaOptions, writer, item, _user, null, null, deviceId, filter);
+ }
+
+ provided++;
+ }
+ else
+ {
+ var childrenResult = GetUserItems(item, serverItem.StubType, _user, sortCriteria, start, requestedCount);
+ totalCount = childrenResult.TotalRecordCount;
+
+ provided = childrenResult.Items.Count;
+
+ var dlnaOptions = _config.GetDlnaConfiguration();
+ foreach (var i in childrenResult.Items)
+ {
+ var childItem = i.Item;
+ var displayStubType = i.StubType;
+
+ if (childItem.IsDisplayedAsFolder || displayStubType.HasValue)
+ {
+ var childCount = GetUserItems(childItem, displayStubType, _user, sortCriteria, null, 0)
+ .TotalRecordCount;
+
+ _didlBuilder.WriteFolderElement(writer, childItem, displayStubType, item, childCount, filter);
+ }
+ else
+ {
+ _didlBuilder.WriteItemElement(dlnaOptions, writer, childItem, _user, item, serverItem.StubType, deviceId, filter);
+ }
}
}
+
+ writer.WriteFullEndElement();
}
- writer.WriteFullEndElement();
- //writer.WriteEndDocument();
+ xmlWriter.WriteElementString("Result", builder.ToString());
}
- var resXML = builder.ToString();
-
- return new[]
- {
- new KeyValuePair<string,string>("Result", resXML),
- new KeyValuePair<string,string>("NumberReturned", provided.ToString(_usCulture)),
- new KeyValuePair<string,string>("TotalMatches", totalCount.ToString(_usCulture)),
- new KeyValuePair<string,string>("UpdateID", _systemUpdateId.ToString(_usCulture))
- };
+ xmlWriter.WriteElementString("NumberReturned", provided.ToString(CultureInfo.InvariantCulture));
+ xmlWriter.WriteElementString("TotalMatches", totalCount.ToString(CultureInfo.InvariantCulture));
+ xmlWriter.WriteElementString("UpdateID", _systemUpdateId.ToString(CultureInfo.InvariantCulture));
}
- private IEnumerable<KeyValuePair<string, string>> HandleX_BrowseByLetter(IDictionary<string, string> sparams, User user, string deviceId)
+ private void HandleXBrowseByLetter(XmlWriter xmlWriter, IDictionary<string, string> sparams, string deviceId)
{
// TODO: Implement this method
- return HandleSearch(sparams, user, deviceId);
+ HandleSearch(xmlWriter, sparams, deviceId);
}
- private IEnumerable<KeyValuePair<string, string>> HandleSearch(IDictionary<string, string> sparams, User user, string deviceId)
+ 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", ""));
@@ -354,99 +365,86 @@ namespace Emby.Dlna.ContentDirectory
start = startVal;
}
- var settings = new XmlWriterSettings
- {
- Encoding = Encoding.UTF8,
- CloseOutput = false,
- OmitXmlDeclaration = true,
- ConformanceLevel = ConformanceLevel.Fragment
- };
-
- StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8);
- int totalCount = 0;
- int provided = 0;
+ QueryResult<BaseItem> childrenResult;
- using (var writer = XmlWriter.Create(builder, settings))
+ using (StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8))
{
- //writer.WriteStartDocument();
-
- writer.WriteStartElement(string.Empty, "DIDL-Lite", NS_DIDL);
-
- writer.WriteAttributeString("xmlns", "dc", null, NS_DC);
- writer.WriteAttributeString("xmlns", "dlna", null, NS_DLNA);
- writer.WriteAttributeString("xmlns", "upnp", null, NS_UPNP);
- //didl.SetAttribute("xmlns:sec", NS_SEC);
+ var settings = new XmlWriterSettings()
+ {
+ Encoding = Encoding.UTF8,
+ CloseOutput = false,
+ OmitXmlDeclaration = true,
+ ConformanceLevel = ConformanceLevel.Fragment
+ };
- DidlBuilder.WriteXmlRootAttributes(_profile, writer);
+ using (var writer = XmlWriter.Create(builder, settings))
+ {
+ writer.WriteStartElement(string.Empty, "DIDL-Lite", NS_DIDL);
- var serverItem = GetItemFromObjectId(sparams["ContainerID"], user);
+ writer.WriteAttributeString("xmlns", "dc", null, NS_DC);
+ writer.WriteAttributeString("xmlns", "dlna", null, NS_DLNA);
+ writer.WriteAttributeString("xmlns", "upnp", null, NS_UPNP);
- var item = serverItem.Item;
+ DidlBuilder.WriteXmlRootAttributes(_profile, writer);
- var childrenResult = (GetChildrenSorted(item, user, searchCriteria, sortCriteria, start, requestedCount));
+ var serverItem = GetItemFromObjectId(sparams["ContainerID"], _user);
- totalCount = childrenResult.TotalRecordCount;
+ var item = serverItem.Item;
- provided = childrenResult.Items.Count;
+ childrenResult = GetChildrenSorted(item, _user, searchCriteria, sortCriteria, start, requestedCount);
- var dlnaOptions = _config.GetDlnaConfiguration();
+ var dlnaOptions = _config.GetDlnaConfiguration();
- foreach (var i in childrenResult.Items)
- {
- if (i.IsDisplayedAsFolder)
+ foreach (var i in childrenResult.Items)
{
- var childCount = (GetChildrenSorted(i, user, searchCriteria, sortCriteria, null, 0))
- .TotalRecordCount;
+ if (i.IsDisplayedAsFolder)
+ {
+ var childCount = GetChildrenSorted(i, _user, searchCriteria, sortCriteria, null, 0)
+ .TotalRecordCount;
- _didlBuilder.WriteFolderElement(writer, i, null, item, childCount, filter);
- }
- else
- {
- _didlBuilder.WriteItemElement(dlnaOptions, writer, i, user, item, serverItem.StubType, deviceId, filter);
+ _didlBuilder.WriteFolderElement(writer, i, null, item, childCount, filter);
+ }
+ else
+ {
+ _didlBuilder.WriteItemElement(dlnaOptions, writer, i, _user, item, serverItem.StubType, deviceId, filter);
+ }
}
+
+ writer.WriteFullEndElement();
}
- writer.WriteFullEndElement();
- //writer.WriteEndDocument();
+ xmlWriter.WriteElementString("Result", builder.ToString());
}
- var resXML = builder.ToString();
-
- return new List<KeyValuePair<string, string>>
- {
- new KeyValuePair<string,string>("Result", resXML),
- new KeyValuePair<string,string>("NumberReturned", provided.ToString(_usCulture)),
- new KeyValuePair<string,string>("TotalMatches", totalCount.ToString(_usCulture)),
- new KeyValuePair<string,string>("UpdateID", _systemUpdateId.ToString(_usCulture))
- };
+ xmlWriter.WriteElementString("NumberReturned", childrenResult.Items.Count.ToString(CultureInfo.InvariantCulture));
+ xmlWriter.WriteElementString("TotalMatches", childrenResult.TotalRecordCount.ToString(CultureInfo.InvariantCulture));
+ xmlWriter.WriteElementString("UpdateID", _systemUpdateId.ToString(CultureInfo.InvariantCulture));
}
private QueryResult<BaseItem> GetChildrenSorted(BaseItem item, User user, SearchCriteria search, SortCriteria sort, int? startIndex, int? limit)
{
var folder = (Folder)item;
- var sortOrders = new List<(string, SortOrder)>();
- if (!folder.IsPreSorted)
- {
- sortOrders.Add((ItemSortBy.SortName, sort.SortOrder));
- }
+ var sortOrders = folder.IsPreSorted
+ ? Array.Empty<(string, SortOrder)>()
+ : new[] { (ItemSortBy.SortName, sort.SortOrder) };
- var mediaTypes = new List<string>();
+ string[] mediaTypes = Array.Empty<string>();
bool? isFolder = null;
if (search.SearchType == SearchType.Audio)
{
- mediaTypes.Add(MediaType.Audio);
+ mediaTypes = new[] { MediaType.Audio };
isFolder = false;
}
else if (search.SearchType == SearchType.Video)
{
- mediaTypes.Add(MediaType.Video);
+ mediaTypes = new[] { MediaType.Video };
isFolder = false;
}
else if (search.SearchType == SearchType.Image)
{
- mediaTypes.Add(MediaType.Photo);
+ mediaTypes = new[] { MediaType.Photo };
isFolder = false;
}
else if (search.SearchType == SearchType.Playlist)
@@ -470,7 +468,7 @@ namespace Emby.Dlna.ContentDirectory
IsMissing = false,
ExcludeItemTypes = new[] { typeof(Book).Name },
IsFolder = isFolder,
- MediaTypes = mediaTypes.ToArray(),
+ MediaTypes = mediaTypes,
DtoOptions = GetDtoOptions()
});
}
@@ -771,11 +769,11 @@ namespace Emby.Dlna.ContentDirectory
})
.ToArray();
- return new QueryResult<ServerItem>
+ 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)
@@ -1304,11 +1302,11 @@ namespace Emby.Dlna.ContentDirectory
StubType? stubType = null;
// After using PlayTo, MediaMonkey sends a request to the server trying to get item info
- const string paramsSrch = "Params=";
- var paramsIndex = id.IndexOf(paramsSrch, StringComparison.OrdinalIgnoreCase);
+ const string ParamsSrch = "Params=";
+ var paramsIndex = id.IndexOf(ParamsSrch, StringComparison.OrdinalIgnoreCase);
if (paramsIndex != -1)
{
- id = id.Substring(paramsIndex + paramsSrch.Length);
+ id = id.Substring(paramsIndex + ParamsSrch.Length);
var parts = id.Split(';');
id = parts[23];
@@ -1336,7 +1334,7 @@ namespace Emby.Dlna.ContentDirectory
};
}
- _logger.LogError("Error parsing item Id: {id}. Returning user root folder.", id);
+ Logger.LogError("Error parsing item Id: {id}. Returning user root folder.", id);
return new ServerItem(_libraryManager.GetUserRootFolder());
}
diff --git a/Emby.Dlna/ContentDirectory/ServiceActionListBuilder.cs b/Emby.Dlna/ContentDirectory/ServiceActionListBuilder.cs
index e999314fa..921b14e39 100644
--- a/Emby.Dlna/ContentDirectory/ServiceActionListBuilder.cs
+++ b/Emby.Dlna/ContentDirectory/ServiceActionListBuilder.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
using Emby.Dlna.Common;
diff --git a/Emby.Dlna/ControlRequest.cs b/Emby.Dlna/ControlRequest.cs
index 8c227159c..a6e03b7e6 100644
--- a/Emby.Dlna/ControlRequest.cs
+++ b/Emby.Dlna/ControlRequest.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.IO;
using Microsoft.AspNetCore.Http;
diff --git a/Emby.Dlna/ControlResponse.cs b/Emby.Dlna/ControlResponse.cs
index d2b79fc58..140ef9b46 100644
--- a/Emby.Dlna/ControlResponse.cs
+++ b/Emby.Dlna/ControlResponse.cs
@@ -1,18 +1,20 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
namespace Emby.Dlna
{
public class ControlResponse
{
+ public ControlResponse()
+ {
+ Headers = new Dictionary<string, string>();
+ }
+
public IDictionary<string, string> Headers { get; set; }
public string Xml { get; set; }
public bool IsSuccessful { get; set; }
-
- public ControlResponse()
- {
- Headers = new Dictionary<string, string>();
- }
}
}
diff --git a/Emby.Dlna/Didl/DidlBuilder.cs b/Emby.Dlna/Didl/DidlBuilder.cs
index 85ef9d482..45335f90d 100644
--- a/Emby.Dlna/Didl/DidlBuilder.cs
+++ b/Emby.Dlna/Didl/DidlBuilder.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Globalization;
using System.IO;
@@ -18,7 +20,6 @@ using MediaBrowser.Controller.Playlists;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Net;
using Microsoft.Extensions.Logging;
@@ -632,7 +633,7 @@ namespace Emby.Dlna.Didl
{
if (item.PremiereDate.HasValue)
{
- AddValue(writer, "dc", "date", item.PremiereDate.Value.ToString("o"), NS_DC);
+ AddValue(writer, "dc", "date", item.PremiereDate.Value.ToString("o", CultureInfo.InvariantCulture), NS_DC);
}
}
diff --git a/Emby.Dlna/Didl/Filter.cs b/Emby.Dlna/Didl/Filter.cs
index a0e67870e..f6217d91e 100644
--- a/Emby.Dlna/Didl/Filter.cs
+++ b/Emby.Dlna/Didl/Filter.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using MediaBrowser.Model.Extensions;
@@ -16,7 +18,7 @@ namespace Emby.Dlna.Didl
public Filter(string filter)
{
- _all = StringHelper.EqualsIgnoreCase(filter, "*");
+ _all = string.Equals(filter, "*", StringComparison.OrdinalIgnoreCase);
_fields = (filter ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
}
diff --git a/Emby.Dlna/Didl/StringWriterWithEncoding.cs b/Emby.Dlna/Didl/StringWriterWithEncoding.cs
index c3c4bd393..67fc56ec0 100644
--- a/Emby.Dlna/Didl/StringWriterWithEncoding.cs
+++ b/Emby.Dlna/Didl/StringWriterWithEncoding.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.IO;
using System.Text;
diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs
index d5d788021..10f881fe7 100644
--- a/Emby.Dlna/DlnaManager.cs
+++ b/Emby.Dlna/DlnaManager.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -385,7 +387,7 @@ namespace Emby.Dlna
{
Directory.CreateDirectory(systemProfilesPath);
- using (var fileStream = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
+ using (var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read))
{
await stream.CopyToAsync(fileStream);
}
diff --git a/Emby.Dlna/Emby.Dlna.csproj b/Emby.Dlna/Emby.Dlna.csproj
index 8d6fabdb4..0cabe43d5 100644
--- a/Emby.Dlna/Emby.Dlna.csproj
+++ b/Emby.Dlna/Emby.Dlna.csproj
@@ -15,6 +15,19 @@
<TargetFramework>netstandard2.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
+ <TreatWarningsAsErrors Condition=" '$(Configuration)' == 'Release'" >true</TreatWarningsAsErrors>
+ </PropertyGroup>
+
+ <!-- Code Analyzers-->
+ <ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
+ <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8" PrivateAssets="All" />
+ <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
+ <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" />
+ <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
+ </ItemGroup>
+
+ <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
+ <CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
diff --git a/Emby.Dlna/EventSubscriptionResponse.cs b/Emby.Dlna/EventSubscriptionResponse.cs
index 6dc1aacf4..fd18343e6 100644
--- a/Emby.Dlna/EventSubscriptionResponse.cs
+++ b/Emby.Dlna/EventSubscriptionResponse.cs
@@ -1,17 +1,20 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
namespace Emby.Dlna
{
public class EventSubscriptionResponse
{
- public string Content { get; set; }
- public string ContentType { get; set; }
-
- public Dictionary<string, string> Headers { get; set; }
-
public EventSubscriptionResponse()
{
Headers = new Dictionary<string, string>();
}
+
+ public string Content { get; set; }
+
+ public string ContentType { get; set; }
+
+ public Dictionary<string, string> Headers { get; set; }
}
}
diff --git a/Emby.Dlna/Eventing/EventManager.cs b/Emby.Dlna/Eventing/EventManager.cs
index b76a0066d..efbb53b64 100644
--- a/Emby.Dlna/Eventing/EventManager.cs
+++ b/Emby.Dlna/Eventing/EventManager.cs
@@ -1,8 +1,11 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
+using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using MediaBrowser.Common.Extensions;
@@ -164,7 +167,7 @@ namespace Emby.Dlna.Eventing
try
{
- using (await _httpClient.SendAsync(options, "NOTIFY").ConfigureAwait(false))
+ using (await _httpClient.SendAsync(options, new HttpMethod("NOTIFY")).ConfigureAwait(false))
{
}
diff --git a/Emby.Dlna/Eventing/EventSubscription.cs b/Emby.Dlna/Eventing/EventSubscription.cs
index eb8781e0c..51eaee9d7 100644
--- a/Emby.Dlna/Eventing/EventSubscription.cs
+++ b/Emby.Dlna/Eventing/EventSubscription.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace Emby.Dlna.Eventing
@@ -13,6 +15,8 @@ namespace Emby.Dlna.Eventing
public long TriggerCount { get; set; }
+ public bool IsExpired => SubscriptionTime.AddSeconds(TimeoutSeconds) >= DateTime.UtcNow;
+
public void IncrementTriggerCount()
{
if (TriggerCount == long.MaxValue)
@@ -22,7 +26,5 @@ namespace Emby.Dlna.Eventing
TriggerCount++;
}
-
- public bool IsExpired => SubscriptionTime.AddSeconds(TimeoutSeconds) >= DateTime.UtcNow;
}
}
diff --git a/Emby.Dlna/IConnectionManager.cs b/Emby.Dlna/IConnectionManager.cs
index 855c4454d..7b4a33a98 100644
--- a/Emby.Dlna/IConnectionManager.cs
+++ b/Emby.Dlna/IConnectionManager.cs
@@ -1,3 +1,4 @@
+#pragma warning disable CS1591
namespace Emby.Dlna
{
diff --git a/Emby.Dlna/IContentDirectory.cs b/Emby.Dlna/IContentDirectory.cs
index b54a17c00..83ef09c66 100644
--- a/Emby.Dlna/IContentDirectory.cs
+++ b/Emby.Dlna/IContentDirectory.cs
@@ -1,3 +1,4 @@
+#pragma warning disable CS1591
namespace Emby.Dlna
{
diff --git a/Emby.Dlna/IEventManager.cs b/Emby.Dlna/IEventManager.cs
index 4f67a1b9b..287203389 100644
--- a/Emby.Dlna/IEventManager.cs
+++ b/Emby.Dlna/IEventManager.cs
@@ -1,3 +1,4 @@
+#pragma warning disable CS1591
namespace Emby.Dlna
{
diff --git a/Emby.Dlna/IMediaReceiverRegistrar.cs b/Emby.Dlna/IMediaReceiverRegistrar.cs
index 5dde01f58..b0376b6a9 100644
--- a/Emby.Dlna/IMediaReceiverRegistrar.cs
+++ b/Emby.Dlna/IMediaReceiverRegistrar.cs
@@ -1,3 +1,4 @@
+#pragma warning disable CS1591
namespace Emby.Dlna
{
diff --git a/Emby.Dlna/IUpnpService.cs b/Emby.Dlna/IUpnpService.cs
index ae90e95c7..9e7859567 100644
--- a/Emby.Dlna/IUpnpService.cs
+++ b/Emby.Dlna/IUpnpService.cs
@@ -1,3 +1,7 @@
+#pragma warning disable CS1591
+
+using System.Threading.Tasks;
+
namespace Emby.Dlna
{
public interface IUpnpService
@@ -13,6 +17,6 @@ namespace Emby.Dlna
/// </summary>
/// <param name="request">The request.</param>
/// <returns>ControlResponse.</returns>
- ControlResponse ProcessControlRequest(ControlRequest request);
+ Task<ControlResponse> ProcessControlRequestAsync(ControlRequest request);
}
}
diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs
index 77bde0ca2..770d48168 100644
--- a/Emby.Dlna/Main/DlnaEntryPoint.cs
+++ b/Emby.Dlna/Main/DlnaEntryPoint.cs
@@ -1,6 +1,8 @@
+#pragma warning disable CS1591
+
using System;
-using System.Net.Sockets;
using System.Globalization;
+using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using Emby.Dlna.PlayTo;
@@ -24,7 +26,7 @@ using MediaBrowser.Model.System;
using Microsoft.Extensions.Logging;
using Rssdp;
using Rssdp.Infrastructure;
-using OperatingSystem = MediaBrowser.Common.System.OperatingSystem;
+using OperatingSystem = MediaBrowser.Common.System.OperatingSystem;
namespace Emby.Dlna.Main
{
@@ -56,7 +58,9 @@ namespace Emby.Dlna.Main
private ISsdpCommunicationsServer _communicationsServer;
internal IContentDirectory ContentDirectory { get; private set; }
+
internal IConnectionManager ConnectionManager { get; private set; }
+
internal IMediaReceiverRegistrar MediaReceiverRegistrar { get; private set; }
public static DlnaEntryPoint Current;
@@ -104,7 +108,7 @@ namespace Emby.Dlna.Main
libraryManager,
config,
userManager,
- _logger,
+ loggerFactory.CreateLogger<ContentDirectory.ContentDirectory>(),
httpClient,
localizationManager,
mediaSourceManager,
@@ -112,9 +116,16 @@ namespace Emby.Dlna.Main
mediaEncoder,
tvSeriesManager);
- ConnectionManager = new ConnectionManager.ConnectionManager(dlnaManager, config, _logger, httpClient);
+ ConnectionManager = new ConnectionManager.ConnectionManager(
+ dlnaManager,
+ config,
+ loggerFactory.CreateLogger<ConnectionManager.ConnectionManager>(),
+ httpClient);
- MediaReceiverRegistrar = new MediaReceiverRegistrar.MediaReceiverRegistrar(_logger, httpClient, config);
+ MediaReceiverRegistrar = new MediaReceiverRegistrar.MediaReceiverRegistrar(
+ loggerFactory.CreateLogger<MediaReceiverRegistrar.MediaReceiverRegistrar>(),
+ httpClient,
+ config);
Current = this;
}
diff --git a/Emby.Dlna/MediaReceiverRegistrar/ControlHandler.cs b/Emby.Dlna/MediaReceiverRegistrar/ControlHandler.cs
index 7381e5258..8bf0cd961 100644
--- a/Emby.Dlna/MediaReceiverRegistrar/ControlHandler.cs
+++ b/Emby.Dlna/MediaReceiverRegistrar/ControlHandler.cs
@@ -1,5 +1,8 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
+using System.Xml;
using Emby.Dlna.Service;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration;
@@ -9,35 +12,33 @@ namespace Emby.Dlna.MediaReceiverRegistrar
{
public class ControlHandler : BaseControlHandler
{
- protected override IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, IDictionary<string, string> methodParams)
+ public ControlHandler(IServerConfigurationManager config, ILogger logger)
+ : base(config, logger)
{
- if (string.Equals(methodName, "IsAuthorized", StringComparison.OrdinalIgnoreCase))
- return HandleIsAuthorized();
- if (string.Equals(methodName, "IsValidated", StringComparison.OrdinalIgnoreCase))
- return HandleIsValidated();
-
- throw new ResourceNotFoundException("Unexpected control request name: " + methodName);
}
- private static IEnumerable<KeyValuePair<string, string>> HandleIsAuthorized()
+ /// <inheritdoc />
+ protected override void WriteResult(string methodName, IDictionary<string, string> methodParams, XmlWriter xmlWriter)
{
- return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
+ if (string.Equals(methodName, "IsAuthorized", StringComparison.OrdinalIgnoreCase))
{
- { "Result", "1" }
- };
- }
+ HandleIsAuthorized(xmlWriter);
+ return;
+ }
- private static IEnumerable<KeyValuePair<string, string>> HandleIsValidated()
- {
- return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
+ if (string.Equals(methodName, "IsValidated", StringComparison.OrdinalIgnoreCase))
{
- { "Result", "1" }
- };
- }
+ HandleIsValidated(xmlWriter);
+ return;
+ }
- public ControlHandler(IServerConfigurationManager config, ILogger logger)
- : base(config, logger)
- {
+ throw new ResourceNotFoundException("Unexpected control request name: " + methodName);
}
+
+ private static void HandleIsAuthorized(XmlWriter xmlWriter)
+ => xmlWriter.WriteElementString("Result", "1");
+
+ private static void HandleIsValidated(XmlWriter xmlWriter)
+ => xmlWriter.WriteElementString("Result", "1");
}
}
diff --git a/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrar.cs b/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrar.cs
index b565cb631..64dfc840a 100644
--- a/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrar.cs
+++ b/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrar.cs
@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+
+using System.Threading.Tasks;
using Emby.Dlna.Service;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
@@ -9,23 +12,28 @@ namespace Emby.Dlna.MediaReceiverRegistrar
{
private readonly IServerConfigurationManager _config;
- public MediaReceiverRegistrar(ILogger logger, IHttpClient httpClient, IServerConfigurationManager config)
+ public MediaReceiverRegistrar(
+ ILogger<MediaReceiverRegistrar> logger,
+ IHttpClient httpClient,
+ IServerConfigurationManager config)
: base(logger, httpClient)
{
_config = config;
}
+ /// <inheritdoc />
public string GetServiceXml()
{
return new MediaReceiverRegistrarXmlBuilder().GetXml();
}
- public ControlResponse ProcessControlRequest(ControlRequest request)
+ /// <inheritdoc />
+ public Task<ControlResponse> ProcessControlRequestAsync(ControlRequest request)
{
return new ControlHandler(
_config,
Logger)
- .ProcessControlRequest(request);
+ .ProcessControlRequestAsync(request);
}
}
}
diff --git a/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs b/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs
index 641341185..849702546 100644
--- a/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs
+++ b/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
using Emby.Dlna.Common;
using Emby.Dlna.Service;
diff --git a/Emby.Dlna/MediaReceiverRegistrar/ServiceActionListBuilder.cs b/Emby.Dlna/MediaReceiverRegistrar/ServiceActionListBuilder.cs
index 86429f605..13545c689 100644
--- a/Emby.Dlna/MediaReceiverRegistrar/ServiceActionListBuilder.cs
+++ b/Emby.Dlna/MediaReceiverRegistrar/ServiceActionListBuilder.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
using Emby.Dlna.Common;
diff --git a/Emby.Dlna/PlayTo/Device.cs b/Emby.Dlna/PlayTo/Device.cs
index 0c5ddee65..b77a2bbac 100644
--- a/Emby.Dlna/PlayTo/Device.cs
+++ b/Emby.Dlna/PlayTo/Device.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -221,7 +223,7 @@ namespace Emby.Dlna.PlayTo
_logger.LogDebug("Setting mute");
var value = mute ? 1 : 0;
- await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType, value))
+ await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType, value))
.ConfigureAwait(false);
IsMuted = mute;
@@ -251,7 +253,7 @@ namespace Emby.Dlna.PlayTo
// Remote control will perform better
Volume = value;
- await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType, value))
+ await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType, value))
.ConfigureAwait(false);
}
@@ -270,7 +272,7 @@ namespace Emby.Dlna.PlayTo
throw new InvalidOperationException("Unable to find service");
}
- await new SsdpHttpClient(_httpClient, _config).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("{0:hh}:{0:mm}:{0:ss}", value), "REL_TIME"))
.ConfigureAwait(false);
RestartTimer(true);
@@ -302,7 +304,7 @@ namespace Emby.Dlna.PlayTo
}
var post = avCommands.BuildPost(command, service.ServiceType, url, dictionary);
- await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, post, header: header)
+ await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, post, header: header)
.ConfigureAwait(false);
await Task.Delay(50).ConfigureAwait(false);
@@ -344,7 +346,7 @@ namespace Emby.Dlna.PlayTo
throw new InvalidOperationException("Unable to find service");
}
- return new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, 1));
+ return new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, 1));
}
public async Task SetPlay(CancellationToken cancellationToken)
@@ -368,7 +370,7 @@ namespace Emby.Dlna.PlayTo
var service = GetAvTransportService();
- await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, 1))
+ await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, 1))
.ConfigureAwait(false);
RestartTimer(true);
@@ -386,7 +388,7 @@ namespace Emby.Dlna.PlayTo
var service = GetAvTransportService();
- await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, 1))
+ await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, 1))
.ConfigureAwait(false);
TransportState = TRANSPORTSTATE.PAUSED;
@@ -513,7 +515,7 @@ namespace Emby.Dlna.PlayTo
return;
}
- var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType), true)
+ var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType), true)
.ConfigureAwait(false);
if (result == null || result.Document == null)
@@ -559,7 +561,7 @@ namespace Emby.Dlna.PlayTo
return;
}
- var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType), true)
+ var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType), true)
.ConfigureAwait(false);
if (result == null || result.Document == null)
@@ -586,7 +588,7 @@ namespace Emby.Dlna.PlayTo
return null;
}
- var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType), false)
+ var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType), false)
.ConfigureAwait(false);
if (result == null || result.Document == null)
@@ -624,7 +626,7 @@ namespace Emby.Dlna.PlayTo
var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false);
- var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType), false)
+ var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType), false)
.ConfigureAwait(false);
if (result == null || result.Document == null)
@@ -687,7 +689,7 @@ namespace Emby.Dlna.PlayTo
var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false);
- var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType), false)
+ var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType), false)
.ConfigureAwait(false);
if (result == null || result.Document == null)
@@ -868,7 +870,7 @@ namespace Emby.Dlna.PlayTo
string url = NormalizeUrl(Properties.BaseUrl, avService.ScpdUrl);
- var httpClient = new SsdpHttpClient(_httpClient, _config);
+ var httpClient = new SsdpHttpClient(_httpClient);
var document = await httpClient.GetDataAsync(url, cancellationToken).ConfigureAwait(false);
@@ -896,7 +898,7 @@ namespace Emby.Dlna.PlayTo
string url = NormalizeUrl(Properties.BaseUrl, avService.ScpdUrl);
- var httpClient = new SsdpHttpClient(_httpClient, _config);
+ var httpClient = new SsdpHttpClient(_httpClient);
_logger.LogDebug("Dlna Device.GetRenderingProtocolAsync");
var document = await httpClient.GetDataAsync(url, cancellationToken).ConfigureAwait(false);
@@ -931,7 +933,7 @@ namespace Emby.Dlna.PlayTo
public static async Task<Device> CreateuPnpDeviceAsync(Uri url, IHttpClient httpClient, IServerConfigurationManager config, ILogger logger, CancellationToken cancellationToken)
{
- var ssdpHttpClient = new SsdpHttpClient(httpClient, config);
+ var ssdpHttpClient = new SsdpHttpClient(httpClient);
var document = await ssdpHttpClient.GetDataAsync(url.ToString(), cancellationToken).ConfigureAwait(false);
diff --git a/Emby.Dlna/PlayTo/DeviceInfo.cs b/Emby.Dlna/PlayTo/DeviceInfo.cs
index 9e7c04bdb..f3aaaebc4 100644
--- a/Emby.Dlna/PlayTo/DeviceInfo.cs
+++ b/Emby.Dlna/PlayTo/DeviceInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
using Emby.Dlna.Common;
using MediaBrowser.Model.Dlna;
diff --git a/Emby.Dlna/PlayTo/PlayToController.cs b/Emby.Dlna/PlayTo/PlayToController.cs
index c58f16438..cf978d742 100644
--- a/Emby.Dlna/PlayTo/PlayToController.cs
+++ b/Emby.Dlna/PlayTo/PlayToController.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -6,7 +8,6 @@ using System.Threading;
using System.Threading.Tasks;
using Emby.Dlna.Didl;
using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Entities;
diff --git a/Emby.Dlna/PlayTo/PlayToManager.cs b/Emby.Dlna/PlayTo/PlayToManager.cs
index 2ca44b7ea..b8a47c44c 100644
--- a/Emby.Dlna/PlayTo/PlayToManager.cs
+++ b/Emby.Dlna/PlayTo/PlayToManager.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Globalization;
using System.Linq;
@@ -21,7 +23,7 @@ using Microsoft.Extensions.Logging;
namespace Emby.Dlna.PlayTo
{
- class PlayToManager : IDisposable
+ public class PlayToManager : IDisposable
{
private readonly ILogger _logger;
private readonly ISessionManager _sessionManager;
@@ -64,10 +66,10 @@ namespace Emby.Dlna.PlayTo
public void Start()
{
- _deviceDiscovery.DeviceDiscovered += _deviceDiscovery_DeviceDiscovered;
+ _deviceDiscovery.DeviceDiscovered += OnDeviceDiscoveryDeviceDiscovered;
}
- async void _deviceDiscovery_DeviceDiscovered(object sender, GenericEventArgs<UpnpDeviceInfo> e)
+ private async void OnDeviceDiscoveryDeviceDiscovered(object sender, GenericEventArgs<UpnpDeviceInfo> e)
{
if (_disposed)
{
@@ -231,7 +233,7 @@ namespace Emby.Dlna.PlayTo
public void Dispose()
{
- _deviceDiscovery.DeviceDiscovered -= _deviceDiscovery_DeviceDiscovered;
+ _deviceDiscovery.DeviceDiscovered -= OnDeviceDiscoveryDeviceDiscovered;
try
{
diff --git a/Emby.Dlna/PlayTo/PlaybackProgressEventArgs.cs b/Emby.Dlna/PlayTo/PlaybackProgressEventArgs.cs
index ffa56419b..795618df2 100644
--- a/Emby.Dlna/PlayTo/PlaybackProgressEventArgs.cs
+++ b/Emby.Dlna/PlayTo/PlaybackProgressEventArgs.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace Emby.Dlna.PlayTo
diff --git a/Emby.Dlna/PlayTo/PlaybackStartEventArgs.cs b/Emby.Dlna/PlayTo/PlaybackStartEventArgs.cs
index 8cd8b47ac..27883ca32 100644
--- a/Emby.Dlna/PlayTo/PlaybackStartEventArgs.cs
+++ b/Emby.Dlna/PlayTo/PlaybackStartEventArgs.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace Emby.Dlna.PlayTo
diff --git a/Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs b/Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs
index 2afdc324d..3b169e599 100644
--- a/Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs
+++ b/Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace Emby.Dlna.PlayTo
diff --git a/Emby.Dlna/PlayTo/PlaylistItem.cs b/Emby.Dlna/PlayTo/PlaylistItem.cs
index 1e62b61e9..85846166c 100644
--- a/Emby.Dlna/PlayTo/PlaylistItem.cs
+++ b/Emby.Dlna/PlayTo/PlaylistItem.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.PlayTo
diff --git a/Emby.Dlna/PlayTo/PlaylistItemFactory.cs b/Emby.Dlna/PlayTo/PlaylistItemFactory.cs
index 446d8e1e6..bedc8b9ad 100644
--- a/Emby.Dlna/PlayTo/PlaylistItemFactory.cs
+++ b/Emby.Dlna/PlayTo/PlaylistItemFactory.cs
@@ -1,4 +1,5 @@
-using System.Globalization;
+#pragma warning disable CS1591
+
using System.IO;
using System.Linq;
using MediaBrowser.Controller.Entities;
diff --git a/Emby.Dlna/PlayTo/SsdpHttpClient.cs b/Emby.Dlna/PlayTo/SsdpHttpClient.cs
index 66c634150..dab5f29bd 100644
--- a/Emby.Dlna/PlayTo/SsdpHttpClient.cs
+++ b/Emby.Dlna/PlayTo/SsdpHttpClient.cs
@@ -1,13 +1,15 @@
+#pragma warning disable CS1591
+
using System;
using System.Globalization;
using System.IO;
+using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
using Emby.Dlna.Common;
using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Configuration;
namespace Emby.Dlna.PlayTo
{
@@ -19,12 +21,10 @@ namespace Emby.Dlna.PlayTo
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
private readonly IHttpClient _httpClient;
- private readonly IServerConfigurationManager _config;
- public SsdpHttpClient(IHttpClient httpClient, IServerConfigurationManager config)
+ public SsdpHttpClient(IHttpClient httpClient)
{
_httpClient = httpClient;
- _config = config;
}
public async Task<XDocument> SendCommandAsync(
@@ -64,7 +64,9 @@ namespace Emby.Dlna.PlayTo
}
if (!serviceUrl.StartsWith("/"))
+ {
serviceUrl = "/" + serviceUrl;
+ }
return baseUrl + serviceUrl;
}
@@ -90,7 +92,7 @@ namespace Emby.Dlna.PlayTo
options.RequestHeaders["NT"] = "upnp:event";
options.RequestHeaders["TIMEOUT"] = "Second-" + timeOut.ToString(_usCulture);
- using (await _httpClient.SendAsync(options, "SUBSCRIBE").ConfigureAwait(false))
+ using (await _httpClient.SendAsync(options, new HttpMethod("SUBSCRIBE")).ConfigureAwait(false))
{
}
@@ -110,7 +112,7 @@ namespace Emby.Dlna.PlayTo
options.RequestHeaders["FriendlyName.DLNA.ORG"] = FriendlyName;
- using (var response = await _httpClient.SendAsync(options, "GET").ConfigureAwait(false))
+ using (var response = await _httpClient.SendAsync(options, HttpMethod.Get).ConfigureAwait(false))
using (var stream = response.Content)
using (var reader = new StreamReader(stream, Encoding.UTF8))
{
diff --git a/Emby.Dlna/PlayTo/TRANSPORTSTATE.cs b/Emby.Dlna/PlayTo/TRANSPORTSTATE.cs
index 9f1690b04..7daefeca8 100644
--- a/Emby.Dlna/PlayTo/TRANSPORTSTATE.cs
+++ b/Emby.Dlna/PlayTo/TRANSPORTSTATE.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace Emby.Dlna.PlayTo
{
public enum TRANSPORTSTATE
diff --git a/Emby.Dlna/PlayTo/TransportCommands.cs b/Emby.Dlna/PlayTo/TransportCommands.cs
index 4f9e398e9..c0ce3ab6e 100644
--- a/Emby.Dlna/PlayTo/TransportCommands.cs
+++ b/Emby.Dlna/PlayTo/TransportCommands.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Linq;
diff --git a/Emby.Dlna/PlayTo/UpnpContainer.cs b/Emby.Dlna/PlayTo/UpnpContainer.cs
index 943e0347b..e2d7a10f0 100644
--- a/Emby.Dlna/PlayTo/UpnpContainer.cs
+++ b/Emby.Dlna/PlayTo/UpnpContainer.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Xml.Linq;
using Emby.Dlna.Ssdp;
diff --git a/Emby.Dlna/PlayTo/uBaseObject.cs b/Emby.Dlna/PlayTo/uBaseObject.cs
index f29a126df..a8ed5692c 100644
--- a/Emby.Dlna/PlayTo/uBaseObject.cs
+++ b/Emby.Dlna/PlayTo/uBaseObject.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace Emby.Dlna.PlayTo
diff --git a/Emby.Dlna/PlayTo/uPnpNamespaces.cs b/Emby.Dlna/PlayTo/uPnpNamespaces.cs
index 7132ecd15..dc65cdf43 100644
--- a/Emby.Dlna/PlayTo/uPnpNamespaces.cs
+++ b/Emby.Dlna/PlayTo/uPnpNamespaces.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Xml.Linq;
namespace Emby.Dlna.PlayTo
diff --git a/Emby.Dlna/Profiles/DefaultProfile.cs b/Emby.Dlna/Profiles/DefaultProfile.cs
index ea50bd4a7..d10804b22 100644
--- a/Emby.Dlna/Profiles/DefaultProfile.cs
+++ b/Emby.Dlna/Profiles/DefaultProfile.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Linq;
using MediaBrowser.Model.Dlna;
diff --git a/Emby.Dlna/Profiles/DenonAvrProfile.cs b/Emby.Dlna/Profiles/DenonAvrProfile.cs
index a73885191..73a87c499 100644
--- a/Emby.Dlna/Profiles/DenonAvrProfile.cs
+++ b/Emby.Dlna/Profiles/DenonAvrProfile.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles
diff --git a/Emby.Dlna/Profiles/DirectTvProfile.cs b/Emby.Dlna/Profiles/DirectTvProfile.cs
index 317c0976a..5ca388167 100644
--- a/Emby.Dlna/Profiles/DirectTvProfile.cs
+++ b/Emby.Dlna/Profiles/DirectTvProfile.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles
diff --git a/Emby.Dlna/Profiles/DishHopperJoeyProfile.cs b/Emby.Dlna/Profiles/DishHopperJoeyProfile.cs
index 8d8ab41ca..942e36930 100644
--- a/Emby.Dlna/Profiles/DishHopperJoeyProfile.cs
+++ b/Emby.Dlna/Profiles/DishHopperJoeyProfile.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles
diff --git a/Emby.Dlna/Profiles/Foobar2000Profile.cs b/Emby.Dlna/Profiles/Foobar2000Profile.cs
index 947194bce..ea3de686a 100644
--- a/Emby.Dlna/Profiles/Foobar2000Profile.cs
+++ b/Emby.Dlna/Profiles/Foobar2000Profile.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles
diff --git a/Emby.Dlna/Profiles/LgTvProfile.cs b/Emby.Dlna/Profiles/LgTvProfile.cs
index 145685ab1..02301764c 100644
--- a/Emby.Dlna/Profiles/LgTvProfile.cs
+++ b/Emby.Dlna/Profiles/LgTvProfile.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles
diff --git a/Emby.Dlna/Profiles/LinksysDMA2100Profile.cs b/Emby.Dlna/Profiles/LinksysDMA2100Profile.cs
index 3f0bb4263..1b1423520 100644
--- a/Emby.Dlna/Profiles/LinksysDMA2100Profile.cs
+++ b/Emby.Dlna/Profiles/LinksysDMA2100Profile.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles
diff --git a/Emby.Dlna/Profiles/MarantzProfile.cs b/Emby.Dlna/Profiles/MarantzProfile.cs
index 162e284be..6cfcc3b82 100644
--- a/Emby.Dlna/Profiles/MarantzProfile.cs
+++ b/Emby.Dlna/Profiles/MarantzProfile.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles
diff --git a/Emby.Dlna/Profiles/MediaMonkeyProfile.cs b/Emby.Dlna/Profiles/MediaMonkeyProfile.cs
index 53cae4e2f..7161af738 100644
--- a/Emby.Dlna/Profiles/MediaMonkeyProfile.cs
+++ b/Emby.Dlna/Profiles/MediaMonkeyProfile.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles
diff --git a/Emby.Dlna/Profiles/PanasonicVieraProfile.cs b/Emby.Dlna/Profiles/PanasonicVieraProfile.cs
index 5f31ec484..44c35e142 100644
--- a/Emby.Dlna/Profiles/PanasonicVieraProfile.cs
+++ b/Emby.Dlna/Profiles/PanasonicVieraProfile.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles
diff --git a/Emby.Dlna/Profiles/PopcornHourProfile.cs b/Emby.Dlna/Profiles/PopcornHourProfile.cs
index aefe8c44f..9e9f6966f 100644
--- a/Emby.Dlna/Profiles/PopcornHourProfile.cs
+++ b/Emby.Dlna/Profiles/PopcornHourProfile.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles
diff --git a/Emby.Dlna/Profiles/SamsungSmartTvProfile.cs b/Emby.Dlna/Profiles/SamsungSmartTvProfile.cs
index 51a1c8173..4ff2ab9be 100644
--- a/Emby.Dlna/Profiles/SamsungSmartTvProfile.cs
+++ b/Emby.Dlna/Profiles/SamsungSmartTvProfile.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles
diff --git a/Emby.Dlna/Profiles/SharpSmartTvProfile.cs b/Emby.Dlna/Profiles/SharpSmartTvProfile.cs
index f840cfb34..aa8d434e3 100644
--- a/Emby.Dlna/Profiles/SharpSmartTvProfile.cs
+++ b/Emby.Dlna/Profiles/SharpSmartTvProfile.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles
diff --git a/Emby.Dlna/Profiles/SonyBlurayPlayer2013.cs b/Emby.Dlna/Profiles/SonyBlurayPlayer2013.cs
index 2af1d3b50..42b066d52 100644
--- a/Emby.Dlna/Profiles/SonyBlurayPlayer2013.cs
+++ b/Emby.Dlna/Profiles/SonyBlurayPlayer2013.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles
diff --git a/Emby.Dlna/Profiles/SonyBlurayPlayer2014.cs b/Emby.Dlna/Profiles/SonyBlurayPlayer2014.cs
index 3de0b5192..fbdf2c18e 100644
--- a/Emby.Dlna/Profiles/SonyBlurayPlayer2014.cs
+++ b/Emby.Dlna/Profiles/SonyBlurayPlayer2014.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles
diff --git a/Emby.Dlna/Profiles/SonyBlurayPlayer2015.cs b/Emby.Dlna/Profiles/SonyBlurayPlayer2015.cs
index 889484bea..ce32179a1 100644
--- a/Emby.Dlna/Profiles/SonyBlurayPlayer2015.cs
+++ b/Emby.Dlna/Profiles/SonyBlurayPlayer2015.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles
diff --git a/Emby.Dlna/Profiles/SonyBlurayPlayer2016.cs b/Emby.Dlna/Profiles/SonyBlurayPlayer2016.cs
index acb90bd01..aa1721d39 100644
--- a/Emby.Dlna/Profiles/SonyBlurayPlayer2016.cs
+++ b/Emby.Dlna/Profiles/SonyBlurayPlayer2016.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles
diff --git a/Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs b/Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs
index e1808b205..ecdd2e7a4 100644
--- a/Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs
+++ b/Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles
diff --git a/Emby.Dlna/Profiles/SonyBravia2010Profile.cs b/Emby.Dlna/Profiles/SonyBravia2010Profile.cs
index f8e8faa76..68365ba4a 100644
--- a/Emby.Dlna/Profiles/SonyBravia2010Profile.cs
+++ b/Emby.Dlna/Profiles/SonyBravia2010Profile.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles
diff --git a/Emby.Dlna/Profiles/SonyBravia2011Profile.cs b/Emby.Dlna/Profiles/SonyBravia2011Profile.cs
index 111f36e9b..b34af04a5 100644
--- a/Emby.Dlna/Profiles/SonyBravia2011Profile.cs
+++ b/Emby.Dlna/Profiles/SonyBravia2011Profile.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles
diff --git a/Emby.Dlna/Profiles/SonyBravia2012Profile.cs b/Emby.Dlna/Profiles/SonyBravia2012Profile.cs
index d5efe4270..0e75d0cb5 100644
--- a/Emby.Dlna/Profiles/SonyBravia2012Profile.cs
+++ b/Emby.Dlna/Profiles/SonyBravia2012Profile.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles
diff --git a/Emby.Dlna/Profiles/SonyBravia2013Profile.cs b/Emby.Dlna/Profiles/SonyBravia2013Profile.cs
index 3b0228694..3300863c9 100644
--- a/Emby.Dlna/Profiles/SonyBravia2013Profile.cs
+++ b/Emby.Dlna/Profiles/SonyBravia2013Profile.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles
diff --git a/Emby.Dlna/Profiles/SonyBravia2014Profile.cs b/Emby.Dlna/Profiles/SonyBravia2014Profile.cs
index e860eae34..4e833441c 100644
--- a/Emby.Dlna/Profiles/SonyBravia2014Profile.cs
+++ b/Emby.Dlna/Profiles/SonyBravia2014Profile.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles
diff --git a/Emby.Dlna/Profiles/SonyPs3Profile.cs b/Emby.Dlna/Profiles/SonyPs3Profile.cs
index 88d064695..7f72356bd 100644
--- a/Emby.Dlna/Profiles/SonyPs3Profile.cs
+++ b/Emby.Dlna/Profiles/SonyPs3Profile.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles
diff --git a/Emby.Dlna/Profiles/SonyPs4Profile.cs b/Emby.Dlna/Profiles/SonyPs4Profile.cs
index 499cf8803..411bfe2b0 100644
--- a/Emby.Dlna/Profiles/SonyPs4Profile.cs
+++ b/Emby.Dlna/Profiles/SonyPs4Profile.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles
diff --git a/Emby.Dlna/Profiles/WdtvLiveProfile.cs b/Emby.Dlna/Profiles/WdtvLiveProfile.cs
index bf7b1ab47..2de9a8cd9 100644
--- a/Emby.Dlna/Profiles/WdtvLiveProfile.cs
+++ b/Emby.Dlna/Profiles/WdtvLiveProfile.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles
diff --git a/Emby.Dlna/Profiles/XboxOneProfile.cs b/Emby.Dlna/Profiles/XboxOneProfile.cs
index 710b891e3..2cbe4e6ac 100644
--- a/Emby.Dlna/Profiles/XboxOneProfile.cs
+++ b/Emby.Dlna/Profiles/XboxOneProfile.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles
diff --git a/Emby.Dlna/Server/DescriptionXmlBuilder.cs b/Emby.Dlna/Server/DescriptionXmlBuilder.cs
index 03d8f80ab..5ecc81a2f 100644
--- a/Emby.Dlna/Server/DescriptionXmlBuilder.cs
+++ b/Emby.Dlna/Server/DescriptionXmlBuilder.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -5,7 +7,6 @@ using System.Linq;
using System.Text;
using Emby.Dlna.Common;
using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Extensions;
namespace Emby.Dlna.Server
{
diff --git a/Emby.Dlna/Service/BaseControlHandler.cs b/Emby.Dlna/Service/BaseControlHandler.cs
index 067d5fa43..161a3434c 100644
--- a/Emby.Dlna/Service/BaseControlHandler.cs
+++ b/Emby.Dlna/Service/BaseControlHandler.cs
@@ -1,8 +1,10 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.IO;
-using System.Linq;
using System.Text;
+using System.Threading.Tasks;
using System.Xml;
using Emby.Dlna.Didl;
using MediaBrowser.Controller.Configuration;
@@ -15,44 +17,34 @@ namespace Emby.Dlna.Service
{
private const string NS_SOAPENV = "http://schemas.xmlsoap.org/soap/envelope/";
- protected readonly IServerConfigurationManager Config;
- protected readonly ILogger _logger;
+ protected IServerConfigurationManager Config { get; }
+ protected ILogger Logger { get; }
protected BaseControlHandler(IServerConfigurationManager config, ILogger logger)
{
Config = config;
- _logger = logger;
+ Logger = logger;
}
- public ControlResponse ProcessControlRequest(ControlRequest request)
+ public async Task<ControlResponse> ProcessControlRequestAsync(ControlRequest request)
{
try
{
- var enableDebugLogging = Config.GetDlnaConfiguration().EnableDebugLog;
-
- if (enableDebugLogging)
- {
- LogRequest(request);
- }
-
- var response = ProcessControlRequestInternal(request);
-
- if (enableDebugLogging)
- {
- LogResponse(response);
- }
+ LogRequest(request);
+ var response = await ProcessControlRequestInternalAsync(request).ConfigureAwait(false);
+ LogResponse(response);
return response;
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error processing control request");
+ Logger.LogError(ex, "Error processing control request");
- return new ControlErrorHandler().GetResponse(ex);
+ return ControlErrorHandler.GetResponse(ex);
}
}
- private ControlResponse ProcessControlRequestInternal(ControlRequest request)
+ private async Task<ControlResponse> ProcessControlRequestInternalAsync(ControlRequest request)
{
ControlRequestInfo requestInfo = null;
@@ -63,18 +55,17 @@ namespace Emby.Dlna.Service
ValidationType = ValidationType.None,
CheckCharacters = false,
IgnoreProcessingInstructions = true,
- IgnoreComments = true
+ IgnoreComments = true,
+ Async = true
};
using (var reader = XmlReader.Create(streamReader, readerSettings))
{
- requestInfo = ParseRequest(reader);
+ requestInfo = await ParseRequestAsync(reader).ConfigureAwait(false);
}
}
- _logger.LogDebug("Received control request {0}", requestInfo.LocalName);
-
- var result = GetResult(requestInfo.LocalName, requestInfo.Headers);
+ Logger.LogDebug("Received control request {0}", requestInfo.LocalName);
var settings = new XmlWriterSettings
{
@@ -93,12 +84,9 @@ namespace Emby.Dlna.Service
writer.WriteStartElement("SOAP-ENV", "Body", NS_SOAPENV);
writer.WriteStartElement("u", requestInfo.LocalName + "Response", requestInfo.NamespaceURI);
- foreach (var i in result)
- {
- writer.WriteStartElement(i.Key);
- writer.WriteString(i.Value);
- writer.WriteFullEndElement();
- }
+
+ WriteResult(requestInfo.LocalName, requestInfo.Headers, writer);
+
writer.WriteFullEndElement();
writer.WriteFullEndElement();
@@ -106,7 +94,7 @@ namespace Emby.Dlna.Service
writer.WriteEndDocument();
}
- var xml = builder.ToString().Replace("xmlns:m=", "xmlns:u=");
+ var xml = builder.ToString().Replace("xmlns:m=", "xmlns:u=", StringComparison.Ordinal);
var controlResponse = new ControlResponse
{
@@ -114,17 +102,15 @@ namespace Emby.Dlna.Service
IsSuccessful = true
};
- //logger.LogDebug(xml);
-
controlResponse.Headers.Add("EXT", string.Empty);
return controlResponse;
}
- private ControlRequestInfo ParseRequest(XmlReader reader)
+ private async Task<ControlRequestInfo> ParseRequestAsync(XmlReader reader)
{
- reader.MoveToContent();
- reader.Read();
+ await reader.MoveToContentAsync().ConfigureAwait(false);
+ await reader.ReadAsync().ConfigureAwait(false);
// Loop through each element
while (!reader.EOF && reader.ReadState == ReadState.Interactive)
@@ -139,37 +125,38 @@ namespace Emby.Dlna.Service
{
using (var subReader = reader.ReadSubtree())
{
- return ParseBodyTag(subReader);
+ return await ParseBodyTagAsync(subReader).ConfigureAwait(false);
}
}
else
{
- reader.Read();
+ await reader.ReadAsync().ConfigureAwait(false);
}
+
break;
}
default:
{
- reader.Skip();
+ await reader.SkipAsync().ConfigureAwait(false);
break;
}
}
}
else
{
- reader.Read();
+ await reader.ReadAsync().ConfigureAwait(false);
}
}
return new ControlRequestInfo();
}
- private ControlRequestInfo ParseBodyTag(XmlReader reader)
+ private async Task<ControlRequestInfo> ParseBodyTagAsync(XmlReader reader)
{
var result = new ControlRequestInfo();
- reader.MoveToContent();
- reader.Read();
+ await reader.MoveToContentAsync().ConfigureAwait(false);
+ await reader.ReadAsync().ConfigureAwait(false);
// Loop through each element
while (!reader.EOF && reader.ReadState == ReadState.Interactive)
@@ -183,28 +170,28 @@ namespace Emby.Dlna.Service
{
using (var subReader = reader.ReadSubtree())
{
- ParseFirstBodyChild(subReader, result.Headers);
+ await ParseFirstBodyChildAsync(subReader, result.Headers).ConfigureAwait(false);
return result;
}
}
else
{
- reader.Read();
+ await reader.ReadAsync().ConfigureAwait(false);
}
}
else
{
- reader.Read();
+ await reader.ReadAsync().ConfigureAwait(false);
}
}
return result;
}
- private void ParseFirstBodyChild(XmlReader reader, IDictionary<string, string> headers)
+ private async Task ParseFirstBodyChildAsync(XmlReader reader, IDictionary<string, string> headers)
{
- reader.MoveToContent();
- reader.Read();
+ await reader.MoveToContentAsync().ConfigureAwait(false);
+ await reader.ReadAsync().ConfigureAwait(false);
// Loop through each element
while (!reader.EOF && reader.ReadState == ReadState.Interactive)
@@ -212,23 +199,23 @@ namespace Emby.Dlna.Service
if (reader.NodeType == XmlNodeType.Element)
{
// TODO: Should we be doing this here, or should it be handled earlier when decoding the request?
- headers[reader.LocalName.RemoveDiacritics()] = reader.ReadElementContentAsString();
+ headers[reader.LocalName.RemoveDiacritics()] = await reader.ReadElementContentAsStringAsync().ConfigureAwait(false);
}
else
{
- reader.Read();
+ await reader.ReadAsync().ConfigureAwait(false);
}
}
}
private class ControlRequestInfo
{
- public string LocalName;
- public string NamespaceURI;
- public IDictionary<string, string> Headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+ public string LocalName { get; set; }
+ public string NamespaceURI { get; set; }
+ public Dictionary<string, string> Headers { get; } = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
}
- protected abstract IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, IDictionary<string, string> methodParams);
+ protected abstract void WriteResult(string methodName, IDictionary<string, string> methodParams, XmlWriter xmlWriter);
private void LogRequest(ControlRequest request)
{
@@ -237,10 +224,7 @@ namespace Emby.Dlna.Service
return;
}
- var originalHeaders = request.Headers;
- var headers = string.Join(", ", originalHeaders.Select(i => string.Format("{0}={1}", i.Key, i.Value)).ToArray());
-
- _logger.LogDebug("Control request. Headers: {0}", headers);
+ Logger.LogDebug("Control request. Headers: {@Headers}", request.Headers);
}
private void LogResponse(ControlResponse response)
@@ -250,11 +234,7 @@ namespace Emby.Dlna.Service
return;
}
- var originalHeaders = response.Headers;
- var headers = string.Join(", ", originalHeaders.Select(i => string.Format("{0}={1}", i.Key, i.Value)).ToArray());
- //builder.Append(response.Xml);
-
- _logger.LogDebug("Control response. Headers: {0}", headers);
+ Logger.LogDebug("Control response. Headers: {@Headers}\n{Xml}", response.Headers, response.Xml);
}
}
}
diff --git a/Emby.Dlna/Service/BaseService.cs b/Emby.Dlna/Service/BaseService.cs
index 5359e94c4..3704bedcd 100644
--- a/Emby.Dlna/Service/BaseService.cs
+++ b/Emby.Dlna/Service/BaseService.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using Emby.Dlna.Eventing;
using MediaBrowser.Common.Net;
using Microsoft.Extensions.Logging;
@@ -10,7 +12,7 @@ namespace Emby.Dlna.Service
protected IHttpClient HttpClient;
protected ILogger Logger;
- protected BaseService(ILogger logger, IHttpClient httpClient)
+ protected BaseService(ILogger<BaseService> logger, IHttpClient httpClient)
{
Logger = logger;
HttpClient = httpClient;
diff --git a/Emby.Dlna/Service/ControlErrorHandler.cs b/Emby.Dlna/Service/ControlErrorHandler.cs
index d5eb4a887..047e9f014 100644
--- a/Emby.Dlna/Service/ControlErrorHandler.cs
+++ b/Emby.Dlna/Service/ControlErrorHandler.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.IO;
using System.Text;
@@ -6,11 +8,11 @@ using Emby.Dlna.Didl;
namespace Emby.Dlna.Service
{
- public class ControlErrorHandler
+ public static class ControlErrorHandler
{
private const string NS_SOAPENV = "http://schemas.xmlsoap.org/soap/envelope/";
- public ControlResponse GetResponse(Exception ex)
+ public static ControlResponse GetResponse(Exception ex)
{
var settings = new XmlWriterSettings
{
diff --git a/Emby.Dlna/Service/ServiceXmlBuilder.cs b/Emby.Dlna/Service/ServiceXmlBuilder.cs
index bd1f0bf05..62ffd9e42 100644
--- a/Emby.Dlna/Service/ServiceXmlBuilder.cs
+++ b/Emby.Dlna/Service/ServiceXmlBuilder.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
using System.Text;
using Emby.Dlna.Common;
diff --git a/Emby.Dlna/Ssdp/DeviceDiscovery.cs b/Emby.Dlna/Ssdp/DeviceDiscovery.cs
index c5f3593da..f95b8ce7d 100644
--- a/Emby.Dlna/Ssdp/DeviceDiscovery.cs
+++ b/Emby.Dlna/Ssdp/DeviceDiscovery.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Linq;
@@ -9,16 +11,16 @@ using Rssdp.Infrastructure;
namespace Emby.Dlna.Ssdp
{
- public class DeviceDiscovery : IDeviceDiscovery
+ public sealed class DeviceDiscovery : IDeviceDiscovery, IDisposable
{
- private bool _disposed;
+ private readonly object _syncLock = new object();
private readonly IServerConfigurationManager _config;
- private event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceDiscoveredInternal;
-
private int _listenerCount;
- private object _syncLock = new object();
+ private bool _disposed;
+
+ private event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceDiscoveredInternal;
/// <inheritdoc />
public event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceDiscovered
@@ -33,6 +35,7 @@ namespace Emby.Dlna.Ssdp
StartInternal();
}
+
remove
{
lock (_syncLock)
@@ -130,6 +133,7 @@ namespace Emby.Dlna.Ssdp
DeviceLeft?.Invoke(this, args);
}
+ /// <inheritdoc />
public void Dispose()
{
if (!_disposed)
diff --git a/Emby.Dlna/Ssdp/Extensions.cs b/Emby.Dlna/Ssdp/Extensions.cs
index c680c123e..10c1f321b 100644
--- a/Emby.Dlna/Ssdp/Extensions.cs
+++ b/Emby.Dlna/Ssdp/Extensions.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Xml.Linq;
namespace Emby.Dlna.Ssdp
diff --git a/Emby.Drawing/Emby.Drawing.csproj b/Emby.Drawing/Emby.Drawing.csproj
index 85cecdc44..b7090b262 100644
--- a/Emby.Drawing/Emby.Drawing.csproj
+++ b/Emby.Drawing/Emby.Drawing.csproj
@@ -17,4 +17,16 @@
<Compile Include="..\SharedVersion.cs" />
</ItemGroup>
+ <!-- Code analysers-->
+ <ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
+ <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8" PrivateAssets="All" />
+ <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
+ <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" />
+ <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
+ </ItemGroup>
+
+ <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
+ <CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
+ </PropertyGroup>
+
</Project>
diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs
index ce8089e59..eca4b56eb 100644
--- a/Emby.Drawing/ImageProcessor.cs
+++ b/Emby.Drawing/ImageProcessor.cs
@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
-using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller;
@@ -11,10 +10,8 @@ using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Net;
using Microsoft.Extensions.Logging;
@@ -24,7 +21,7 @@ namespace Emby.Drawing
/// <summary>
/// Class ImageProcessor.
/// </summary>
- public class ImageProcessor : IImageProcessor, IDisposable
+ public sealed class ImageProcessor : IImageProcessor, IDisposable
{
// Increment this when there's a change requiring caches to be invalidated
private const string Version = "3";
@@ -32,28 +29,24 @@ namespace Emby.Drawing
private static readonly HashSet<string> _transparentImageTypes
= new HashSet<string>(StringComparer.OrdinalIgnoreCase) { ".png", ".webp", ".gif" };
- /// <summary>
- /// The _logger
- /// </summary>
private readonly ILogger _logger;
private readonly IFileSystem _fileSystem;
private readonly IServerApplicationPaths _appPaths;
- private IImageEncoder _imageEncoder;
+ private readonly IImageEncoder _imageEncoder;
private readonly Func<ILibraryManager> _libraryManager;
private readonly Func<IMediaEncoder> _mediaEncoder;
- private readonly Dictionary<string, LockInfo> _locks = new Dictionary<string, LockInfo>();
private bool _disposed = false;
/// <summary>
- ///
+ /// Initializes a new instance of the <see cref="ImageProcessor"/> class.
/// </summary>
- /// <param name="logger"></param>
- /// <param name="appPaths"></param>
- /// <param name="fileSystem"></param>
- /// <param name="imageEncoder"></param>
- /// <param name="libraryManager"></param>
- /// <param name="mediaEncoder"></param>
+ /// <param name="logger">The logger.</param>
+ /// <param name="appPaths">The server application paths.</param>
+ /// <param name="fileSystem">The filesystem.</param>
+ /// <param name="imageEncoder">The image encoder.</param>
+ /// <param name="libraryManager">The library manager.</param>
+ /// <param name="mediaEncoder">The media encoder.</param>
public ImageProcessor(
ILogger<ImageProcessor> logger,
IServerApplicationPaths appPaths,
@@ -68,16 +61,10 @@ namespace Emby.Drawing
_libraryManager = libraryManager;
_mediaEncoder = mediaEncoder;
_appPaths = appPaths;
-
- ImageEnhancers = Array.Empty<IImageEnhancer>();
-
- ImageHelper.ImageProcessor = this;
}
private string ResizedImageCachePath => Path.Combine(_appPaths.ImageCachePath, "resized-images");
- private string EnhancedImageCachePath => Path.Combine(_appPaths.ImageCachePath, "enhanced-images");
-
/// <inheritdoc />
public IReadOnlyCollection<string> SupportedInputFormats =>
new HashSet<string>(StringComparer.OrdinalIgnoreCase)
@@ -90,9 +77,7 @@ namespace Emby.Drawing
"aiff",
"cr2",
"crw",
-
- // Remove until supported
- //"nef",
+ "nef",
"orf",
"pef",
"arw",
@@ -112,24 +97,14 @@ namespace Emby.Drawing
};
/// <inheritdoc />
- public IReadOnlyCollection<IImageEnhancer> ImageEnhancers { get; set; }
-
- /// <inheritdoc />
public bool SupportsImageCollageCreation => _imageEncoder.SupportsImageCollageCreation;
/// <inheritdoc />
- public IImageEncoder ImageEncoder
- {
- get => _imageEncoder;
- set => _imageEncoder = value ?? throw new ArgumentNullException(nameof(value));
- }
-
- /// <inheritdoc />
public async Task ProcessImage(ImageProcessingOptions options, Stream toStream)
{
var file = await ProcessImage(options).ConfigureAwait(false);
- using (var fileStream = _fileSystem.GetFileStream(file.Item1, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read, true))
+ using (var fileStream = new FileStream(file.Item1, FileMode.Open, FileAccess.Read, FileShare.Read, IODefaults.FileStreamBufferSize, true))
{
await fileStream.CopyToAsync(toStream).ConfigureAwait(false);
}
@@ -151,6 +126,8 @@ namespace Emby.Drawing
throw new ArgumentNullException(nameof(options));
}
+ var libraryManager = _libraryManager();
+
ItemImageInfo originalImage = options.Image;
BaseItem item = options.Item;
@@ -158,9 +135,10 @@ namespace Emby.Drawing
{
if (item == null)
{
- item = _libraryManager().GetItemById(options.ItemId);
+ item = libraryManager.GetItemById(options.ItemId);
}
- originalImage = await _libraryManager().ConvertImageToLocal(item, originalImage, options.ImageIndex).ConfigureAwait(false);
+
+ originalImage = await libraryManager.ConvertImageToLocal(item, originalImage, options.ImageIndex).ConfigureAwait(false);
}
string originalImagePath = originalImage.Path;
@@ -187,27 +165,6 @@ namespace Emby.Drawing
dateModified = supportedImageInfo.dateModified;
bool requiresTransparency = _transparentImageTypes.Contains(Path.GetExtension(originalImagePath));
- if (options.Enhancers.Count > 0)
- {
- if (item == null)
- {
- item = _libraryManager().GetItemById(options.ItemId);
- }
-
- var tuple = await GetEnhancedImage(new ItemImageInfo
- {
- DateModified = dateModified,
- Type = originalImage.Type,
- Path = originalImagePath
- }, requiresTransparency, item, options.ImageIndex, options.Enhancers, CancellationToken.None).ConfigureAwait(false);
-
- originalImagePath = tuple.path;
- dateModified = tuple.dateModified;
- requiresTransparency = tuple.transparent;
- // TODO: Get this info
- originalImageSize = null;
- }
-
bool autoOrient = false;
ImageOrientation? orientation = null;
if (item is Photo photo)
@@ -240,12 +197,6 @@ namespace Emby.Drawing
ImageFormat outputFormat = GetOutputFormat(options.SupportedOutputFormats, requiresTransparency);
string cacheFilePath = GetCacheFilePath(originalImagePath, newSize, quality, dateModified, outputFormat, options.AddPlayedIndicator, options.PercentPlayed, options.UnplayedCount, options.Blur, options.BackgroundColor, options.ForegroundLayer);
- CheckDisposed();
-
- LockInfo lockInfo = GetLock(cacheFilePath);
-
- await lockInfo.Lock.WaitAsync().ConfigureAwait(false);
-
try
{
if (!File.Exists(cacheFilePath))
@@ -271,10 +222,6 @@ namespace Emby.Drawing
_logger.LogError(ex, "Error encoding image");
return (originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified);
}
- finally
- {
- ReleaseLock(cacheFilePath, lockInfo);
- }
}
private ImageFormat GetOutputFormat(IReadOnlyCollection<ImageFormat> clientSupportedFormats, bool requiresTransparency)
@@ -306,20 +253,18 @@ namespace Emby.Drawing
}
private string GetMimeType(ImageFormat format, string path)
- {
- switch(format)
- {
- case ImageFormat.Bmp: return MimeTypes.GetMimeType("i.bmp");
- case ImageFormat.Gif: return MimeTypes.GetMimeType("i.gif");
- case ImageFormat.Jpg: return MimeTypes.GetMimeType("i.jpg");
- case ImageFormat.Png: return MimeTypes.GetMimeType("i.png");
- case ImageFormat.Webp: return MimeTypes.GetMimeType("i.webp");
- default: return MimeTypes.GetMimeType(path);
- }
- }
+ => format switch
+ {
+ ImageFormat.Bmp => MimeTypes.GetMimeType("i.bmp"),
+ ImageFormat.Gif => MimeTypes.GetMimeType("i.gif"),
+ ImageFormat.Jpg => MimeTypes.GetMimeType("i.jpg"),
+ ImageFormat.Png => MimeTypes.GetMimeType("i.png"),
+ ImageFormat.Webp => MimeTypes.GetMimeType("i.webp"),
+ _ => MimeTypes.GetMimeType(path)
+ };
/// <summary>
- /// Gets the cache file path based on a set of parameters
+ /// Gets the cache file path based on a set of parameters.
/// </summary>
private string GetCacheFilePath(string originalPath, ImageDimensions outputSize, int quality, DateTime dateModified, ImageFormat format, bool addPlayedIndicator, double percentPlayed, int? unwatchedCount, int? blur, string backgroundColor, string foregroundLayer)
{
@@ -401,11 +346,7 @@ namespace Emby.Drawing
/// <inheritdoc />
public string GetImageCacheTag(BaseItem item, ItemImageInfo image)
- {
- var supportedEnhancers = GetSupportedEnhancers(item, image.Type).ToArray();
-
- return GetImageCacheTag(item, image, supportedEnhancers);
- }
+ => (item.Path + image.DateModified.Ticks).GetMD5().ToString("N", CultureInfo.InvariantCulture);
/// <inheritdoc />
public string GetImageCacheTag(BaseItem item, ChapterInfo chapter)
@@ -425,26 +366,6 @@ namespace Emby.Drawing
}
}
- /// <inheritdoc />
- public string GetImageCacheTag(BaseItem item, ItemImageInfo image, IReadOnlyCollection<IImageEnhancer> imageEnhancers)
- {
- string originalImagePath = image.Path;
- DateTime dateModified = image.DateModified;
- ImageType imageType = image.Type;
-
- // Optimization
- if (imageEnhancers.Count == 0)
- {
- return (originalImagePath + dateModified.Ticks).GetMD5().ToString("N", CultureInfo.InvariantCulture);
- }
-
- // Cache name is created with supported enhancers combined with the last config change so we pick up new config changes
- var cacheKeys = imageEnhancers.Select(i => i.GetConfigurationCacheKey(item, imageType)).ToList();
- cacheKeys.Add(originalImagePath + dateModified.Ticks);
-
- return string.Join("|", cacheKeys).GetMD5().ToString("N", CultureInfo.InvariantCulture);
- }
-
private async Task<(string path, DateTime dateModified)> GetSupportedImage(string originalImagePath, DateTime dateModified)
{
var inputFormat = Path.GetExtension(originalImagePath)
@@ -488,154 +409,6 @@ namespace Emby.Drawing
return (originalImagePath, dateModified);
}
- /// <inheritdoc />
- public async Task<string> GetEnhancedImage(BaseItem item, ImageType imageType, int imageIndex)
- {
- var enhancers = GetSupportedEnhancers(item, imageType).ToArray();
-
- ItemImageInfo imageInfo = item.GetImageInfo(imageType, imageIndex);
-
- bool inputImageSupportsTransparency = SupportsTransparency(imageInfo.Path);
-
- var result = await GetEnhancedImage(imageInfo, inputImageSupportsTransparency, item, imageIndex, enhancers, CancellationToken.None);
-
- return result.path;
- }
-
- private async Task<(string path, DateTime dateModified, bool transparent)> GetEnhancedImage(
- ItemImageInfo image,
- bool inputImageSupportsTransparency,
- BaseItem item,
- int imageIndex,
- IReadOnlyCollection<IImageEnhancer> enhancers,
- CancellationToken cancellationToken)
- {
- var originalImagePath = image.Path;
- var dateModified = image.DateModified;
- var imageType = image.Type;
-
- try
- {
- var cacheGuid = GetImageCacheTag(item, image, enhancers);
-
- // Enhance if we have enhancers
- var enhancedImageInfo = await GetEnhancedImageInternal(originalImagePath, item, imageType, imageIndex, enhancers, cacheGuid, cancellationToken).ConfigureAwait(false);
-
- string enhancedImagePath = enhancedImageInfo.path;
-
- // If the path changed update dateModified
- if (!string.Equals(enhancedImagePath, originalImagePath, StringComparison.OrdinalIgnoreCase))
- {
- var treatmentRequiresTransparency = enhancedImageInfo.transparent;
-
- return (enhancedImagePath, _fileSystem.GetLastWriteTimeUtc(enhancedImagePath), treatmentRequiresTransparency);
- }
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error enhancing image");
- }
-
- return (originalImagePath, dateModified, inputImageSupportsTransparency);
- }
-
- /// <summary>
- /// Gets the enhanced image internal.
- /// </summary>
- /// <param name="originalImagePath">The original image path.</param>
- /// <param name="item">The item.</param>
- /// <param name="imageType">Type of the image.</param>
- /// <param name="imageIndex">Index of the image.</param>
- /// <param name="supportedEnhancers">The supported enhancers.</param>
- /// <param name="cacheGuid">The cache unique identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task&lt;System.String&gt;.</returns>
- /// <exception cref="ArgumentNullException">
- /// originalImagePath
- /// or
- /// item
- /// </exception>
- private async Task<(string path, bool transparent)> GetEnhancedImageInternal(
- string originalImagePath,
- BaseItem item,
- ImageType imageType,
- int imageIndex,
- IReadOnlyCollection<IImageEnhancer> supportedEnhancers,
- string cacheGuid,
- CancellationToken cancellationToken = default)
- {
- if (string.IsNullOrEmpty(originalImagePath))
- {
- throw new ArgumentNullException(nameof(originalImagePath));
- }
-
- if (item == null)
- {
- throw new ArgumentNullException(nameof(item));
- }
-
- var treatmentRequiresTransparency = false;
- foreach (var enhancer in supportedEnhancers)
- {
- if (!treatmentRequiresTransparency)
- {
- treatmentRequiresTransparency = enhancer.GetEnhancedImageInfo(item, originalImagePath, imageType, imageIndex).RequiresTransparency;
- }
- }
-
- // All enhanced images are saved as png to allow transparency
- string cacheExtension = _imageEncoder.SupportedOutputFormats.Contains(ImageFormat.Webp) ?
- ".webp" :
- (treatmentRequiresTransparency ? ".png" : ".jpg");
-
- string enhancedImagePath = GetCachePath(EnhancedImageCachePath, cacheGuid + cacheExtension);
-
- LockInfo lockInfo = GetLock(enhancedImagePath);
-
- await lockInfo.Lock.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- try
- {
- // Check again in case of contention
- if (File.Exists(enhancedImagePath))
- {
- return (enhancedImagePath, treatmentRequiresTransparency);
- }
-
- Directory.CreateDirectory(Path.GetDirectoryName(enhancedImagePath));
-
- await ExecuteImageEnhancers(supportedEnhancers, originalImagePath, enhancedImagePath, item, imageType, imageIndex).ConfigureAwait(false);
-
- return (enhancedImagePath, treatmentRequiresTransparency);
- }
- finally
- {
- ReleaseLock(enhancedImagePath, lockInfo);
- }
- }
-
- /// <summary>
- /// Executes the image enhancers.
- /// </summary>
- /// <param name="imageEnhancers">The image enhancers.</param>
- /// <param name="inputPath">The input path.</param>
- /// <param name="outputPath">The output path.</param>
- /// <param name="item">The item.</param>
- /// <param name="imageType">Type of the image.</param>
- /// <param name="imageIndex">Index of the image.</param>
- /// <returns>Task{EnhancedImage}.</returns>
- private static async Task ExecuteImageEnhancers(IEnumerable<IImageEnhancer> imageEnhancers, string inputPath, string outputPath, BaseItem item, ImageType imageType, int imageIndex)
- {
- // Run the enhancers sequentially in order of priority
- foreach (var enhancer in imageEnhancers)
- {
- await enhancer.EnhanceImageAsync(item, inputPath, outputPath, imageType, imageIndex).ConfigureAwait(false);
-
- // Feed the output into the next enhancer as input
- inputPath = outputPath;
- }
- }
-
/// <summary>
/// Gets the cache path.
/// </summary>
@@ -648,7 +421,7 @@ namespace Emby.Drawing
/// or
/// uniqueName
/// or
- /// fileExtension
+ /// fileExtension.
/// </exception>
public string GetCachePath(string path, string uniqueName, string fileExtension)
{
@@ -681,7 +454,7 @@ namespace Emby.Drawing
/// <exception cref="ArgumentNullException">
/// path
/// or
- /// filename
+ /// filename.
/// </exception>
public string GetCachePath(string path, string filename)
{
@@ -689,6 +462,7 @@ namespace Emby.Drawing
{
throw new ArgumentNullException(nameof(path));
}
+
if (string.IsNullOrEmpty(filename))
{
throw new ArgumentNullException(nameof(filename));
@@ -710,74 +484,19 @@ namespace Emby.Drawing
}
/// <inheritdoc />
- public IEnumerable<IImageEnhancer> GetSupportedEnhancers(BaseItem item, ImageType imageType)
- {
- foreach (var i in ImageEnhancers)
- {
- if (i.Supports(item, imageType))
- {
- yield return i;
- }
- }
- }
-
-
- private class LockInfo
- {
- public SemaphoreSlim Lock = new SemaphoreSlim(1, 1);
- public int Count = 1;
- }
-
- private LockInfo GetLock(string key)
- {
- lock (_locks)
- {
- if (_locks.TryGetValue(key, out LockInfo info))
- {
- info.Count++;
- }
- else
- {
- info = new LockInfo();
- _locks[key] = info;
- }
- return info;
- }
- }
-
- private void ReleaseLock(string key, LockInfo info)
+ public void Dispose()
{
- info.Lock.Release();
-
- lock (_locks)
+ if (_disposed)
{
- info.Count--;
- if (info.Count <= 0)
- {
- _locks.Remove(key);
- info.Lock.Dispose();
- }
+ return;
}
- }
-
- /// <inheritdoc />
- public void Dispose()
- {
- _disposed = true;
- var disposable = _imageEncoder as IDisposable;
- if (disposable != null)
+ if (_imageEncoder is IDisposable disposable)
{
disposable.Dispose();
}
- }
- private void CheckDisposed()
- {
- if (_disposed)
- {
- throw new ObjectDisposedException(GetType().Name);
- }
+ _disposed = true;
}
}
}
diff --git a/Emby.Naming/Audio/AlbumParser.cs b/Emby.Naming/Audio/AlbumParser.cs
index 4975b8e19..33f4468d9 100644
--- a/Emby.Naming/Audio/AlbumParser.cs
+++ b/Emby.Naming/Audio/AlbumParser.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Globalization;
@@ -19,15 +18,13 @@ namespace Emby.Naming.Audio
_options = options;
}
- public MultiPartResult ParseMultiPart(string path)
+ public bool IsMultiPart(string path)
{
- var result = new MultiPartResult();
-
var filename = Path.GetFileName(path);
if (string.IsNullOrEmpty(filename))
{
- return result;
+ return false;
}
// TODO: Move this logic into options object
@@ -57,12 +54,11 @@ namespace Emby.Naming.Audio
if (int.TryParse(tmp, NumberStyles.Integer, CultureInfo.InvariantCulture, out _))
{
- result.IsMultiPart = true;
- break;
+ return true;
}
}
- return result;
+ return false;
}
}
}
diff --git a/Emby.Naming/Audio/AudioFileParser.cs b/Emby.Naming/Audio/AudioFileParser.cs
index 9f21e93dc..25d5f8735 100644
--- a/Emby.Naming/Audio/AudioFileParser.cs
+++ b/Emby.Naming/Audio/AudioFileParser.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.IO;
@@ -8,19 +7,12 @@ using Emby.Naming.Common;
namespace Emby.Naming.Audio
{
- public class AudioFileParser
+ public static class AudioFileParser
{
- private readonly NamingOptions _options;
-
- public AudioFileParser(NamingOptions options)
- {
- _options = options;
- }
-
- public bool IsAudioFile(string path)
+ public static bool IsAudioFile(string path, NamingOptions options)
{
var extension = Path.GetExtension(path) ?? string.Empty;
- return _options.AudioFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase);
+ return options.AudioFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase);
}
}
}
diff --git a/Emby.Naming/Audio/MultiPartResult.cs b/Emby.Naming/Audio/MultiPartResult.cs
deleted file mode 100644
index 8f68d97fa..000000000
--- a/Emby.Naming/Audio/MultiPartResult.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-#pragma warning disable CS1591
-#pragma warning disable SA1600
-
-namespace Emby.Naming.Audio
-{
- public class MultiPartResult
- {
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the part.
- /// </summary>
- /// <value>The part.</value>
- public string Part { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is multi part.
- /// </summary>
- /// <value><c>true</c> if this instance is multi part; otherwise, <c>false</c>.</value>
- public bool IsMultiPart { get; set; }
- }
-}
diff --git a/Emby.Naming/AudioBook/AudioBookFileInfo.cs b/Emby.Naming/AudioBook/AudioBookFileInfo.cs
index 769e3d7fa..0bc6ec7e4 100644
--- a/Emby.Naming/AudioBook/AudioBookFileInfo.cs
+++ b/Emby.Naming/AudioBook/AudioBookFileInfo.cs
@@ -32,7 +32,7 @@ namespace Emby.Naming.AudioBook
public int? ChapterNumber { get; set; }
/// <summary>
- /// Gets or sets the type.
+ /// Gets or sets a value indicating whether this instance is a directory.
/// </summary>
/// <value>The type.</value>
public bool IsDirectory { get; set; }
diff --git a/Emby.Naming/AudioBook/AudioBookFilePathParser.cs b/Emby.Naming/AudioBook/AudioBookFilePathParser.cs
index 8dc2e1b97..5494df9d6 100644
--- a/Emby.Naming/AudioBook/AudioBookFilePathParser.cs
+++ b/Emby.Naming/AudioBook/AudioBookFilePathParser.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Globalization;
diff --git a/Emby.Naming/AudioBook/AudioBookFilePathParserResult.cs b/Emby.Naming/AudioBook/AudioBookFilePathParserResult.cs
index 68d6ca4d4..e28a58db7 100644
--- a/Emby.Naming/AudioBook/AudioBookFilePathParserResult.cs
+++ b/Emby.Naming/AudioBook/AudioBookFilePathParserResult.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
namespace Emby.Naming.AudioBook
{
diff --git a/Emby.Naming/AudioBook/AudioBookListResolver.cs b/Emby.Naming/AudioBook/AudioBookListResolver.cs
index 97f359285..081510f95 100644
--- a/Emby.Naming/AudioBook/AudioBookListResolver.cs
+++ b/Emby.Naming/AudioBook/AudioBookListResolver.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System.Collections.Generic;
using System.Linq;
@@ -39,9 +38,7 @@ namespace Emby.Naming.AudioBook
var stackResult = new StackResolver(_options)
.ResolveAudioBooks(metadata);
- var list = new List<AudioBookInfo>();
-
- foreach (var stack in stackResult.Stacks)
+ foreach (var stack in stackResult)
{
var stackFiles = stack.Files.Select(i => audioBookResolver.Resolve(i, stack.IsDirectoryStack)).ToList();
stackFiles.Sort();
@@ -50,20 +47,9 @@ namespace Emby.Naming.AudioBook
Files = stackFiles,
Name = stack.Name
};
- list.Add(info);
- }
-
- // Whatever files are left, just add them
- /*list.AddRange(remainingFiles.Select(i => new AudioBookInfo
- {
- Files = new List<AudioBookFileInfo> { i },
- Name = i.,
- Year = i.Year
- }));*/
-
- var orderedList = list.OrderBy(i => i.Name);
- return orderedList;
+ yield return info;
+ }
}
}
}
diff --git a/Emby.Naming/AudioBook/AudioBookResolver.cs b/Emby.Naming/AudioBook/AudioBookResolver.cs
index 0b0d2035e..5466b4637 100644
--- a/Emby.Naming/AudioBook/AudioBookResolver.cs
+++ b/Emby.Naming/AudioBook/AudioBookResolver.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.IO;
diff --git a/Emby.Naming/Common/EpisodeExpression.cs b/Emby.Naming/Common/EpisodeExpression.cs
index 30a74fb65..07de72851 100644
--- a/Emby.Naming/Common/EpisodeExpression.cs
+++ b/Emby.Naming/Common/EpisodeExpression.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Text.RegularExpressions;
@@ -11,6 +10,24 @@ namespace Emby.Naming.Common
private string _expression;
private Regex _regex;
+ public EpisodeExpression(string expression, bool byDate)
+ {
+ Expression = expression;
+ IsByDate = byDate;
+ DateTimeFormats = Array.Empty<string>();
+ SupportsAbsoluteEpisodeNumbers = true;
+ }
+
+ public EpisodeExpression(string expression)
+ : this(expression, false)
+ {
+ }
+
+ public EpisodeExpression()
+ : this(null)
+ {
+ }
+
public string Expression
{
get => _expression;
@@ -32,23 +49,5 @@ namespace Emby.Naming.Common
public string[] DateTimeFormats { get; set; }
public Regex Regex => _regex ?? (_regex = new Regex(Expression, RegexOptions.IgnoreCase | RegexOptions.Compiled));
-
- public EpisodeExpression(string expression, bool byDate)
- {
- Expression = expression;
- IsByDate = byDate;
- DateTimeFormats = Array.Empty<string>();
- SupportsAbsoluteEpisodeNumbers = true;
- }
-
- public EpisodeExpression(string expression)
- : this(expression, false)
- {
- }
-
- public EpisodeExpression()
- : this(null)
- {
- }
}
}
diff --git a/Emby.Naming/Common/MediaType.cs b/Emby.Naming/Common/MediaType.cs
index a61f10489..cc18ce4cd 100644
--- a/Emby.Naming/Common/MediaType.cs
+++ b/Emby.Naming/Common/MediaType.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
namespace Emby.Naming.Common
{
diff --git a/Emby.Naming/Common/NamingOptions.cs b/Emby.Naming/Common/NamingOptions.cs
index 69e68660d..793847f84 100644
--- a/Emby.Naming/Common/NamingOptions.cs
+++ b/Emby.Naming/Common/NamingOptions.cs
@@ -1,55 +1,15 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Linq;
using System.Text.RegularExpressions;
using Emby.Naming.Video;
+using MediaBrowser.Model.Entities;
namespace Emby.Naming.Common
{
public class NamingOptions
{
- public string[] AudioFileExtensions { get; set; }
-
- public string[] AlbumStackingPrefixes { get; set; }
-
- public string[] SubtitleFileExtensions { get; set; }
-
- public char[] SubtitleFlagDelimiters { get; set; }
-
- public string[] SubtitleForcedFlags { get; set; }
-
- public string[] SubtitleDefaultFlags { get; set; }
-
- public EpisodeExpression[] EpisodeExpressions { get; set; }
-
- public string[] EpisodeWithoutSeasonExpressions { get; set; }
-
- public string[] EpisodeMultiPartExpressions { get; set; }
-
- public string[] VideoFileExtensions { get; set; }
-
- public string[] StubFileExtensions { get; set; }
-
- public string[] AudioBookPartsExpressions { get; set; }
-
- public StubTypeRule[] StubTypes { get; set; }
-
- public char[] VideoFlagDelimiters { get; set; }
-
- public Format3DRule[] Format3DRules { get; set; }
-
- public string[] VideoFileStackingExpressions { get; set; }
-
- public string[] CleanDateTimes { get; set; }
-
- public string[] CleanStrings { get; set; }
-
- public EpisodeExpression[] MultipleEpisodeExpressions { get; set; }
-
- public ExtraRule[] VideoExtraRules { get; set; }
-
public NamingOptions()
{
VideoFileExtensions = new[]
@@ -176,13 +136,12 @@ namespace Emby.Naming.Common
CleanDateTimes = new[]
{
- @"(.+[^ _\,\.\(\)\[\]\-])[ _\.\(\)\[\]\-]+(19[0-9][0-9]|20[0-1][0-9])([ _\,\.\(\)\[\]\-][^0-9]|$)"
+ @"(.+[^_\,\.\(\)\[\]\-])[_\.\(\)\[\]\-](19\d{2}|20\d{2})([ _\,\.\(\)\[\]\-][^0-9]|).*(19\d{2}|20\d{2})*"
};
CleanStrings = new[]
{
- @"[ _\,\.\(\)\[\]\-](ac3|dts|custom|dc|divx|divx5|dsr|dsrip|dutch|dvd|dvdrip|dvdscr|dvdscreener|screener|dvdivx|cam|fragment|fs|hdtv|hdrip|hdtvrip|internal|limited|multisubs|ntsc|ogg|ogm|pal|pdtv|proper|repack|rerip|retail|cd[1-9]|r3|r5|bd5|se|svcd|swedish|german|read.nfo|nfofix|unrated|ws|telesync|ts|telecine|tc|brrip|bdrip|480p|480i|576p|576i|720p|720i|1080p|1080i|2160p|hrhd|hrhdtv|hddvd|bluray|x264|h264|xvid|xvidvd|xxx|www.www|\[.*\])([ _\,\.\(\)\[\]\-]|$)",
- @"[ _\,\.\(\)\[\]\-](3d|sbs|tab|hsbs|htab|mvc|\[.*\])([ _\,\.\(\)\[\]\-]|$)",
+ @"[ _\,\.\(\)\[\]\-](3d|sbs|tab|hsbs|htab|mvc|HDR|HDC|UHD|UltraHD|4k|ac3|dts|custom|dc|divx|divx5|dsr|dsrip|dutch|dvd|dvdrip|dvdscr|dvdscreener|screener|dvdivx|cam|fragment|fs|hdtv|hdrip|hdtvrip|internal|limited|multisubs|ntsc|ogg|ogm|pal|pdtv|proper|repack|rerip|retail|cd[1-9]|r3|r5|bd5|se|svcd|swedish|german|read.nfo|nfofix|unrated|ws|telesync|ts|telecine|tc|brrip|bdrip|480p|480i|576p|576i|720p|720i|1080p|1080i|2160p|hrhd|hrhdtv|hddvd|bluray|x264|h264|xvid|xvidvd|xxx|www.www|\[.*\])([ _\,\.\(\)\[\]\-]|$)",
@"(\[.*\])"
};
@@ -317,7 +276,7 @@ namespace Emby.Naming.Common
// This isn't a Kodi naming rule, but the expression below causes false positives,
// so we make sure this one gets tested first.
// "Foo Bar 889"
- new EpisodeExpression(@".*[\\\/](?![Ee]pisode)(?<seriesname>[\w\s]+?)\s(?<epnumber>\d{1,3})(-(?<endingepnumber>\d{2,3}))*[^\\\/]*$")
+ new EpisodeExpression(@".*[\\\/](?![Ee]pisode)(?<seriesname>[\w\s]+?)\s(?<epnumber>\d{1,3})(-(?<endingepnumber>\d{2,3}))*[^\\\/x]*$")
{
IsNamed = true
},
@@ -339,7 +298,7 @@ namespace Emby.Naming.Common
// *** End Kodi Standard Naming
-                // [bar] Foo - 1 [baz]
+ // [bar] Foo - 1 [baz]
new EpisodeExpression(@".*?(\[.*?\])+.*?(?<seriesname>[\w\s]+?)[-\s_]+(?<epnumber>\d+).*$")
{
IsNamed = true
@@ -423,126 +382,126 @@ namespace Emby.Naming.Common
{
new ExtraRule
{
- ExtraType = "trailer",
+ ExtraType = ExtraType.Trailer,
RuleType = ExtraRuleType.Filename,
Token = "trailer",
MediaType = MediaType.Video
},
new ExtraRule
{
- ExtraType = "trailer",
+ ExtraType = ExtraType.Trailer,
RuleType = ExtraRuleType.Suffix,
Token = "-trailer",
MediaType = MediaType.Video
},
new ExtraRule
{
- ExtraType = "trailer",
+ ExtraType = ExtraType.Trailer,
RuleType = ExtraRuleType.Suffix,
Token = ".trailer",
MediaType = MediaType.Video
},
new ExtraRule
{
- ExtraType = "trailer",
+ ExtraType = ExtraType.Trailer,
RuleType = ExtraRuleType.Suffix,
Token = "_trailer",
MediaType = MediaType.Video
},
new ExtraRule
{
- ExtraType = "trailer",
+ ExtraType = ExtraType.Trailer,
RuleType = ExtraRuleType.Suffix,
Token = " trailer",
MediaType = MediaType.Video
},
new ExtraRule
{
- ExtraType = "sample",
+ ExtraType = ExtraType.Sample,
RuleType = ExtraRuleType.Filename,
Token = "sample",
MediaType = MediaType.Video
},
new ExtraRule
{
- ExtraType = "sample",
+ ExtraType = ExtraType.Sample,
RuleType = ExtraRuleType.Suffix,
Token = "-sample",
MediaType = MediaType.Video
},
new ExtraRule
{
- ExtraType = "sample",
+ ExtraType = ExtraType.Sample,
RuleType = ExtraRuleType.Suffix,
Token = ".sample",
MediaType = MediaType.Video
},
new ExtraRule
{
- ExtraType = "sample",
+ ExtraType = ExtraType.Sample,
RuleType = ExtraRuleType.Suffix,
Token = "_sample",
MediaType = MediaType.Video
},
new ExtraRule
{
- ExtraType = "sample",
+ ExtraType = ExtraType.Sample,
RuleType = ExtraRuleType.Suffix,
Token = " sample",
MediaType = MediaType.Video
},
new ExtraRule
{
- ExtraType = "themesong",
+ ExtraType = ExtraType.ThemeSong,
RuleType = ExtraRuleType.Filename,
Token = "theme",
MediaType = MediaType.Audio
},
new ExtraRule
{
- ExtraType = "scene",
+ ExtraType = ExtraType.Scene,
RuleType = ExtraRuleType.Suffix,
Token = "-scene",
MediaType = MediaType.Video
},
new ExtraRule
{
- ExtraType = "clip",
+ ExtraType = ExtraType.Clip,
RuleType = ExtraRuleType.Suffix,
Token = "-clip",
MediaType = MediaType.Video
},
new ExtraRule
{
- ExtraType = "interview",
+ ExtraType = ExtraType.Interview,
RuleType = ExtraRuleType.Suffix,
Token = "-interview",
MediaType = MediaType.Video
},
new ExtraRule
{
- ExtraType = "behindthescenes",
+ ExtraType = ExtraType.BehindTheScenes,
RuleType = ExtraRuleType.Suffix,
Token = "-behindthescenes",
MediaType = MediaType.Video
},
new ExtraRule
{
- ExtraType = "deletedscene",
+ ExtraType = ExtraType.DeletedScene,
RuleType = ExtraRuleType.Suffix,
Token = "-deleted",
MediaType = MediaType.Video
},
new ExtraRule
{
- ExtraType = "featurette",
+ ExtraType = ExtraType.Clip,
RuleType = ExtraRuleType.Suffix,
Token = "-featurette",
MediaType = MediaType.Video
},
new ExtraRule
{
- ExtraType = "short",
+ ExtraType = ExtraType.Clip,
RuleType = ExtraRuleType.Suffix,
Token = "-short",
MediaType = MediaType.Video
@@ -681,11 +640,54 @@ namespace Emby.Naming.Common
Compile();
}
+ public string[] AudioFileExtensions { get; set; }
+
+ public string[] AlbumStackingPrefixes { get; set; }
+
+ public string[] SubtitleFileExtensions { get; set; }
+
+ public char[] SubtitleFlagDelimiters { get; set; }
+
+ public string[] SubtitleForcedFlags { get; set; }
+
+ public string[] SubtitleDefaultFlags { get; set; }
+
+ public EpisodeExpression[] EpisodeExpressions { get; set; }
+
+ public string[] EpisodeWithoutSeasonExpressions { get; set; }
+
+ public string[] EpisodeMultiPartExpressions { get; set; }
+
+ public string[] VideoFileExtensions { get; set; }
+
+ public string[] StubFileExtensions { get; set; }
+
+ public string[] AudioBookPartsExpressions { get; set; }
+
+ public StubTypeRule[] StubTypes { get; set; }
+
+ public char[] VideoFlagDelimiters { get; set; }
+
+ public Format3DRule[] Format3DRules { get; set; }
+
+ public string[] VideoFileStackingExpressions { get; set; }
+
+ public string[] CleanDateTimes { get; set; }
+
+ public string[] CleanStrings { get; set; }
+
+ public EpisodeExpression[] MultipleEpisodeExpressions { get; set; }
+
+ public ExtraRule[] VideoExtraRules { get; set; }
+
public Regex[] VideoFileStackingRegexes { get; private set; }
+
public Regex[] CleanDateTimeRegexes { get; private set; }
+
public Regex[] CleanStringRegexes { get; private set; }
public Regex[] EpisodeWithoutSeasonRegexes { get; private set; }
+
public Regex[] EpisodeMultiPartRegexes { get; private set; }
public void Compile()
diff --git a/Emby.Naming/Emby.Naming.csproj b/Emby.Naming/Emby.Naming.csproj
index ed1670adf..4e08170a4 100644
--- a/Emby.Naming/Emby.Naming.csproj
+++ b/Emby.Naming/Emby.Naming.csproj
@@ -4,9 +4,6 @@
<TargetFramework>netstandard2.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
- </PropertyGroup>
-
- <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
@@ -27,7 +24,7 @@
<!-- Code Analyzers-->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
- <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.7" PrivateAssets="All" />
+ <!-- TODO: <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/Emby.Naming/Subtitles/SubtitleInfo.cs b/Emby.Naming/Subtitles/SubtitleInfo.cs
index fe42846c6..f39c496b7 100644
--- a/Emby.Naming/Subtitles/SubtitleInfo.cs
+++ b/Emby.Naming/Subtitles/SubtitleInfo.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
namespace Emby.Naming.Subtitles
{
diff --git a/Emby.Naming/Subtitles/SubtitleParser.cs b/Emby.Naming/Subtitles/SubtitleParser.cs
index 99680c622..082696da4 100644
--- a/Emby.Naming/Subtitles/SubtitleParser.cs
+++ b/Emby.Naming/Subtitles/SubtitleParser.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.IO;
@@ -31,7 +30,6 @@ namespace Emby.Naming.Subtitles
}
var flags = GetFlags(path);
-
var info = new SubtitleInfo
{
Path = path,
@@ -45,7 +43,7 @@ namespace Emby.Naming.Subtitles
// Should have a name, language and file extension
if (parts.Count >= 3)
{
- info.Language = parts[parts.Count - 2];
+ info.Language = parts[^2];
}
return info;
diff --git a/Emby.Naming/TV/EpisodeInfo.cs b/Emby.Naming/TV/EpisodeInfo.cs
index 667129a57..250df4e2d 100644
--- a/Emby.Naming/TV/EpisodeInfo.cs
+++ b/Emby.Naming/TV/EpisodeInfo.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
namespace Emby.Naming.TV
{
diff --git a/Emby.Naming/TV/EpisodePathParser.cs b/Emby.Naming/TV/EpisodePathParser.cs
index 4fac543f9..d3a822b17 100644
--- a/Emby.Naming/TV/EpisodePathParser.cs
+++ b/Emby.Naming/TV/EpisodePathParser.cs
@@ -1,5 +1,5 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
+#nullable enable
using System;
using System.Collections.Generic;
@@ -28,7 +28,7 @@ namespace Emby.Naming.TV
path += ".mp4";
}
- EpisodePathParserResult result = null;
+ EpisodePathParserResult? result = null;
foreach (var expression in _options.EpisodeExpressions)
{
@@ -131,12 +131,12 @@ namespace Emby.Naming.TV
var endingNumberGroup = match.Groups["endingepnumber"];
if (endingNumberGroup.Success)
{
- // Will only set EndingEpsiodeNumber if the captured number is not followed by additional numbers
+ // Will only set EndingEpisodeNumber if the captured number is not followed by additional numbers
// or a 'p' or 'i' as what you would get with a pixel resolution specification.
// It avoids erroneous parsing of something like "series-s09e14-1080p.mkv" as a multi-episode from E14 to E108
int nextIndex = endingNumberGroup.Index + endingNumberGroup.Length;
if (nextIndex >= name.Length
- || "0123456789iIpP".IndexOf(name[nextIndex]) == -1)
+ || !"0123456789iIpP".Contains(name[nextIndex], StringComparison.Ordinal))
{
if (int.TryParse(endingNumberGroup.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out num))
{
diff --git a/Emby.Naming/TV/EpisodePathParserResult.cs b/Emby.Naming/TV/EpisodePathParserResult.cs
index 3acbbc101..05f921edc 100644
--- a/Emby.Naming/TV/EpisodePathParserResult.cs
+++ b/Emby.Naming/TV/EpisodePathParserResult.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
namespace Emby.Naming.TV
{
diff --git a/Emby.Naming/TV/EpisodeResolver.cs b/Emby.Naming/TV/EpisodeResolver.cs
index 5e115fc75..6994f69fc 100644
--- a/Emby.Naming/TV/EpisodeResolver.cs
+++ b/Emby.Naming/TV/EpisodeResolver.cs
@@ -1,5 +1,5 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
+#nullable enable
using System;
using System.IO;
@@ -18,7 +18,7 @@ namespace Emby.Naming.TV
_options = options;
}
- public EpisodeInfo Resolve(
+ public EpisodeInfo? Resolve(
string path,
bool isDirectory,
bool? isNamed = null,
@@ -26,14 +26,9 @@ namespace Emby.Naming.TV
bool? supportsAbsoluteNumbers = null,
bool fillExtendedInfo = true)
{
- if (string.IsNullOrEmpty(path))
- {
- throw new ArgumentNullException(nameof(path));
- }
-
bool isStub = false;
- string container = null;
- string stubType = null;
+ string? container = null;
+ string? stubType = null;
if (!isDirectory)
{
@@ -41,17 +36,13 @@ namespace Emby.Naming.TV
// Check supported extensions
if (!_options.VideoFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
{
- var stubResult = StubResolver.ResolveFile(path, _options);
-
- isStub = stubResult.IsStub;
-
// It's not supported. Check stub extensions
- if (!isStub)
+ if (!StubResolver.TryResolveFile(path, _options, out stubType))
{
return null;
}
- stubType = stubResult.StubType;
+ isStub = true;
}
container = extension.TrimStart('.');
diff --git a/Emby.Naming/TV/SeasonPathParser.cs b/Emby.Naming/TV/SeasonPathParser.cs
index e5f90e966..2fa6b4353 100644
--- a/Emby.Naming/TV/SeasonPathParser.cs
+++ b/Emby.Naming/TV/SeasonPathParser.cs
@@ -1,32 +1,13 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Globalization;
using System.IO;
-using System.Linq;
namespace Emby.Naming.TV
{
- public class SeasonPathParser
+ public static class SeasonPathParser
{
- public SeasonPathParserResult Parse(string path, bool supportSpecialAliases, bool supportNumericSeasonFolders)
- {
- var result = new SeasonPathParserResult();
-
- var seasonNumberInfo = GetSeasonNumberFromPath(path, supportSpecialAliases, supportNumericSeasonFolders);
-
- result.SeasonNumber = seasonNumberInfo.seasonNumber;
-
- if (result.SeasonNumber.HasValue)
- {
- result.Success = true;
- result.IsSeasonFolder = seasonNumberInfo.isSeasonFolder;
- }
-
- return result;
- }
-
/// <summary>
/// A season folder must contain one of these somewhere in the name.
/// </summary>
@@ -42,6 +23,23 @@ namespace Emby.Naming.TV
"stagione"
};
+ public static SeasonPathParserResult Parse(string path, bool supportSpecialAliases, bool supportNumericSeasonFolders)
+ {
+ var result = new SeasonPathParserResult();
+
+ var (seasonNumber, isSeasonFolder) = GetSeasonNumberFromPath(path, supportSpecialAliases, supportNumericSeasonFolders);
+
+ result.SeasonNumber = seasonNumber;
+
+ if (result.SeasonNumber.HasValue)
+ {
+ result.Success = true;
+ result.IsSeasonFolder = isSeasonFolder;
+ }
+
+ return result;
+ }
+
/// <summary>
/// Gets the season number from path.
/// </summary>
@@ -90,12 +88,10 @@ namespace Emby.Naming.TV
// Look for one of the season folder names
foreach (var name in _seasonFolderNames)
{
- var index = filename.IndexOf(name, StringComparison.OrdinalIgnoreCase);
-
- if (index != -1)
+ if (filename.Contains(name, StringComparison.OrdinalIgnoreCase))
{
var result = GetSeasonNumberFromPathSubstring(filename.Replace(name, " ", StringComparison.OrdinalIgnoreCase));
- if (result.Item1.HasValue)
+ if (result.seasonNumber.HasValue)
{
return result;
}
@@ -105,25 +101,32 @@ namespace Emby.Naming.TV
}
var parts = filename.Split(new[] { '.', '_', ' ', '-' }, StringSplitOptions.RemoveEmptyEntries);
- var resultNumber = parts.Select(GetSeasonNumberFromPart).FirstOrDefault(i => i.HasValue);
- return (resultNumber, true);
+ for (int i = 0; i < parts.Length; i++)
+ {
+ if (TryGetSeasonNumberFromPart(parts[i], out int seasonNumber))
+ {
+ return (seasonNumber, true);
+ }
+ }
+
+ return (null, true);
}
- private static int? GetSeasonNumberFromPart(string part)
+ private static bool TryGetSeasonNumberFromPart(ReadOnlySpan<char> part, out int seasonNumber)
{
+ seasonNumber = 0;
if (part.Length < 2 || !part.StartsWith("s", StringComparison.OrdinalIgnoreCase))
{
- return null;
+ return false;
}
- part = part.Substring(1);
-
- if (int.TryParse(part, NumberStyles.Integer, CultureInfo.InvariantCulture, out var value))
+ if (int.TryParse(part.Slice(1), NumberStyles.Integer, CultureInfo.InvariantCulture, out var value))
{
- return value;
+ seasonNumber = value;
+ return true;
}
- return null;
+ return false;
}
/// <summary>
@@ -131,7 +134,7 @@ namespace Emby.Naming.TV
/// </summary>
/// <param name="path">The path.</param>
/// <returns>System.Nullable{System.Int32}.</returns>
- private static (int? seasonNumber, bool isSeasonFolder) GetSeasonNumberFromPathSubstring(string path)
+ private static (int? seasonNumber, bool isSeasonFolder) GetSeasonNumberFromPathSubstring(ReadOnlySpan<char> path)
{
var numericStart = -1;
var length = 0;
@@ -142,7 +145,7 @@ namespace Emby.Naming.TV
// Find out where the numbers start, and then keep going until they end
for (var i = 0; i < path.Length; i++)
{
- if (char.IsNumber(path, i))
+ if (char.IsNumber(path[i]))
{
if (!hasOpenParenth)
{
@@ -150,6 +153,7 @@ namespace Emby.Naming.TV
{
numericStart = i;
}
+
length++;
}
}
@@ -161,11 +165,11 @@ namespace Emby.Naming.TV
}
var currentChar = path[i];
- if (currentChar.Equals('('))
+ if (currentChar == '(')
{
hasOpenParenth = true;
}
- else if (currentChar.Equals(')'))
+ else if (currentChar == ')')
{
hasOpenParenth = false;
}
@@ -176,7 +180,7 @@ namespace Emby.Naming.TV
return (null, isSeasonFolder);
}
- return (int.Parse(path.Substring(numericStart, length), CultureInfo.InvariantCulture), isSeasonFolder);
+ return (int.Parse(path.Slice(numericStart, length), provider: CultureInfo.InvariantCulture), isSeasonFolder);
}
}
}
diff --git a/Emby.Naming/TV/SeasonPathParserResult.cs b/Emby.Naming/TV/SeasonPathParserResult.cs
index 57c234754..44090c059 100644
--- a/Emby.Naming/TV/SeasonPathParserResult.cs
+++ b/Emby.Naming/TV/SeasonPathParserResult.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
namespace Emby.Naming.TV
{
diff --git a/Emby.Naming/Video/CleanDateTimeParser.cs b/Emby.Naming/Video/CleanDateTimeParser.cs
index a9db4cccc..579c9e91e 100644
--- a/Emby.Naming/Video/CleanDateTimeParser.cs
+++ b/Emby.Naming/Video/CleanDateTimeParser.cs
@@ -1,89 +1,47 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
+#nullable enable
-using System;
+using System.Collections.Generic;
using System.Globalization;
-using System.IO;
-using System.Linq;
using System.Text.RegularExpressions;
-using Emby.Naming.Common;
namespace Emby.Naming.Video
{
/// <summary>
/// <see href="http://kodi.wiki/view/Advancedsettings.xml#video" />.
/// </summary>
- public class CleanDateTimeParser
+ public static class CleanDateTimeParser
{
- private readonly NamingOptions _options;
-
- public CleanDateTimeParser(NamingOptions options)
+ public static CleanDateTimeResult Clean(string name, IReadOnlyList<Regex> cleanDateTimeRegexes)
{
- _options = options;
- }
-
- public CleanDateTimeResult Clean(string name)
- {
- var originalName = name;
-
- try
+ CleanDateTimeResult result = new CleanDateTimeResult(name);
+ var len = cleanDateTimeRegexes.Count;
+ for (int i = 0; i < len; i++)
{
- var extension = Path.GetExtension(name) ?? string.Empty;
- // Check supported extensions
- if (!_options.VideoFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)
- && !_options.AudioFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
+ if (TryClean(name, cleanDateTimeRegexes[i], ref result))
{
- // Dummy up a file extension because the expressions will fail without one
- // This is tricky because we can't just check Path.GetExtension for empty
- // If the input is "St. Vincent (2014)", it will produce ". Vincent (2014)" as the extension
- name += ".mkv";
+ return result;
}
}
- catch (ArgumentException)
- {
- }
-
- var result = _options.CleanDateTimeRegexes.Select(i => Clean(name, i))
- .FirstOrDefault(i => i.HasChanged) ??
- new CleanDateTimeResult { Name = originalName };
-
- if (result.HasChanged)
- {
- return result;
- }
-
- // Make a second pass, running clean string first
- var cleanStringResult = new CleanStringParser().Clean(name, _options.CleanStringRegexes);
- if (!cleanStringResult.HasChanged)
- {
- return result;
- }
-
- return _options.CleanDateTimeRegexes.Select(i => Clean(cleanStringResult.Name, i))
- .FirstOrDefault(i => i.HasChanged) ??
- result;
+ return result;
}
- private static CleanDateTimeResult Clean(string name, Regex expression)
+ private static bool TryClean(string name, Regex expression, ref CleanDateTimeResult result)
{
- var result = new CleanDateTimeResult();
-
var match = expression.Match(name);
if (match.Success
- && match.Groups.Count == 4
+ && match.Groups.Count == 5
&& match.Groups[1].Success
&& match.Groups[2].Success
&& int.TryParse(match.Groups[2].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var year))
{
- name = match.Groups[1].Value;
- result.Year = year;
- result.HasChanged = true;
+ result = new CleanDateTimeResult(match.Groups[1].Value.TrimEnd(), year);
+ return true;
}
- result.Name = name;
- return result;
+ return false;
}
}
}
diff --git a/Emby.Naming/Video/CleanDateTimeResult.cs b/Emby.Naming/Video/CleanDateTimeResult.cs
index a7581972e..57eeaa7e3 100644
--- a/Emby.Naming/Video/CleanDateTimeResult.cs
+++ b/Emby.Naming/Video/CleanDateTimeResult.cs
@@ -1,26 +1,32 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
+#nullable enable
namespace Emby.Naming.Video
{
- public class CleanDateTimeResult
+ public readonly struct CleanDateTimeResult
{
+ public CleanDateTimeResult(string name, int? year)
+ {
+ Name = name;
+ Year = year;
+ }
+
+ public CleanDateTimeResult(string name)
+ {
+ Name = name;
+ Year = null;
+ }
+
/// <summary>
- /// Gets or sets the name.
+ /// Gets the name.
/// </summary>
/// <value>The name.</value>
- public string Name { get; set; }
+ public string Name { get; }
/// <summary>
- /// Gets or sets the year.
+ /// Gets the year.
/// </summary>
/// <value>The year.</value>
- public int? Year { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance has changed.
- /// </summary>
- /// <value><c>true</c> if this instance has changed; otherwise, <c>false</c>.</value>
- public bool HasChanged { get; set; }
+ public int? Year { get; }
}
}
diff --git a/Emby.Naming/Video/CleanStringParser.cs b/Emby.Naming/Video/CleanStringParser.cs
index be028c662..3f584d584 100644
--- a/Emby.Naming/Video/CleanStringParser.cs
+++ b/Emby.Naming/Video/CleanStringParser.cs
@@ -1,52 +1,44 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
+#nullable enable
+using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace Emby.Naming.Video
{
/// <summary>
- /// http://kodi.wiki/view/Advancedsettings.xml#video
+ /// <see href="http://kodi.wiki/view/Advancedsettings.xml#video" />.
/// </summary>
- public class CleanStringParser
+ public static class CleanStringParser
{
- public CleanStringResult Clean(string name, IEnumerable<Regex> expressions)
+ public static bool TryClean(string name, IReadOnlyList<Regex> expressions, out ReadOnlySpan<char> newName)
{
- var hasChanged = false;
-
- foreach (var exp in expressions)
+ var len = expressions.Count;
+ for (int i = 0; i < len; i++)
{
- var result = Clean(name, exp);
-
- if (!string.IsNullOrEmpty(result.Name))
+ if (TryClean(name, expressions[i], out newName))
{
- name = result.Name;
- hasChanged = hasChanged || result.HasChanged;
+ return true;
}
}
- return new CleanStringResult
- {
- Name = name,
- HasChanged = hasChanged
- };
+ newName = ReadOnlySpan<char>.Empty;
+ return false;
}
- private static CleanStringResult Clean(string name, Regex expression)
+ private static bool TryClean(string name, Regex expression, out ReadOnlySpan<char> newName)
{
- var result = new CleanStringResult();
-
var match = expression.Match(name);
-
- if (match.Success)
+ int index = match.Index;
+ if (match.Success && index != 0)
{
- result.HasChanged = true;
- name = name.Substring(0, match.Index);
+ newName = name.AsSpan().Slice(0, match.Index);
+ return true;
}
- result.Name = name;
- return result;
+ newName = string.Empty;
+ return false;
}
}
}
diff --git a/Emby.Naming/Video/CleanStringResult.cs b/Emby.Naming/Video/CleanStringResult.cs
deleted file mode 100644
index 786fe9e02..000000000
--- a/Emby.Naming/Video/CleanStringResult.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-#pragma warning disable CS1591
-#pragma warning disable SA1600
-
-namespace Emby.Naming.Video
-{
- public class CleanStringResult
- {
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance has changed.
- /// </summary>
- /// <value><c>true</c> if this instance has changed; otherwise, <c>false</c>.</value>
- public bool HasChanged { get; set; }
- }
-}
diff --git a/Emby.Naming/Video/ExtraResolver.cs b/Emby.Naming/Video/ExtraResolver.cs
index 989ede206..42a5c88b3 100644
--- a/Emby.Naming/Video/ExtraResolver.cs
+++ b/Emby.Naming/Video/ExtraResolver.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.IO;
@@ -23,7 +22,7 @@ namespace Emby.Naming.Video
{
return _options.VideoExtraRules
.Select(i => GetExtraInfo(path, i))
- .FirstOrDefault(i => !string.IsNullOrEmpty(i.ExtraType)) ?? new ExtraResult();
+ .FirstOrDefault(i => i.ExtraType != null) ?? new ExtraResult();
}
private ExtraResult GetExtraInfo(string path, ExtraRule rule)
@@ -32,7 +31,7 @@ namespace Emby.Naming.Video
if (rule.MediaType == MediaType.Audio)
{
- if (!new AudioFileParser(_options).IsAudioFile(path))
+ if (!AudioFileParser.IsAudioFile(path, _options))
{
return result;
}
diff --git a/Emby.Naming/Video/ExtraResult.cs b/Emby.Naming/Video/ExtraResult.cs
index 6081a4494..15db32e87 100644
--- a/Emby.Naming/Video/ExtraResult.cs
+++ b/Emby.Naming/Video/ExtraResult.cs
@@ -1,5 +1,6 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
+
+using MediaBrowser.Model.Entities;
namespace Emby.Naming.Video
{
@@ -9,7 +10,7 @@ namespace Emby.Naming.Video
/// Gets or sets the type of the extra.
/// </summary>
/// <value>The type of the extra.</value>
- public string ExtraType { get; set; }
+ public ExtraType? ExtraType { get; set; }
/// <summary>
/// Gets or sets the rule.
diff --git a/Emby.Naming/Video/ExtraRule.cs b/Emby.Naming/Video/ExtraRule.cs
index cfce79fd0..cb58a3934 100644
--- a/Emby.Naming/Video/ExtraRule.cs
+++ b/Emby.Naming/Video/ExtraRule.cs
@@ -1,7 +1,7 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
-using Emby.Naming.Common;
+using MediaBrowser.Model.Entities;
+using MediaType = Emby.Naming.Common.MediaType;
namespace Emby.Naming.Video
{
@@ -17,7 +17,7 @@ namespace Emby.Naming.Video
/// Gets or sets the type of the extra.
/// </summary>
/// <value>The type of the extra.</value>
- public string ExtraType { get; set; }
+ public ExtraType ExtraType { get; set; }
/// <summary>
/// Gets or sets the type of the rule.
diff --git a/Emby.Naming/Video/ExtraRuleType.cs b/Emby.Naming/Video/ExtraRuleType.cs
index 2bf2799ff..b021a04a3 100644
--- a/Emby.Naming/Video/ExtraRuleType.cs
+++ b/Emby.Naming/Video/ExtraRuleType.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
namespace Emby.Naming.Video
{
diff --git a/Emby.Naming/Video/FileStack.cs b/Emby.Naming/Video/FileStack.cs
index 56adf6add..3ef190b86 100644
--- a/Emby.Naming/Video/FileStack.cs
+++ b/Emby.Naming/Video/FileStack.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Naming/Video/FlagParser.cs b/Emby.Naming/Video/FlagParser.cs
index acf3438c2..a8bd9d5c5 100644
--- a/Emby.Naming/Video/FlagParser.cs
+++ b/Emby.Naming/Video/FlagParser.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.IO;
diff --git a/Emby.Naming/Video/Format3DParser.cs b/Emby.Naming/Video/Format3DParser.cs
index 25905f33c..51c26af86 100644
--- a/Emby.Naming/Video/Format3DParser.cs
+++ b/Emby.Naming/Video/Format3DParser.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Linq;
diff --git a/Emby.Naming/Video/Format3DResult.cs b/Emby.Naming/Video/Format3DResult.cs
index 6ebd72f6b..fa0e9d3b8 100644
--- a/Emby.Naming/Video/Format3DResult.cs
+++ b/Emby.Naming/Video/Format3DResult.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System.Collections.Generic;
diff --git a/Emby.Naming/Video/Format3DRule.cs b/Emby.Naming/Video/Format3DRule.cs
index ae9fb5b19..310ec84e8 100644
--- a/Emby.Naming/Video/Format3DRule.cs
+++ b/Emby.Naming/Video/Format3DRule.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
namespace Emby.Naming.Video
{
diff --git a/Emby.Naming/Video/StackResolver.cs b/Emby.Naming/Video/StackResolver.cs
index e7a769ae6..ee05904c7 100644
--- a/Emby.Naming/Video/StackResolver.cs
+++ b/Emby.Naming/Video/StackResolver.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
@@ -20,7 +19,7 @@ namespace Emby.Naming.Video
_options = options;
}
- public StackResult ResolveDirectories(IEnumerable<string> files)
+ public IEnumerable<FileStack> ResolveDirectories(IEnumerable<string> files)
{
return Resolve(files.Select(i => new FileSystemMetadata
{
@@ -29,7 +28,7 @@ namespace Emby.Naming.Video
}));
}
- public StackResult ResolveFiles(IEnumerable<string> files)
+ public IEnumerable<FileStack> ResolveFiles(IEnumerable<string> files)
{
return Resolve(files.Select(i => new FileSystemMetadata
{
@@ -38,9 +37,8 @@ namespace Emby.Naming.Video
}));
}
- public StackResult ResolveAudioBooks(IEnumerable<FileSystemMetadata> files)
+ public IEnumerable<FileStack> ResolveAudioBooks(IEnumerable<FileSystemMetadata> files)
{
- var result = new StackResult();
foreach (var directory in files.GroupBy(file => file.IsDirectory ? file.FullName : Path.GetDirectoryName(file.FullName)))
{
var stack = new FileStack()
@@ -58,20 +56,16 @@ namespace Emby.Naming.Video
stack.Files.Add(file.FullName);
}
- result.Stacks.Add(stack);
+ yield return stack;
}
-
- return result;
}
- public StackResult Resolve(IEnumerable<FileSystemMetadata> files)
+ public IEnumerable<FileStack> Resolve(IEnumerable<FileSystemMetadata> files)
{
- var result = new StackResult();
-
var resolver = new VideoResolver(_options);
var list = files
- .Where(i => i.IsDirectory || (resolver.IsVideoFile(i.FullName) || resolver.IsStubFile(i.FullName)))
+ .Where(i => i.IsDirectory || resolver.IsVideoFile(i.FullName) || resolver.IsStubFile(i.FullName))
.OrderBy(i => i.FullName)
.ToList();
@@ -191,17 +185,15 @@ namespace Emby.Naming.Video
if (stack.Files.Count > 1)
{
- result.Stacks.Add(stack);
+ yield return stack;
i += stack.Files.Count - 1;
break;
}
}
}
-
- return result;
}
- private string GetRegexInput(FileSystemMetadata file)
+ private static string GetRegexInput(FileSystemMetadata file)
{
// For directories, dummy up an extension otherwise the expressions will fail
var input = !file.IsDirectory
@@ -211,7 +203,7 @@ namespace Emby.Naming.Video
return Path.GetFileName(input);
}
- private Match FindMatch(FileSystemMetadata input, Regex regex, int offset)
+ private static Match FindMatch(FileSystemMetadata input, Regex regex, int offset)
{
var regexInput = GetRegexInput(input);
diff --git a/Emby.Naming/Video/StackResult.cs b/Emby.Naming/Video/StackResult.cs
deleted file mode 100644
index 31ef2d69c..000000000
--- a/Emby.Naming/Video/StackResult.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-#pragma warning disable CS1591
-#pragma warning disable SA1600
-
-using System.Collections.Generic;
-
-namespace Emby.Naming.Video
-{
- public class StackResult
- {
- public List<FileStack> Stacks { get; set; }
-
- public StackResult()
- {
- Stacks = new List<FileStack>();
- }
- }
-}
diff --git a/Emby.Naming/Video/StubResolver.cs b/Emby.Naming/Video/StubResolver.cs
index bbf399677..f1b5d7bcc 100644
--- a/Emby.Naming/Video/StubResolver.cs
+++ b/Emby.Naming/Video/StubResolver.cs
@@ -1,5 +1,5 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
+#nullable enable
using System;
using System.IO;
@@ -10,25 +10,22 @@ namespace Emby.Naming.Video
{
public static class StubResolver
{
- public static StubResult ResolveFile(string path, NamingOptions options)
+ public static bool TryResolveFile(string path, NamingOptions options, out string? stubType)
{
+ stubType = default;
+
if (path == null)
{
- return default(StubResult);
+ return false;
}
var extension = Path.GetExtension(path);
if (!options.StubFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
{
- return default(StubResult);
+ return false;
}
- var result = new StubResult()
- {
- IsStub = true
- };
-
path = Path.GetFileNameWithoutExtension(path);
var token = Path.GetExtension(path).TrimStart('.');
@@ -36,12 +33,12 @@ namespace Emby.Naming.Video
{
if (string.Equals(rule.Token, token, StringComparison.OrdinalIgnoreCase))
{
- result.StubType = rule.StubType;
- break;
+ stubType = rule.StubType;
+ return true;
}
}
- return result;
+ return true;
}
}
}
diff --git a/Emby.Naming/Video/StubResult.cs b/Emby.Naming/Video/StubResult.cs
index 5ac85528f..1b8e99b0d 100644
--- a/Emby.Naming/Video/StubResult.cs
+++ b/Emby.Naming/Video/StubResult.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
namespace Emby.Naming.Video
{
diff --git a/Emby.Naming/Video/StubTypeRule.cs b/Emby.Naming/Video/StubTypeRule.cs
index 17c3ef8c5..8285cb51a 100644
--- a/Emby.Naming/Video/StubTypeRule.cs
+++ b/Emby.Naming/Video/StubTypeRule.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
namespace Emby.Naming.Video
{
diff --git a/Emby.Naming/Video/VideoFileInfo.cs b/Emby.Naming/Video/VideoFileInfo.cs
index 250a1ec45..aa4f3a35c 100644
--- a/Emby.Naming/Video/VideoFileInfo.cs
+++ b/Emby.Naming/Video/VideoFileInfo.cs
@@ -1,3 +1,5 @@
+using MediaBrowser.Model.Entities;
+
namespace Emby.Naming.Video
{
/// <summary>
@@ -30,10 +32,10 @@ namespace Emby.Naming.Video
public int? Year { get; set; }
/// <summary>
- /// Gets or sets the type of the extra, e.g. trailer, theme song, behing the scenes, etc.
+ /// Gets or sets the type of the extra, e.g. trailer, theme song, behind the scenes, etc.
/// </summary>
/// <value>The type of the extra.</value>
- public string ExtraType { get; set; }
+ public ExtraType? ExtraType { get; set; }
/// <summary>
/// Gets or sets the extra rule.
@@ -66,7 +68,7 @@ namespace Emby.Naming.Video
public string StubType { get; set; }
/// <summary>
- /// Gets or sets the type.
+ /// Gets or sets a value indicating whether this instance is a directory.
/// </summary>
/// <value>The type.</value>
public bool IsDirectory { get; set; }
diff --git a/Emby.Naming/Video/VideoInfo.cs b/Emby.Naming/Video/VideoInfo.cs
index a585bb99a..ea74c40e2 100644
--- a/Emby.Naming/Video/VideoInfo.cs
+++ b/Emby.Naming/Video/VideoInfo.cs
@@ -1,3 +1,4 @@
+using System;
using System.Collections.Generic;
namespace Emby.Naming.Video
@@ -10,11 +11,14 @@ namespace Emby.Naming.Video
/// <summary>
/// Initializes a new instance of the <see cref="VideoInfo" /> class.
/// </summary>
- public VideoInfo()
+ /// <param name="name">The name.</param>
+ public VideoInfo(string name)
{
- Files = new List<VideoFileInfo>();
- Extras = new List<VideoFileInfo>();
- AlternateVersions = new List<VideoFileInfo>();
+ Name = name;
+
+ Files = Array.Empty<VideoFileInfo>();
+ Extras = Array.Empty<VideoFileInfo>();
+ AlternateVersions = Array.Empty<VideoFileInfo>();
}
/// <summary>
@@ -33,18 +37,18 @@ namespace Emby.Naming.Video
/// Gets or sets the files.
/// </summary>
/// <value>The files.</value>
- public List<VideoFileInfo> Files { get; set; }
+ public IReadOnlyList<VideoFileInfo> Files { get; set; }
/// <summary>
/// Gets or sets the extras.
/// </summary>
/// <value>The extras.</value>
- public List<VideoFileInfo> Extras { get; set; }
+ public IReadOnlyList<VideoFileInfo> Extras { get; set; }
/// <summary>
/// Gets or sets the alternate versions.
/// </summary>
/// <value>The alternate versions.</value>
- public List<VideoFileInfo> AlternateVersions { get; set; }
+ public IReadOnlyList<VideoFileInfo> AlternateVersions { get; set; }
}
}
diff --git a/Emby.Naming/Video/VideoListResolver.cs b/Emby.Naming/Video/VideoListResolver.cs
index 5a32846bf..d4b02cf2a 100644
--- a/Emby.Naming/Video/VideoListResolver.cs
+++ b/Emby.Naming/Video/VideoListResolver.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
@@ -7,6 +6,7 @@ using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using Emby.Naming.Common;
+using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
namespace Emby.Naming.Video
@@ -32,7 +32,7 @@ namespace Emby.Naming.Video
// Filter out all extras, otherwise they could cause stacks to not be resolved
// See the unit test TestStackedWithTrailer
var nonExtras = videoInfos
- .Where(i => string.IsNullOrEmpty(i.ExtraType))
+ .Where(i => i.ExtraType == null)
.Select(i => new FileSystemMetadata
{
FullName = i.Path,
@@ -40,20 +40,19 @@ namespace Emby.Naming.Video
});
var stackResult = new StackResolver(_options)
- .Resolve(nonExtras);
+ .Resolve(nonExtras).ToList();
var remainingFiles = videoInfos
- .Where(i => !stackResult.Stacks.Any(s => s.ContainsFile(i.Path, i.IsDirectory)))
+ .Where(i => !stackResult.Any(s => s.ContainsFile(i.Path, i.IsDirectory)))
.ToList();
var list = new List<VideoInfo>();
- foreach (var stack in stackResult.Stacks)
+ foreach (var stack in stackResult)
{
- var info = new VideoInfo
+ var info = new VideoInfo(stack.Name)
{
- Files = stack.Files.Select(i => videoResolver.Resolve(i, stack.IsDirectoryStack)).ToList(),
- Name = stack.Name
+ Files = stack.Files.Select(i => videoResolver.Resolve(i, stack.IsDirectoryStack)).ToList()
};
info.Year = info.Files[0].Year;
@@ -79,15 +78,14 @@ namespace Emby.Naming.Video
}
var standaloneMedia = remainingFiles
- .Where(i => string.IsNullOrEmpty(i.ExtraType))
+ .Where(i => i.ExtraType == null)
.ToList();
foreach (var media in standaloneMedia)
{
- var info = new VideoInfo
+ var info = new VideoInfo(media.Name)
{
- Files = new List<VideoFileInfo> { media },
- Name = media.Name
+ Files = new List<VideoFileInfo> { media }
};
info.Year = info.Files[0].Year;
@@ -127,7 +125,8 @@ namespace Emby.Naming.Video
.Except(extras)
.ToList();
- info.Extras.AddRange(extras);
+ extras.AddRange(info.Extras);
+ info.Extras = extras;
}
}
@@ -140,7 +139,8 @@ namespace Emby.Naming.Video
.Except(extrasByFileName)
.ToList();
- info.Extras.AddRange(extrasByFileName);
+ extrasByFileName.AddRange(info.Extras);
+ info.Extras = extrasByFileName;
}
// If there's only one video, accept all trailers
@@ -148,10 +148,11 @@ namespace Emby.Naming.Video
if (list.Count == 1)
{
var trailers = remainingFiles
- .Where(i => string.Equals(i.ExtraType, "trailer", StringComparison.OrdinalIgnoreCase))
+ .Where(i => i.ExtraType == ExtraType.Trailer)
.ToList();
- list[0].Extras.AddRange(trailers);
+ trailers.AddRange(list[0].Extras);
+ list[0].Extras = trailers;
remainingFiles = remainingFiles
.Except(trailers)
@@ -159,14 +160,13 @@ namespace Emby.Naming.Video
}
// Whatever files are left, just add them
- list.AddRange(remainingFiles.Select(i => new VideoInfo
+ list.AddRange(remainingFiles.Select(i => new VideoInfo(i.Name)
{
Files = new List<VideoFileInfo> { i },
- Name = i.Name,
Year = i.Year
}));
- return list.OrderBy(i => i.Name);
+ return list;
}
private IEnumerable<VideoInfo> GetVideosGroupedByVersion(List<VideoInfo> videos)
@@ -190,9 +190,18 @@ namespace Emby.Naming.Video
list.Add(ordered[0]);
- list[0].AlternateVersions = ordered.Skip(1).Select(i => i.Files[0]).ToList();
+ var alternateVersionsLen = ordered.Count - 1;
+ var alternateVersions = new VideoFileInfo[alternateVersionsLen];
+ for (int i = 0; i < alternateVersionsLen; i++)
+ {
+ alternateVersions[i] = ordered[i + 1].Files[0];
+ }
+
+ list[0].AlternateVersions = alternateVersions;
list[0].Name = folderName;
- list[0].Extras.AddRange(ordered.Skip(1).SelectMany(i => i.Extras));
+ var extras = ordered.Skip(1).SelectMany(i => i.Extras).ToList();
+ extras.AddRange(list[0].Extras);
+ list[0].Extras = extras;
return list;
}
@@ -229,7 +238,7 @@ namespace Emby.Naming.Video
}
return remainingFiles
- .Where(i => !string.IsNullOrEmpty(i.ExtraType))
+ .Where(i => i.ExtraType == null)
.Where(i => baseNames.Any(b => i.FileNameWithoutExtension.StartsWith(b, StringComparison.OrdinalIgnoreCase)))
.ToList();
}
diff --git a/Emby.Naming/Video/VideoResolver.cs b/Emby.Naming/Video/VideoResolver.cs
index 5a93e1eaf..0b75a8cce 100644
--- a/Emby.Naming/Video/VideoResolver.cs
+++ b/Emby.Naming/Video/VideoResolver.cs
@@ -1,5 +1,5 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
+#nullable enable
using System;
using System.IO;
@@ -22,7 +22,7 @@ namespace Emby.Naming.Video
/// </summary>
/// <param name="path">The path.</param>
/// <returns>VideoFileInfo.</returns>
- public VideoFileInfo ResolveDirectory(string path)
+ public VideoFileInfo? ResolveDirectory(string path)
{
return Resolve(path, true);
}
@@ -32,7 +32,7 @@ namespace Emby.Naming.Video
/// </summary>
/// <param name="path">The path.</param>
/// <returns>VideoFileInfo.</returns>
- public VideoFileInfo ResolveFile(string path)
+ public VideoFileInfo? ResolveFile(string path)
{
return Resolve(path, false);
}
@@ -42,10 +42,10 @@ namespace Emby.Naming.Video
/// </summary>
/// <param name="path">The path.</param>
/// <param name="isDirectory">if set to <c>true</c> [is folder].</param>
- /// <param name="parseName">Whether or not the name should be parsed for info</param>
+ /// <param name="parseName">Whether or not the name should be parsed for info.</param>
/// <returns>VideoFileInfo.</returns>
/// <exception cref="ArgumentNullException"><c>path</c> is <c>null</c>.</exception>
- public VideoFileInfo Resolve(string path, bool isDirectory, bool parseName = true)
+ public VideoFileInfo? Resolve(string path, bool isDirectory, bool parseName = true)
{
if (string.IsNullOrEmpty(path))
{
@@ -53,8 +53,8 @@ namespace Emby.Naming.Video
}
bool isStub = false;
- string container = null;
- string stubType = null;
+ string? container = null;
+ string? stubType = null;
if (!isDirectory)
{
@@ -63,17 +63,13 @@ namespace Emby.Naming.Video
// Check supported extensions
if (!_options.VideoFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
{
- var stubResult = StubResolver.ResolveFile(path, _options);
-
- isStub = stubResult.IsStub;
-
// It's not supported. Check stub extensions
- if (!isStub)
+ if (!StubResolver.TryResolveFile(path, _options, out stubType))
{
return null;
}
- stubType = stubResult.StubType;
+ isStub = true;
}
container = extension.TrimStart('.');
@@ -94,9 +90,10 @@ namespace Emby.Naming.Video
{
var cleanDateTimeResult = CleanDateTime(name);
- if (string.IsNullOrEmpty(extraResult.ExtraType))
+ if (extraResult.ExtraType == null
+ && TryCleanString(cleanDateTimeResult.Name, out ReadOnlySpan<char> newName))
{
- name = CleanString(cleanDateTimeResult.Name).Name;
+ name = newName.ToString();
}
year = cleanDateTimeResult.Year;
@@ -130,14 +127,14 @@ namespace Emby.Naming.Video
return _options.StubFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase);
}
- public CleanStringResult CleanString(string name)
+ public bool TryCleanString(string name, out ReadOnlySpan<char> newName)
{
- return new CleanStringParser().Clean(name, _options.CleanStringRegexes);
+ return CleanStringParser.TryClean(name, _options.CleanStringRegexes, out newName);
}
public CleanDateTimeResult CleanDateTime(string name)
{
- return new CleanDateTimeParser(_options).Clean(name);
+ return CleanDateTimeParser.Clean(name, _options.CleanDateTimeRegexes);
}
}
}
diff --git a/Emby.Notifications/Api/NotificationsService.cs b/Emby.Notifications/Api/NotificationsService.cs
index 83845558a..f2f381838 100644
--- a/Emby.Notifications/Api/NotificationsService.cs
+++ b/Emby.Notifications/Api/NotificationsService.cs
@@ -1,5 +1,11 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1402
+#pragma warning disable SA1600
+#pragma warning disable SA1649
+
using System;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -16,7 +22,7 @@ namespace Emby.Notifications.Api
public class GetNotifications : IReturn<NotificationResult>
{
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string UserId { get; set; }
+ public string UserId { get; set; } = string.Empty;
[ApiMember(Name = "IsRead", Description = "An optional filter by IsRead", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool? IsRead { get; set; }
@@ -30,32 +36,34 @@ namespace Emby.Notifications.Api
public class Notification
{
- public string Id { get; set; }
+ public string Id { get; set; } = string.Empty;
- public string UserId { get; set; }
+ public string UserId { get; set; } = string.Empty;
public DateTime Date { get; set; }
public bool IsRead { get; set; }
- public string Name { get; set; }
+ public string Name { get; set; } = string.Empty;
- public string Description { get; set; }
+ public string Description { get; set; } = string.Empty;
- public string Url { get; set; }
+ public string Url { get; set; } = string.Empty;
public NotificationLevel Level { get; set; }
}
public class NotificationResult
{
- public Notification[] Notifications { get; set; }
+ public IReadOnlyList<Notification> Notifications { get; set; } = Array.Empty<Notification>();
+
public int TotalRecordCount { get; set; }
}
public class NotificationsSummary
{
public int UnreadCount { get; set; }
+
public NotificationLevel MaxUnreadNotificationLevel { get; set; }
}
@@ -63,7 +71,7 @@ namespace Emby.Notifications.Api
public class GetNotificationsSummary : IReturn<NotificationsSummary>
{
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string UserId { get; set; }
+ public string UserId { get; set; } = string.Empty;
}
[Route("/Notifications/Types", "GET", Summary = "Gets notification types")]
@@ -80,16 +88,16 @@ namespace Emby.Notifications.Api
public class AddAdminNotification : IReturnVoid
{
[ApiMember(Name = "Name", Description = "The notification's name", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
- public string Name { get; set; }
+ public string Name { get; set; } = string.Empty;
[ApiMember(Name = "Description", Description = "The notification's description", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
- public string Description { get; set; }
+ public string Description { get; set; } = string.Empty;
[ApiMember(Name = "ImageUrl", Description = "The notification's image url", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
- public string ImageUrl { get; set; }
+ public string? ImageUrl { get; set; }
[ApiMember(Name = "Url", Description = "The notification's info url", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
- public string Url { get; set; }
+ public string? Url { get; set; }
[ApiMember(Name = "Level", Description = "The notification level", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
public NotificationLevel Level { get; set; }
@@ -99,20 +107,20 @@ namespace Emby.Notifications.Api
public class MarkRead : IReturnVoid
{
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
- public string UserId { get; set; }
+ public string UserId { get; set; } = string.Empty;
[ApiMember(Name = "Ids", Description = "A list of notification ids, comma delimited", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST", AllowMultiple = true)]
- public string Ids { get; set; }
+ public string Ids { get; set; } = string.Empty;
}
[Route("/Notifications/{UserId}/Unread", "POST", Summary = "Marks notifications as unread")]
public class MarkUnread : IReturnVoid
{
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
- public string UserId { get; set; }
+ public string UserId { get; set; } = string.Empty;
[ApiMember(Name = "Ids", Description = "A list of notification ids, comma delimited", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST", AllowMultiple = true)]
- public string Ids { get; set; }
+ public string Ids { get; set; } = string.Empty;
}
[Authenticated]
@@ -127,32 +135,29 @@ namespace Emby.Notifications.Api
_userManager = userManager;
}
+ [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request")]
public object Get(GetNotificationTypes request)
{
return _notificationManager.GetNotificationTypes();
}
+ [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request")]
public object Get(GetNotificationServices request)
{
return _notificationManager.GetNotificationServices().ToList();
}
+ [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request")]
public object Get(GetNotificationsSummary request)
{
return new NotificationsSummary
{
-
};
}
public Task Post(AddAdminNotification request)
{
// This endpoint really just exists as post of a real with sickbeard
- return AddNotification(request);
- }
-
- private Task AddNotification(AddAdminNotification request)
- {
var notification = new NotificationRequest
{
Date = DateTime.UtcNow,
@@ -166,14 +171,17 @@ namespace Emby.Notifications.Api
return _notificationManager.SendNotification(notification, CancellationToken.None);
}
+ [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request")]
public void Post(MarkRead request)
{
}
+ [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request")]
public void Post(MarkUnread request)
{
}
+ [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request")]
public object Get(GetNotifications request)
{
return new NotificationResult();
diff --git a/Emby.Notifications/CoreNotificationTypes.cs b/Emby.Notifications/CoreNotificationTypes.cs
index 0f9fc08d9..73e0b0256 100644
--- a/Emby.Notifications/CoreNotificationTypes.cs
+++ b/Emby.Notifications/CoreNotificationTypes.cs
@@ -1,7 +1,9 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
using System;
using System.Collections.Generic;
using System.Linq;
-using MediaBrowser.Controller;
using MediaBrowser.Controller.Notifications;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Notifications;
diff --git a/Emby.Notifications/Emby.Notifications.csproj b/Emby.Notifications/Emby.Notifications.csproj
index 004ded77b..e6bf785bf 100644
--- a/Emby.Notifications/Emby.Notifications.csproj
+++ b/Emby.Notifications/Emby.Notifications.csproj
@@ -4,6 +4,8 @@
<TargetFramework>netstandard2.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
+ <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+ <Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
@@ -16,4 +18,16 @@
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" />
</ItemGroup>
+ <!-- Code analyzers-->
+ <ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
+ <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8" PrivateAssets="All" />
+ <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
+ <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" />
+ <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
+ </ItemGroup>
+
+ <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
+ <CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
+ </PropertyGroup>
+
</Project>
diff --git a/Emby.Notifications/NotificationConfigurationFactory.cs b/Emby.Notifications/NotificationConfigurationFactory.cs
index d08475f7d..b168ed221 100644
--- a/Emby.Notifications/NotificationConfigurationFactory.cs
+++ b/Emby.Notifications/NotificationConfigurationFactory.cs
@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
using System.Collections.Generic;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.Notifications;
@@ -13,7 +16,7 @@ namespace Emby.Notifications
new ConfigurationStore
{
Key = "notifications",
- ConfigurationType = typeof (NotificationOptions)
+ ConfigurationType = typeof(NotificationOptions)
}
};
}
diff --git a/Emby.Notifications/Notifications.cs b/Emby.Notifications/NotificationEntryPoint.cs
index 7aa1e7ae8..befecc570 100644
--- a/Emby.Notifications/Notifications.cs
+++ b/Emby.Notifications/NotificationEntryPoint.cs
@@ -21,70 +21,85 @@ using Microsoft.Extensions.Logging;
namespace Emby.Notifications
{
/// <summary>
- /// Creates notifications for various system events
+ /// Creates notifications for various system events.
/// </summary>
- public class Notifications : IServerEntryPoint
+ public class NotificationEntryPoint : IServerEntryPoint
{
private readonly ILogger _logger;
-
+ private readonly IActivityManager _activityManager;
+ private readonly ILocalizationManager _localization;
private readonly INotificationManager _notificationManager;
-
private readonly ILibraryManager _libraryManager;
private readonly IServerApplicationHost _appHost;
+ private readonly IConfigurationManager _config;
- private Timer LibraryUpdateTimer { get; set; }
private readonly object _libraryChangedSyncLock = new object();
+ private readonly List<BaseItem> _itemsAdded = new List<BaseItem>();
- private readonly IConfigurationManager _config;
- private readonly ILocalizationManager _localization;
- private readonly IActivityManager _activityManager;
+ private Timer? _libraryUpdateTimer;
private string[] _coreNotificationTypes;
- public Notifications(
+ private bool _disposed = false;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="NotificationEntryPoint" /> class.
+ /// </summary>
+ /// <param name="logger">The logger.</param>
+ /// <param name="activityManager">The activity manager.</param>
+ /// <param name="localization">The localization manager.</param>
+ /// <param name="notificationManager">The notification manager.</param>
+ /// <param name="libraryManager">The library manager.</param>
+ /// <param name="appHost">The application host.</param>
+ /// <param name="config">The configuration manager.</param>
+ public NotificationEntryPoint(
+ ILogger<NotificationEntryPoint> logger,
IActivityManager activityManager,
ILocalizationManager localization,
- ILogger logger,
INotificationManager notificationManager,
ILibraryManager libraryManager,
IServerApplicationHost appHost,
IConfigurationManager config)
{
_logger = logger;
+ _activityManager = activityManager;
+ _localization = localization;
_notificationManager = notificationManager;
_libraryManager = libraryManager;
_appHost = appHost;
_config = config;
- _localization = localization;
- _activityManager = activityManager;
_coreNotificationTypes = new CoreNotificationTypes(localization).GetNotificationTypes().Select(i => i.Type).ToArray();
}
+ /// <inheritdoc />
public Task RunAsync()
{
- _libraryManager.ItemAdded += _libraryManager_ItemAdded;
- _appHost.HasPendingRestartChanged += _appHost_HasPendingRestartChanged;
- _appHost.HasUpdateAvailableChanged += _appHost_HasUpdateAvailableChanged;
- _activityManager.EntryCreated += _activityManager_EntryCreated;
+ _libraryManager.ItemAdded += OnLibraryManagerItemAdded;
+ _appHost.HasPendingRestartChanged += OnAppHostHasPendingRestartChanged;
+ _appHost.HasUpdateAvailableChanged += OnAppHostHasUpdateAvailableChanged;
+ _activityManager.EntryCreated += OnActivityManagerEntryCreated;
return Task.CompletedTask;
}
- private async void _appHost_HasPendingRestartChanged(object sender, EventArgs e)
+ private async void OnAppHostHasPendingRestartChanged(object sender, EventArgs e)
{
var type = NotificationType.ServerRestartRequired.ToString();
var notification = new NotificationRequest
{
NotificationType = type,
- Name = string.Format(_localization.GetLocalizedString("ServerNameNeedsToBeRestarted"), _appHost.Name)
+ Name = string.Format(
+ CultureInfo.InvariantCulture,
+ _localization.GetLocalizedString("ServerNameNeedsToBeRestarted"),
+ _appHost.Name)
};
await SendNotification(notification, null).ConfigureAwait(false);
}
- private async void _activityManager_EntryCreated(object sender, GenericEventArgs<ActivityLogEntry> e)
+ private async void OnActivityManagerEntryCreated(object sender, GenericEventArgs<ActivityLogEntry> e)
{
var entry = e.Argument;
@@ -117,7 +132,7 @@ namespace Emby.Notifications
return _config.GetConfiguration<NotificationOptions>("notifications");
}
- private async void _appHost_HasUpdateAvailableChanged(object sender, EventArgs e)
+ private async void OnAppHostHasUpdateAvailableChanged(object sender, EventArgs e)
{
if (!_appHost.HasUpdateAvailable)
{
@@ -136,8 +151,7 @@ namespace Emby.Notifications
await SendNotification(notification, null).ConfigureAwait(false);
}
- private readonly List<BaseItem> _itemsAdded = new List<BaseItem>();
- private void _libraryManager_ItemAdded(object sender, ItemChangeEventArgs e)
+ private void OnLibraryManagerItemAdded(object sender, ItemChangeEventArgs e)
{
if (!FilterItem(e.Item))
{
@@ -146,14 +160,17 @@ namespace Emby.Notifications
lock (_libraryChangedSyncLock)
{
- if (LibraryUpdateTimer == null)
+ if (_libraryUpdateTimer == null)
{
- LibraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, 5000,
- Timeout.Infinite);
+ _libraryUpdateTimer = new Timer(
+ LibraryUpdateTimerCallback,
+ null,
+ 5000,
+ Timeout.Infinite);
}
else
{
- LibraryUpdateTimer.Change(5000, Timeout.Infinite);
+ _libraryUpdateTimer.Change(5000, Timeout.Infinite);
}
_itemsAdded.Add(e.Item);
@@ -188,7 +205,8 @@ namespace Emby.Notifications
{
items = _itemsAdded.ToList();
_itemsAdded.Clear();
- DisposeLibraryUpdateTimer();
+ _libraryUpdateTimer!.Dispose(); // Shouldn't be null as it just set off this callback
+ _libraryUpdateTimer = null;
}
items = items.Take(10).ToList();
@@ -198,7 +216,10 @@ namespace Emby.Notifications
var notification = new NotificationRequest
{
NotificationType = NotificationType.NewLibraryContent.ToString(),
- Name = string.Format(_localization.GetLocalizedString("ValueHasBeenAddedToLibrary"), GetItemName(item)),
+ Name = string.Format(
+ CultureInfo.InvariantCulture,
+ _localization.GetLocalizedString("ValueHasBeenAddedToLibrary"),
+ GetItemName(item)),
Description = item.Overview
};
@@ -206,6 +227,11 @@ namespace Emby.Notifications
}
}
+ /// <summary>
+ /// Creates a human readable name for the item.
+ /// </summary>
+ /// <param name="item">The item.</param>
+ /// <returns>A human readable name for the item.</returns>
public static string GetItemName(BaseItem item)
{
var name = item.Name;
@@ -219,6 +245,7 @@ namespace Emby.Notifications
episode.IndexNumber.Value,
name);
}
+
if (episode.ParentIndexNumber.HasValue)
{
name = string.Format(
@@ -229,7 +256,6 @@ namespace Emby.Notifications
}
}
-
if (item is IHasSeries hasSeries)
{
name = hasSeries.SeriesName + " - " + name;
@@ -257,7 +283,7 @@ namespace Emby.Notifications
return name;
}
- private async Task SendNotification(NotificationRequest notification, BaseItem relatedItem)
+ private async Task SendNotification(NotificationRequest notification, BaseItem? relatedItem)
{
try
{
@@ -269,23 +295,37 @@ namespace Emby.Notifications
}
}
+ /// <inheritdoc />
public void Dispose()
{
- DisposeLibraryUpdateTimer();
-
- _libraryManager.ItemAdded -= _libraryManager_ItemAdded;
- _appHost.HasPendingRestartChanged -= _appHost_HasPendingRestartChanged;
- _appHost.HasUpdateAvailableChanged -= _appHost_HasUpdateAvailableChanged;
- _activityManager.EntryCreated -= _activityManager_EntryCreated;
+ Dispose(true);
+ GC.SuppressFinalize(this);
}
- private void DisposeLibraryUpdateTimer()
+ /// <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 (LibraryUpdateTimer != null)
+ if (_disposed)
+ {
+ return;
+ }
+
+ if (disposing)
{
- LibraryUpdateTimer.Dispose();
- LibraryUpdateTimer = null;
+ _libraryUpdateTimer?.Dispose();
}
+
+ _libraryUpdateTimer = null;
+
+ _libraryManager.ItemAdded -= OnLibraryManagerItemAdded;
+ _appHost.HasPendingRestartChanged -= OnAppHostHasPendingRestartChanged;
+ _appHost.HasUpdateAvailableChanged -= OnAppHostHasUpdateAvailableChanged;
+ _activityManager.EntryCreated -= OnActivityManagerEntryCreated;
+
+ _disposed = true;
}
}
}
diff --git a/Emby.Notifications/NotificationManager.cs b/Emby.Notifications/NotificationManager.cs
index eecbbea07..639a5e1aa 100644
--- a/Emby.Notifications/NotificationManager.cs
+++ b/Emby.Notifications/NotificationManager.cs
@@ -16,20 +16,32 @@ using Microsoft.Extensions.Logging;
namespace Emby.Notifications
{
+ /// <summary>
+ /// NotificationManager class.
+ /// </summary>
public class NotificationManager : INotificationManager
{
private readonly ILogger _logger;
private readonly IUserManager _userManager;
private readonly IServerConfigurationManager _config;
- private INotificationService[] _services;
- private INotificationTypeFactory[] _typeFactories;
-
- public NotificationManager(ILoggerFactory loggerFactory, IUserManager userManager, IServerConfigurationManager config)
+ private INotificationService[] _services = Array.Empty<INotificationService>();
+ private INotificationTypeFactory[] _typeFactories = Array.Empty<INotificationTypeFactory>();
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="NotificationManager" /> class.
+ /// </summary>
+ /// <param name="logger">The logger.</param>
+ /// <param name="userManager">The user manager.</param>
+ /// <param name="config">The server configuration manager.</param>
+ public NotificationManager(
+ ILogger<NotificationManager> logger,
+ IUserManager userManager,
+ IServerConfigurationManager config)
{
+ _logger = logger;
_userManager = userManager;
_config = config;
- _logger = loggerFactory.CreateLogger(GetType().Name);
}
private NotificationOptions GetConfiguration()
@@ -37,12 +49,14 @@ namespace Emby.Notifications
return _config.GetConfiguration<NotificationOptions>("notifications");
}
+ /// <inheritdoc />
public Task SendNotification(NotificationRequest request, CancellationToken cancellationToken)
{
return SendNotification(request, null, cancellationToken);
}
- public Task SendNotification(NotificationRequest request, BaseItem relatedItem, CancellationToken cancellationToken)
+ /// <inheritdoc />
+ public Task SendNotification(NotificationRequest request, BaseItem? relatedItem, CancellationToken cancellationToken)
{
var notificationType = request.NotificationType;
@@ -64,7 +78,8 @@ namespace Emby.Notifications
return Task.WhenAll(tasks);
}
- private Task SendNotification(NotificationRequest request,
+ private Task SendNotification(
+ NotificationRequest request,
INotificationService service,
IEnumerable<User> users,
string title,
@@ -79,7 +94,7 @@ namespace Emby.Notifications
return Task.WhenAll(tasks);
}
- private IEnumerable<Guid> GetUserIds(NotificationRequest request, NotificationOption options)
+ private IEnumerable<Guid> GetUserIds(NotificationRequest request, NotificationOption? options)
{
if (request.SendToUserMode.HasValue)
{
@@ -109,7 +124,8 @@ namespace Emby.Notifications
return request.UserIds;
}
- private async Task SendNotification(NotificationRequest request,
+ private async Task SendNotification(
+ NotificationRequest request,
INotificationService service,
string title,
string description,
@@ -161,12 +177,14 @@ namespace Emby.Notifications
return GetConfiguration().IsServiceEnabled(service.Name, notificationType);
}
+ /// <inheritdoc />
public void AddParts(IEnumerable<INotificationService> services, IEnumerable<INotificationTypeFactory> notificationTypeFactories)
{
_services = services.ToArray();
_typeFactories = notificationTypeFactories.ToArray();
}
+ /// <inheritdoc />
public List<NotificationTypeInfo> GetNotificationTypes()
{
var list = _typeFactories.Select(i =>
@@ -180,7 +198,6 @@ namespace Emby.Notifications
_logger.LogError(ex, "Error in GetNotificationTypes");
return new List<NotificationTypeInfo>();
}
-
}).SelectMany(i => i).ToList();
var config = GetConfiguration();
@@ -193,13 +210,13 @@ namespace Emby.Notifications
return list;
}
+ /// <inheritdoc />
public IEnumerable<NameIdPair> GetNotificationServices()
{
return _services.Select(i => new NameIdPair
{
Name = i.Name,
Id = i.Name.GetMD5().ToString("N", CultureInfo.InvariantCulture)
-
}).OrderBy(i => i.Name);
}
}
diff --git a/Emby.Photos/Emby.Photos.csproj b/Emby.Photos/Emby.Photos.csproj
index 23b7a819b..cc3fbb43f 100644
--- a/Emby.Photos/Emby.Photos.csproj
+++ b/Emby.Photos/Emby.Photos.csproj
@@ -1,9 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
-
- <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
- <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
- </PropertyGroup>
-
<ItemGroup>
<ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
@@ -22,13 +17,15 @@
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+ <Nullable>enable</Nullable>
</PropertyGroup>
<!-- Code Analyzers-->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
- <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.7" />
- <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" />
- <PackageReference Include="SerilogAnalyzer" Version="0.15.0" />
+ <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8" PrivateAssets="All" />
+ <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" />
+ <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
+ <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
</ItemGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
diff --git a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
index b622a3167..332dfa95c 100644
--- a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
+++ b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
@@ -28,7 +28,7 @@ using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Activity
{
- public class ActivityLogEntryPoint : IServerEntryPoint
+ public sealed class ActivityLogEntryPoint : IServerEntryPoint
{
private readonly ILogger _logger;
private readonly IInstallationManager _installationManager;
@@ -38,7 +38,6 @@ namespace Emby.Server.Implementations.Activity
private readonly ILocalizationManager _localization;
private readonly ISubtitleManager _subManager;
private readonly IUserManager _userManager;
- private readonly IServerApplicationHost _appHost;
private readonly IDeviceManager _deviceManager;
/// <summary>
@@ -63,8 +62,7 @@ namespace Emby.Server.Implementations.Activity
ILocalizationManager localization,
IInstallationManager installationManager,
ISubtitleManager subManager,
- IUserManager userManager,
- IServerApplicationHost appHost)
+ IUserManager userManager)
{
_logger = logger;
_sessionManager = sessionManager;
@@ -75,7 +73,6 @@ namespace Emby.Server.Implementations.Activity
_installationManager = installationManager;
_subManager = subManager;
_userManager = userManager;
- _appHost = appHost;
}
public Task RunAsync()
@@ -140,7 +137,7 @@ namespace Emby.Server.Implementations.Activity
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("SubtitleDownloadFailureFromForItem"),
e.Provider,
- Notifications.Notifications.GetItemName(e.Item)),
+ Emby.Notifications.NotificationEntryPoint.GetItemName(e.Item)),
Type = "SubtitleDownloadFailure",
ItemId = e.Item.Id.ToString("N", CultureInfo.InvariantCulture),
ShortOverview = e.Exception.Message
@@ -532,6 +529,7 @@ namespace Emby.Server.Implementations.Activity
private void CreateLogEntry(ActivityLogEntry entry)
=> _activityManager.Create(entry);
+ /// <inheritdoc />
public void Dispose()
{
_taskManager.TaskCompleted -= OnTaskCompleted;
diff --git a/Emby.Server.Implementations/Activity/ActivityManager.cs b/Emby.Server.Implementations/Activity/ActivityManager.cs
index a30e93912..ee10845cf 100644
--- a/Emby.Server.Implementations/Activity/ActivityManager.cs
+++ b/Emby.Server.Implementations/Activity/ActivityManager.cs
@@ -1,7 +1,6 @@
#pragma warning disable CS1591
using System;
-using System.Linq;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Activity;
using MediaBrowser.Model.Events;
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index 67bc0cd2b..35b2cba9f 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -99,18 +99,15 @@ using MediaBrowser.Model.Tasks;
using MediaBrowser.Model.Updates;
using MediaBrowser.Providers.Chapters;
using MediaBrowser.Providers.Manager;
+using MediaBrowser.Providers.Plugins.TheTvdb;
using MediaBrowser.Providers.Subtitles;
-using MediaBrowser.Providers.TV.TheTVDB;
using MediaBrowser.WebDashboard.Api;
using MediaBrowser.XbmcMetadata.Providers;
-using Microsoft.AspNetCore.Builder;
-using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
-using Microsoft.OpenApi.Models;
using OperatingSystem = MediaBrowser.Common.System.OperatingSystem;
namespace Emby.Server.Implementations
@@ -121,7 +118,6 @@ namespace Emby.Server.Implementations
public abstract class ApplicationHost : IServerApplicationHost, IDisposable
{
private SqliteUserRepository _userRepository;
-
private SqliteDisplayPreferencesRepository _displayPreferencesRepository;
/// <summary>
@@ -169,10 +165,9 @@ namespace Emby.Server.Implementations
public bool IsShuttingDown { get; private set; }
/// <summary>
- /// Gets or sets the logger.
+ /// Gets the logger.
/// </summary>
- /// <value>The logger.</value>
- protected ILogger Logger { get; set; }
+ protected ILogger Logger { get; }
private IPlugin[] _plugins;
@@ -180,17 +175,12 @@ namespace Emby.Server.Implementations
/// Gets the plugins.
/// </summary>
/// <value>The plugins.</value>
- public IPlugin[] Plugins
- {
- get => _plugins;
- protected set => _plugins = value;
- }
+ public IReadOnlyList<IPlugin> Plugins => _plugins;
/// <summary>
- /// Gets or sets the logger factory.
+ /// Gets the logger factory.
/// </summary>
- /// <value>The logger factory.</value>
- public ILoggerFactory LoggerFactory { get; protected set; }
+ protected ILoggerFactory LoggerFactory { get; }
/// <summary>
/// Gets or sets the application paths.
@@ -334,8 +324,6 @@ namespace Emby.Server.Implementations
private IMediaSourceManager MediaSourceManager { get; set; }
- private readonly IConfiguration _configuration;
-
/// <summary>
/// Gets the installation manager.
/// </summary>
@@ -373,11 +361,8 @@ namespace Emby.Server.Implementations
IStartupOptions options,
IFileSystem fileSystem,
IImageEncoder imageEncoder,
- INetworkManager networkManager,
- IConfiguration configuration)
+ INetworkManager networkManager)
{
- _configuration = configuration;
-
XmlSerializer = new MyXmlSerializer();
NetworkManager = networkManager;
@@ -593,7 +578,8 @@ namespace Emby.Server.Implementations
}
}
- public async Task InitAsync(IServiceCollection serviceCollection)
+ /// <inheritdoc/>
+ public async Task InitAsync(IServiceCollection serviceCollection, IConfiguration startupConfig)
{
HttpPort = ServerConfigurationManager.Configuration.HttpServerPortNumber;
HttpsPort = ServerConfigurationManager.Configuration.HttpsPortNumber;
@@ -605,7 +591,7 @@ namespace Emby.Server.Implementations
HttpsPort = ServerConfiguration.DefaultHttpsPort;
}
- JsonSerializer = new JsonSerializer(FileSystemManager);
+ JsonSerializer = new JsonSerializer();
if (Plugins != null)
{
@@ -626,7 +612,7 @@ namespace Emby.Server.Implementations
DiscoverTypes();
- await RegisterResources(serviceCollection).ConfigureAwait(false);
+ await RegisterResources(serviceCollection, startupConfig).ConfigureAwait(false);
ContentRoot = ServerConfigurationManager.Configuration.DashboardSourcePath;
if (string.IsNullOrEmpty(ContentRoot))
@@ -658,14 +644,14 @@ namespace Emby.Server.Implementations
var response = context.Response;
var localPath = context.Request.Path.ToString();
- var req = new WebSocketSharpRequest(request, response, request.Path, Logger);
+ var req = new WebSocketSharpRequest(request, response, request.Path, LoggerFactory.CreateLogger<WebSocketSharpRequest>());
await HttpServer.RequestHandler(req, request.GetDisplayUrl(), request.Host.ToString(), localPath, context.RequestAborted).ConfigureAwait(false);
}
/// <summary>
/// Registers resources that classes will depend on
/// </summary>
- protected async Task RegisterResources(IServiceCollection serviceCollection)
+ protected async Task RegisterResources(IServiceCollection serviceCollection, IConfiguration startupConfig)
{
serviceCollection.AddMemoryCache();
@@ -674,16 +660,13 @@ namespace Emby.Server.Implementations
serviceCollection.AddSingleton<IApplicationPaths>(ApplicationPaths);
- serviceCollection.AddSingleton<IConfiguration>(_configuration);
-
serviceCollection.AddSingleton(JsonSerializer);
- serviceCollection.AddSingleton(LoggerFactory);
- serviceCollection.AddLogging();
- serviceCollection.AddSingleton(Logger);
+ // TODO: Support for injecting ILogger should be deprecated in favour of ILogger<T> and this removed
+ serviceCollection.AddSingleton<ILogger>(Logger);
serviceCollection.AddSingleton(FileSystemManager);
- serviceCollection.AddSingleton<TvDbClientManager>();
+ serviceCollection.AddSingleton<TvdbClientManager>();
HttpClient = new HttpClientManager.HttpClientManager(
ApplicationPaths,
@@ -761,7 +744,18 @@ namespace Emby.Server.Implementations
serviceCollection.AddSingleton(UserManager);
- LibraryManager = new LibraryManager(this, LoggerFactory, TaskManager, UserManager, ServerConfigurationManager, UserDataManager, () => LibraryMonitor, FileSystemManager, () => ProviderManager, () => UserViewManager);
+ MediaEncoder = new MediaBrowser.MediaEncoding.Encoder.MediaEncoder(
+ LoggerFactory.CreateLogger<MediaBrowser.MediaEncoding.Encoder.MediaEncoder>(),
+ ServerConfigurationManager,
+ FileSystemManager,
+ ProcessFactory,
+ LocalizationManager,
+ () => SubtitleEncoder,
+ startupConfig,
+ StartupOptions.FFmpegPath);
+ serviceCollection.AddSingleton(MediaEncoder);
+
+ LibraryManager = new LibraryManager(this, LoggerFactory, TaskManager, UserManager, ServerConfigurationManager, UserDataManager, () => LibraryMonitor, FileSystemManager, () => ProviderManager, () => UserViewManager, MediaEncoder);
serviceCollection.AddSingleton(LibraryManager);
var musicManager = new MusicManager(LibraryManager);
@@ -779,7 +773,7 @@ namespace Emby.Server.Implementations
this,
LoggerFactory.CreateLogger<HttpListenerHost>(),
ServerConfigurationManager,
- _configuration,
+ startupConfig,
NetworkManager,
JsonSerializer,
XmlSerializer,
@@ -814,7 +808,18 @@ namespace Emby.Server.Implementations
ChannelManager = new ChannelManager(UserManager, DtoService, LibraryManager, LoggerFactory, ServerConfigurationManager, FileSystemManager, UserDataManager, JsonSerializer, ProviderManager);
serviceCollection.AddSingleton(ChannelManager);
- SessionManager = new SessionManager(UserDataManager, LoggerFactory, LibraryManager, UserManager, musicManager, DtoService, ImageProcessor, this, AuthenticationRepository, DeviceManager, MediaSourceManager);
+ SessionManager = new SessionManager(
+ LoggerFactory.CreateLogger<SessionManager>(),
+ UserDataManager,
+ LibraryManager,
+ UserManager,
+ musicManager,
+ DtoService,
+ ImageProcessor,
+ this,
+ AuthenticationRepository,
+ DeviceManager,
+ MediaSourceManager);
serviceCollection.AddSingleton(SessionManager);
serviceCollection.AddSingleton<IDlnaManager>(
@@ -831,7 +836,10 @@ namespace Emby.Server.Implementations
UserViewManager = new UserViewManager(LibraryManager, LocalizationManager, UserManager, ChannelManager, LiveTvManager, ServerConfigurationManager);
serviceCollection.AddSingleton(UserViewManager);
- NotificationManager = new NotificationManager(LoggerFactory, UserManager, ServerConfigurationManager);
+ NotificationManager = new NotificationManager(
+ LoggerFactory.CreateLogger<NotificationManager>(),
+ UserManager,
+ ServerConfigurationManager);
serviceCollection.AddSingleton(NotificationManager);
serviceCollection.AddSingleton<IDeviceDiscovery>(new DeviceDiscovery(ServerConfigurationManager));
@@ -839,17 +847,6 @@ namespace Emby.Server.Implementations
ChapterManager = new ChapterManager(LibraryManager, LoggerFactory, ServerConfigurationManager, ItemRepository);
serviceCollection.AddSingleton(ChapterManager);
- MediaEncoder = new MediaBrowser.MediaEncoding.Encoder.MediaEncoder(
- LoggerFactory.CreateLogger<MediaBrowser.MediaEncoding.Encoder.MediaEncoder>(),
- ServerConfigurationManager,
- FileSystemManager,
- ProcessFactory,
- LocalizationManager,
- () => SubtitleEncoder,
- _configuration,
- StartupOptions.FFmpegPath);
- serviceCollection.AddSingleton(MediaEncoder);
-
EncodingManager = new MediaEncoder.EncodingManager(FileSystemManager, LoggerFactory, MediaEncoder, ChapterManager, LibraryManager);
serviceCollection.AddSingleton(EncodingManager);
@@ -878,6 +875,8 @@ namespace Emby.Server.Implementations
serviceCollection.AddSingleton(typeof(IResourceFileManager), typeof(ResourceFileManager));
serviceCollection.AddSingleton<EncodingHelper>();
+ serviceCollection.AddSingleton(typeof(IAttachmentExtractor), typeof(MediaBrowser.MediaEncoding.Attachments.AttachmentExtractor));
+
_displayPreferencesRepository.Initialize();
var userDataRepo = new SqliteUserDataRepository(LoggerFactory.CreateLogger<SqliteUserDataRepository>(), ApplicationPaths);
@@ -898,6 +897,18 @@ namespace Emby.Server.Implementations
.GetCommandLineArgs()
.Distinct();
+ // Get all 'JELLYFIN_' prefixed environment variables
+ var allEnvVars = Environment.GetEnvironmentVariables();
+ var jellyfinEnvVars = new Dictionary<object, object>();
+ foreach (var key in allEnvVars.Keys)
+ {
+ if (key.ToString().StartsWith("JELLYFIN_", StringComparison.OrdinalIgnoreCase))
+ {
+ jellyfinEnvVars.Add(key, allEnvVars[key]);
+ }
+ }
+
+ logger.LogInformation("Environment Variables: {EnvVars}", jellyfinEnvVars);
logger.LogInformation("Arguments: {Args}", commandLineArgs);
logger.LogInformation("Operating system: {OS}", OperatingSystem.Name);
logger.LogInformation("Architecture: {Architecture}", RuntimeInformation.OSArchitecture);
@@ -1011,7 +1022,7 @@ namespace Emby.Server.Implementations
{
string dir = Path.Combine(ApplicationPaths.PluginsPath, args.Argument.name);
var types = Directory.EnumerateFiles(dir, "*.dll", SearchOption.AllDirectories)
- .Select(x => Assembly.LoadFrom(x))
+ .Select(Assembly.LoadFrom)
.SelectMany(x => x.ExportedTypes)
.Where(x => x.IsClass && !x.IsAbstract && !x.IsInterface && !x.IsGenericType)
.ToArray();
@@ -1057,7 +1068,7 @@ namespace Emby.Server.Implementations
}
ConfigurationManager.AddParts(GetExports<IConfigurationFactory>());
- Plugins = GetExports<IPlugin>()
+ _plugins = GetExports<IPlugin>()
.Select(LoadPlugin)
.Where(i => i != null)
.ToArray();
@@ -1078,8 +1089,6 @@ namespace Emby.Server.Implementations
GetExports<IMetadataSaver>(),
GetExports<IExternalId>());
- ImageProcessor.ImageEnhancers = GetExports<IImageEnhancer>();
-
LiveTvManager.AddParts(GetExports<ILiveTvService>(), GetExports<ITunerHost>(), GetExports<IListingsProvider>());
SubtitleManager.AddParts(GetExports<ISubtitleProvider>());
@@ -1198,7 +1207,7 @@ namespace Emby.Server.Implementations
});
}
- protected IHttpListener CreateHttpListener() => new WebSocketSharpListener(Logger);
+ protected IHttpListener CreateHttpListener() => new WebSocketSharpListener(LoggerFactory.CreateLogger<WebSocketSharpListener>());
private CertificateInfo GetCertificateInfo(bool generateCertificate)
{
@@ -1478,7 +1487,7 @@ namespace Emby.Server.Implementations
/// </summary>
/// <param name="address">The IPv6 address.</param>
/// <returns>The IPv6 address without the scope id.</returns>
- private string RemoveScopeId(string address)
+ private ReadOnlySpan<char> RemoveScopeId(ReadOnlySpan<char> address)
{
var index = address.IndexOf('%');
if (index == -1)
@@ -1486,33 +1495,50 @@ namespace Emby.Server.Implementations
return address;
}
- return address.Substring(0, index);
+ return address.Slice(0, index);
}
+ /// <inheritdoc />
public string GetLocalApiUrl(IPAddress ipAddress)
{
if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6)
{
var str = RemoveScopeId(ipAddress.ToString());
+ Span<char> span = new char[str.Length + 2];
+ span[0] = '[';
+ str.CopyTo(span.Slice(1));
+ span[^1] = ']';
- return GetLocalApiUrl("[" + str + "]");
+ return GetLocalApiUrl(span);
}
return GetLocalApiUrl(ipAddress.ToString());
}
- public string GetLocalApiUrl(string host)
+ /// <inheritdoc />
+ public string GetLocalApiUrl(ReadOnlySpan<char> host)
{
+ var url = new StringBuilder(64);
if (EnableHttps)
{
- return string.Format("https://{0}:{1}",
- host,
- HttpsPort.ToString(CultureInfo.InvariantCulture));
+ url.Append("https://");
+ }
+ else
+ {
+ url.Append("http://");
+ }
+
+ url.Append(host)
+ .Append(':')
+ .Append(HttpPort);
+
+ string baseUrl = ServerConfigurationManager.Configuration.BaseUrl;
+ if (baseUrl.Length != 0)
+ {
+ url.Append(baseUrl);
}
- return string.Format("http://{0}:{1}",
- host,
- HttpPort.ToString(CultureInfo.InvariantCulture));
+ return url.ToString();
}
public Task<List<IPAddress>> GetLocalIpAddresses(CancellationToken cancellationToken)
@@ -1689,32 +1715,9 @@ namespace Emby.Server.Implementations
/// <param name="plugin">The plugin.</param>
public void RemovePlugin(IPlugin plugin)
{
- var list = Plugins.ToList();
+ var list = _plugins.ToList();
list.Remove(plugin);
- Plugins = list.ToArray();
- }
-
- /// <summary>
- /// This returns localhost in the case of no external dns, and the hostname if the
- /// dns is prefixed with a valid Uri prefix.
- /// </summary>
- /// <param name="externalDns">The external dns prefix to get the hostname of.</param>
- /// <returns>The hostname in <paramref name="externalDns"/>.</returns>
- private static string GetHostnameFromExternalDns(string externalDns)
- {
- if (string.IsNullOrEmpty(externalDns))
- {
- return "localhost";
- }
-
- try
- {
- return new Uri(externalDns).Host;
- }
- catch
- {
- return externalDns;
- }
+ _plugins = list.ToArray();
}
public virtual void LaunchUrl(string url)
diff --git a/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs b/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs
index 2712fc8c5..266d539d0 100644
--- a/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs
+++ b/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs
@@ -34,14 +34,6 @@ namespace Emby.Server.Implementations.Channels
return Task.CompletedTask;
}
- public static string GetUserDistinctValue(User user)
- {
- var channels = user.Policy.EnabledChannels
- .OrderBy(i => i);
-
- return string.Join("|", channels);
- }
-
private void CleanDatabase(CancellationToken cancellationToken)
{
var installedChannelIds = ((ChannelManager)_channelManager).GetInstalledChannelIds();
@@ -74,19 +66,23 @@ namespace Emby.Server.Implementations.Channels
{
cancellationToken.ThrowIfCancellationRequested();
- _libraryManager.DeleteItem(item, new DeleteOptions
- {
- DeleteFileLocation = false
-
- }, false);
+ _libraryManager.DeleteItem(
+ item,
+ new DeleteOptions
+ {
+ DeleteFileLocation = false
+ },
+ false);
}
// Finally, delete the channel itself
- _libraryManager.DeleteItem(channel, new DeleteOptions
- {
- DeleteFileLocation = false
-
- }, false);
+ _libraryManager.DeleteItem(
+ channel,
+ new DeleteOptions
+ {
+ DeleteFileLocation = false
+ },
+ false);
}
}
}
diff --git a/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs b/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs
index 5774c0415..891b81a36 100644
--- a/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs
+++ b/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs
@@ -19,7 +19,11 @@ namespace Emby.Server.Implementations.Channels
private readonly ILogger _logger;
private readonly ILibraryManager _libraryManager;
- public RefreshChannelsScheduledTask(IChannelManager channelManager, IUserManager userManager, ILogger logger, ILibraryManager libraryManager)
+ public RefreshChannelsScheduledTask(
+ IChannelManager channelManager,
+ IUserManager userManager,
+ ILogger<RefreshChannelsScheduledTask> logger,
+ ILibraryManager libraryManager)
{
_channelManager = channelManager;
_userManager = userManager;
@@ -27,18 +31,28 @@ namespace Emby.Server.Implementations.Channels
_libraryManager = libraryManager;
}
+ /// <inheritdoc />
public string Name => "Refresh Channels";
+ /// <inheritdoc />
public string Description => "Refreshes internet channel information.";
+ /// <inheritdoc />
public string Category => "Internet Channels";
+ /// <inheritdoc />
public bool IsHidden => ((ChannelManager)_channelManager).Channels.Length == 0;
+ /// <inheritdoc />
public bool IsEnabled => true;
+ /// <inheritdoc />
public bool IsLogged => true;
+ /// <inheritdoc />
+ public string Key => "RefreshInternetChannels";
+
+ /// <inheritdoc />
public async Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
{
var manager = (ChannelManager)_channelManager;
@@ -49,18 +63,18 @@ namespace Emby.Server.Implementations.Channels
.ConfigureAwait(false);
}
- /// <summary>
- /// Creates the triggers that define when the task will run
- /// </summary>
+ /// <inheritdoc />
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
- return new[] {
+ return new[]
+ {
// Every so often
- new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks}
+ new TaskTriggerInfo
+ {
+ Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks
+ }
};
}
-
- public string Key => "RefreshInternetChannels";
}
}
diff --git a/Emby.Server.Implementations/Collections/CollectionImageProvider.cs b/Emby.Server.Implementations/Collections/CollectionImageProvider.cs
index 1fa556ec9..21ba0288e 100644
--- a/Emby.Server.Implementations/Collections/CollectionImageProvider.cs
+++ b/Emby.Server.Implementations/Collections/CollectionImageProvider.cs
@@ -1,6 +1,5 @@
#pragma warning disable CS1591
-using System;
using System.Collections.Generic;
using System.Linq;
using Emby.Server.Implementations.Images;
diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs
index 1d7c11989..321952874 100644
--- a/Emby.Server.Implementations/Collections/CollectionManager.cs
+++ b/Emby.Server.Implementations/Collections/CollectionManager.cs
@@ -347,7 +347,10 @@ namespace Emby.Server.Implementations.Collections
private readonly IServerConfigurationManager _config;
private readonly ILogger _logger;
- public CollectionManagerEntryPoint(ICollectionManager collectionManager, IServerConfigurationManager config, ILogger logger)
+ public CollectionManagerEntryPoint(
+ ICollectionManager collectionManager,
+ IServerConfigurationManager config,
+ ILogger<CollectionManagerEntryPoint> logger)
{
_collectionManager = (CollectionManager)collectionManager;
_config = config;
diff --git a/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs b/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs
index 3d8d15d19..30b654886 100644
--- a/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs
+++ b/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs
@@ -1,5 +1,4 @@
using System;
-using System.Collections.Generic;
using System.Globalization;
using System.IO;
using Emby.Server.Implementations.AppBase;
diff --git a/Emby.Server.Implementations/ConfigurationOptions.cs b/Emby.Server.Implementations/ConfigurationOptions.cs
index 2ea7ff6e9..31fb5ca58 100644
--- a/Emby.Server.Implementations/ConfigurationOptions.cs
+++ b/Emby.Server.Implementations/ConfigurationOptions.cs
@@ -8,9 +8,9 @@ namespace Emby.Server.Implementations
public static Dictionary<string, string> Configuration => new Dictionary<string, string>
{
{ "HttpListenerHost:DefaultRedirectPath", "web/index.html" },
- { "MusicBrainz:BaseUrl", "https://www.musicbrainz.org" },
{ FfmpegProbeSizeKey, "1G" },
- { FfmpegAnalyzeDurationKey, "200M" }
+ { FfmpegAnalyzeDurationKey, "200M" },
+ { PlaylistsAllowDuplicatesKey, bool.TrueString }
};
}
}
diff --git a/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs b/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs
index 776074b72..de83b023d 100644
--- a/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs
+++ b/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs
@@ -121,7 +121,7 @@ namespace Emby.Server.Implementations.Cryptography
/// <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>
+ /// <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)
diff --git a/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs b/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs
index 2a8f2d6b3..37c678a5d 100644
--- a/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs
+++ b/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs
@@ -14,7 +14,7 @@ namespace Emby.Server.Implementations.Data
private readonly ILibraryManager _libraryManager;
private readonly ILogger _logger;
- public CleanDatabaseScheduledTask(ILibraryManager libraryManager, ILogger logger)
+ public CleanDatabaseScheduledTask(ILibraryManager libraryManager, ILogger<CleanDatabaseScheduledTask> logger)
{
_libraryManager = libraryManager;
_logger = logger;
diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
index ef1fe07c1..44f38504a 100644
--- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
@@ -49,6 +49,21 @@ namespace Emby.Server.Implementations.Data
private readonly TypeMapper _typeMapper;
private readonly JsonSerializerOptions _jsonOptions;
+ static SqliteItemRepository()
+ {
+ var queryPrefixText = new StringBuilder();
+ queryPrefixText.Append("insert into mediaattachments (");
+ foreach (var column in _mediaAttachmentSaveColumns)
+ {
+ queryPrefixText.Append(column)
+ .Append(',');
+ }
+
+ queryPrefixText.Length -= 1;
+ queryPrefixText.Append(") values ");
+ _mediaAttachmentInsertPrefix = queryPrefixText.ToString();
+ }
+
/// <summary>
/// Initializes a new instance of the <see cref="SqliteItemRepository"/> class.
/// </summary>
@@ -92,6 +107,8 @@ namespace Emby.Server.Implementations.Data
{
const string CreateMediaStreamsTableCommand
= "create table if not exists mediastreams (ItemId GUID, StreamIndex INT, StreamType TEXT, Codec TEXT, Language TEXT, ChannelLayout TEXT, Profile TEXT, AspectRatio TEXT, Path TEXT, IsInterlaced BIT, BitRate INT NULL, Channels INT NULL, SampleRate INT NULL, IsDefault BIT, IsForced BIT, IsExternal BIT, Height INT NULL, Width INT NULL, AverageFrameRate FLOAT NULL, RealFrameRate FLOAT NULL, Level FLOAT NULL, PixelFormat TEXT, BitDepth INT NULL, IsAnamorphic BIT NULL, RefFrames INT NULL, CodecTag TEXT NULL, Comment TEXT NULL, NalLengthSize TEXT NULL, IsAvc BIT NULL, Title TEXT NULL, TimeBase TEXT NULL, CodecTimeBase TEXT NULL, ColorPrimaries TEXT NULL, ColorSpace TEXT NULL, ColorTransfer TEXT NULL, PRIMARY KEY (ItemId, StreamIndex))";
+ const string CreateMediaAttachmentsTableCommand
+ = "create table if not exists mediaattachments (ItemId GUID, AttachmentIndex INT, Codec TEXT, CodecTag TEXT NULL, Comment TEXT NULL, Filename TEXT NULL, MIMEType TEXT NULL, PRIMARY KEY (ItemId, AttachmentIndex))";
string[] queries =
{
@@ -114,6 +131,7 @@ namespace Emby.Server.Implementations.Data
"create table if not exists " + ChaptersTableName + " (ItemId GUID, ChapterIndex INT NOT NULL, StartPositionTicks BIGINT NOT NULL, Name TEXT, ImagePath TEXT, PRIMARY KEY (ItemId, ChapterIndex))",
CreateMediaStreamsTableCommand,
+ CreateMediaAttachmentsTableCommand,
"pragma shrink_memory"
};
@@ -421,6 +439,19 @@ namespace Emby.Server.Implementations.Data
"ColorTransfer"
};
+ private static readonly string[] _mediaAttachmentSaveColumns =
+ {
+ "ItemId",
+ "AttachmentIndex",
+ "Codec",
+ "CodecTag",
+ "Comment",
+ "Filename",
+ "MIMEType"
+ };
+
+ private static readonly string _mediaAttachmentInsertPrefix;
+
private static string GetSaveItemCommandText()
{
var saveColumns = new []
@@ -3490,20 +3521,6 @@ namespace Emby.Server.Implementations.Data
}
var includeTypes = query.IncludeItemTypes.SelectMany(MapIncludeItemTypes).ToArray();
- if (includeTypes.Length == 1)
- {
- whereClauses.Add("type=@type");
- if (statement != null)
- {
- statement.TryBind("@type", includeTypes[0]);
- }
- }
- else if (includeTypes.Length > 1)
- {
- var inClause = string.Join(",", includeTypes.Select(i => "'" + i + "'"));
- whereClauses.Add($"type in ({inClause})");
- }
-
// Only specify excluded types if no included types are specified
if (includeTypes.Length == 0)
{
@@ -3522,6 +3539,19 @@ namespace Emby.Server.Implementations.Data
whereClauses.Add($"type not in ({inClause})");
}
}
+ else if (includeTypes.Length == 1)
+ {
+ whereClauses.Add("type=@type");
+ if (statement != null)
+ {
+ statement.TryBind("@type", includeTypes[0]);
+ }
+ }
+ else if (includeTypes.Length > 1)
+ {
+ var inClause = string.Join(",", includeTypes.Select(i => "'" + i + "'"));
+ whereClauses.Add($"type in ({inClause})");
+ }
if (query.ChannelIds.Length == 1)
{
@@ -4896,7 +4926,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
// Not crazy about having this all the way down here, but at least it's in one place
readonly Dictionary<string, string[]> _types = GetTypeMapDictionary();
- private IEnumerable<string> MapIncludeItemTypes(string value)
+ private string[] MapIncludeItemTypes(string value)
{
if (_types.TryGetValue(value, out string[] result))
{
@@ -5580,32 +5610,32 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
return counts;
}
- private List<Tuple<int, string>> GetItemValuesToSave(BaseItem item, List<string> inheritedTags)
+ private List<(int, string)> GetItemValuesToSave(BaseItem item, List<string> inheritedTags)
{
- var list = new List<Tuple<int, string>>();
+ var list = new List<(int, string)>();
if (item is IHasArtist hasArtist)
{
- list.AddRange(hasArtist.Artists.Select(i => new Tuple<int, string>(0, i)));
+ list.AddRange(hasArtist.Artists.Select(i => (0, i)));
}
if (item is IHasAlbumArtist hasAlbumArtist)
{
- list.AddRange(hasAlbumArtist.AlbumArtists.Select(i => new Tuple<int, string>(1, i)));
+ list.AddRange(hasAlbumArtist.AlbumArtists.Select(i => (1, i)));
}
- list.AddRange(item.Genres.Select(i => new Tuple<int, string>(2, i)));
- list.AddRange(item.Studios.Select(i => new Tuple<int, string>(3, i)));
- list.AddRange(item.Tags.Select(i => new Tuple<int, string>(4, i)));
+ list.AddRange(item.Genres.Select(i => (2, i)));
+ list.AddRange(item.Studios.Select(i => (3, i)));
+ list.AddRange(item.Tags.Select(i => (4, i)));
// keywords was 5
- list.AddRange(inheritedTags.Select(i => new Tuple<int, string>(6, i)));
+ list.AddRange(inheritedTags.Select(i => (6, i)));
return list;
}
- private void UpdateItemValues(Guid itemId, List<Tuple<int, string>> values, IDatabaseConnection db)
+ private void UpdateItemValues(Guid itemId, List<(int, string)> values, IDatabaseConnection db)
{
if (itemId.Equals(Guid.Empty))
{
@@ -5627,7 +5657,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
InsertItemValues(guidBlob, values, db);
}
- private void InsertItemValues(byte[] idBlob, List<Tuple<int, string>> values, IDatabaseConnection db)
+ private void InsertItemValues(byte[] idBlob, List<(int, string)> values, IDatabaseConnection db)
{
var startIndex = 0;
var limit = 100;
@@ -6136,5 +6166,175 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
return item;
}
+
+ public List<MediaAttachment> GetMediaAttachments(MediaAttachmentQuery query)
+ {
+ CheckDisposed();
+
+ if (query == null)
+ {
+ throw new ArgumentNullException(nameof(query));
+ }
+
+ var cmdText = "select "
+ + string.Join(",", _mediaAttachmentSaveColumns)
+ + " from mediaattachments where"
+ + " ItemId=@ItemId";
+
+ if (query.Index.HasValue)
+ {
+ cmdText += " AND AttachmentIndex=@AttachmentIndex";
+ }
+
+ cmdText += " order by AttachmentIndex ASC";
+
+ var list = new List<MediaAttachment>();
+ using (var connection = GetConnection(true))
+ using (var statement = PrepareStatement(connection, cmdText))
+ {
+ statement.TryBind("@ItemId", query.ItemId.ToByteArray());
+
+ if (query.Index.HasValue)
+ {
+ statement.TryBind("@AttachmentIndex", query.Index.Value);
+ }
+
+ foreach (var row in statement.ExecuteQuery())
+ {
+ list.Add(GetMediaAttachment(row));
+ }
+ }
+
+ return list;
+ }
+
+ public void SaveMediaAttachments(
+ Guid id,
+ IReadOnlyList<MediaAttachment> attachments,
+ CancellationToken cancellationToken)
+ {
+ CheckDisposed();
+ if (id == Guid.Empty)
+ {
+ throw new ArgumentException(nameof(id));
+ }
+
+ if (attachments == null)
+ {
+ throw new ArgumentNullException(nameof(attachments));
+ }
+
+ cancellationToken.ThrowIfCancellationRequested();
+
+ using (var connection = GetConnection())
+ {
+ connection.RunInTransaction(db =>
+ {
+ var itemIdBlob = id.ToByteArray();
+
+ db.Execute("delete from mediaattachments where ItemId=@ItemId", itemIdBlob);
+
+ InsertMediaAttachments(itemIdBlob, attachments, db, cancellationToken);
+
+ }, TransactionMode);
+ }
+ }
+
+ private void InsertMediaAttachments(
+ byte[] idBlob,
+ IReadOnlyList<MediaAttachment> attachments,
+ IDatabaseConnection db,
+ CancellationToken cancellationToken)
+ {
+ const int InsertAtOnce = 10;
+
+ for (var startIndex = 0; startIndex < attachments.Count; startIndex += InsertAtOnce)
+ {
+ var insertText = new StringBuilder(_mediaAttachmentInsertPrefix);
+
+ var endIndex = Math.Min(attachments.Count, startIndex + InsertAtOnce);
+
+ for (var i = startIndex; i < endIndex; i++)
+ {
+ var index = i.ToString(CultureInfo.InvariantCulture);
+ insertText.Append("(@ItemId, ");
+
+ foreach (var column in _mediaAttachmentSaveColumns.Skip(1))
+ {
+ insertText.Append("@" + column + index + ",");
+ }
+
+ insertText.Length -= 1;
+
+ insertText.Append("),");
+ }
+
+ insertText.Length--;
+
+ cancellationToken.ThrowIfCancellationRequested();
+
+ using (var statement = PrepareStatement(db, insertText.ToString()))
+ {
+ statement.TryBind("@ItemId", idBlob);
+
+ for (var i = startIndex; i < endIndex; i++)
+ {
+ var index = i.ToString(CultureInfo.InvariantCulture);
+
+ var attachment = attachments[i];
+
+ statement.TryBind("@AttachmentIndex" + index, attachment.Index);
+ statement.TryBind("@Codec" + index, attachment.Codec);
+ statement.TryBind("@CodecTag" + index, attachment.CodecTag);
+ statement.TryBind("@Comment" + index, attachment.Comment);
+ statement.TryBind("@FileName" + index, attachment.FileName);
+ statement.TryBind("@MimeType" + index, attachment.MimeType);
+ }
+
+ statement.Reset();
+ statement.MoveNext();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets the attachment.
+ /// </summary>
+ /// <param name="reader">The reader.</param>
+ /// <returns>MediaAttachment</returns>
+ private MediaAttachment GetMediaAttachment(IReadOnlyList<IResultSetValue> reader)
+ {
+ var item = new MediaAttachment
+ {
+ Index = reader[1].ToInt()
+ };
+
+ if (reader[2].SQLiteType != SQLiteType.Null)
+ {
+ item.Codec = reader[2].ToString();
+ }
+
+ if (reader[2].SQLiteType != SQLiteType.Null)
+ {
+ item.CodecTag = reader[3].ToString();
+ }
+
+ if (reader[4].SQLiteType != SQLiteType.Null)
+ {
+ item.Comment = reader[4].ToString();
+ }
+
+ if (reader[6].SQLiteType != SQLiteType.Null)
+ {
+ item.FileName = reader[5].ToString();
+ }
+
+ if (reader[6].SQLiteType != SQLiteType.Null)
+ {
+ item.MimeType = reader[6].ToString();
+ }
+
+ return item;
+ }
}
}
diff --git a/Emby.Server.Implementations/Devices/DeviceManager.cs b/Emby.Server.Implementations/Devices/DeviceManager.cs
index 2393f1f45..adb8e793d 100644
--- a/Emby.Server.Implementations/Devices/DeviceManager.cs
+++ b/Emby.Server.Implementations/Devices/DeviceManager.cs
@@ -141,11 +141,10 @@ namespace Emby.Server.Implementations.Devices
public QueryResult<DeviceInfo> GetDevices(DeviceQuery query)
{
- var sessions = _authRepo.Get(new AuthenticationInfoQuery
+ IEnumerable<AuthenticationInfo> sessions = _authRepo.Get(new AuthenticationInfoQuery
{
//UserId = query.UserId
HasUser = true
-
}).Items;
// TODO: DeviceQuery doesn't seem to be used from client. Not even Swagger.
@@ -153,23 +152,19 @@ namespace Emby.Server.Implementations.Devices
{
var val = query.SupportsSync.Value;
- sessions = sessions.Where(i => GetCapabilities(i.DeviceId).SupportsSync == val).ToArray();
+ sessions = sessions.Where(i => GetCapabilities(i.DeviceId).SupportsSync == val);
}
if (!query.UserId.Equals(Guid.Empty))
{
var user = _userManager.GetUserById(query.UserId);
- sessions = sessions.Where(i => CanAccessDevice(user, i.DeviceId)).ToArray();
+ sessions = sessions.Where(i => CanAccessDevice(user, i.DeviceId));
}
var array = sessions.Select(ToDeviceInfo).ToArray();
- return new QueryResult<DeviceInfo>
- {
- Items = array,
- TotalRecordCount = array.Length
- };
+ return new QueryResult<DeviceInfo>(array);
}
private DeviceInfo ToDeviceInfo(AuthenticationInfo authInfo)
@@ -185,7 +180,7 @@ namespace Emby.Server.Implementations.Devices
LastUserName = authInfo.UserName,
Name = authInfo.DeviceName,
DateLastActivity = authInfo.DateLastActivity,
- IconUrl = caps == null ? null : caps.IconUrl
+ IconUrl = caps?.IconUrl
};
}
@@ -242,7 +237,7 @@ namespace Emby.Server.Implementations.Devices
try
{
- using (var fs = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
+ using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read))
{
await stream.CopyToAsync(fs).ConfigureAwait(false);
}
@@ -411,7 +406,10 @@ namespace Emby.Server.Implementations.Devices
private readonly IServerConfigurationManager _config;
private ILogger _logger;
- public DeviceManagerEntryPoint(IDeviceManager deviceManager, IServerConfigurationManager config, ILogger logger)
+ public DeviceManagerEntryPoint(
+ IDeviceManager deviceManager,
+ IServerConfigurationManager config,
+ ILogger<DeviceManagerEntryPoint> logger)
{
_deviceManager = (DeviceManager)deviceManager;
_config = config;
diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs
index 3d622b3fc..65711e89d 100644
--- a/Emby.Server.Implementations/Dto/DtoService.cs
+++ b/Emby.Server.Implementations/Dto/DtoService.cs
@@ -1361,56 +1361,33 @@ namespace Emby.Server.Implementations.Dto
return null;
}
- var supportedEnhancers = _imageProcessor.GetSupportedEnhancers(item, ImageType.Primary).ToArray();
-
ImageDimensions size;
var defaultAspectRatio = item.GetDefaultPrimaryImageAspectRatio();
if (defaultAspectRatio > 0)
{
- if (supportedEnhancers.Length == 0)
- {
- return defaultAspectRatio;
- }
+ return defaultAspectRatio;
+ }
- int dummyWidth = 200;
- int dummyHeight = Convert.ToInt32(dummyWidth / defaultAspectRatio);
- size = new ImageDimensions(dummyWidth, dummyHeight);
+ if (!imageInfo.IsLocalFile)
+ {
+ return null;
}
- else
+
+ try
{
- if (!imageInfo.IsLocalFile)
- {
- return null;
- }
+ size = _imageProcessor.GetImageDimensions(item, imageInfo);
- try
+ if (size.Width <= 0 || size.Height <= 0)
{
- size = _imageProcessor.GetImageDimensions(item, imageInfo);
-
- if (size.Width <= 0 || size.Height <= 0)
- {
- return null;
- }
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Failed to determine primary image aspect ratio for {0}", imageInfo.Path);
return null;
}
}
-
- foreach (var enhancer in supportedEnhancers)
+ catch (Exception ex)
{
- try
- {
- size = enhancer.GetEnhancedImageSize(item, ImageType.Primary, 0, size);
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error in image enhancer: {0}", enhancer.GetType().Name);
- }
+ _logger.LogError(ex, "Failed to determine primary image aspect ratio for {0}", imageInfo.Path);
+ return null;
}
var width = size.Width;
diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
index 7ae6f38a1..f8560ca85 100644
--- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj
+++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
@@ -29,13 +29,13 @@
<PackageReference Include="Microsoft.AspNetCore.ResponseCompression" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.2.1" />
- <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.0.1" />
- <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.0.1" />
- <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.0.1" />
+ <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.1" />
+ <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.1.1" />
+ <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.1" />
<PackageReference Include="Mono.Nat" Version="2.0.0" />
- <PackageReference Include="ServiceStack.Text.Core" Version="5.7.0" />
+ <PackageReference Include="ServiceStack.Text.Core" Version="5.8.0" />
<PackageReference Include="sharpcompress" Version="0.24.0" />
- <PackageReference Include="SQLitePCL.pretty.netstandard" Version="2.0.1" />
+ <PackageReference Include="SQLitePCL.pretty.netstandard" Version="2.1.0" />
<PackageReference Include="System.Interactive.Async" Version="4.0.0" />
</ItemGroup>
@@ -51,10 +51,10 @@
<!-- Code Analyzers-->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
- <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.7" />
- <PackageReference Include="SerilogAnalyzer" Version="0.15.0" />
- <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" />
- <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" />
+ <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" />
</ItemGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
diff --git a/Emby.Server.Implementations/EntryPoints/AutomaticRestartEntryPoint.cs b/Emby.Server.Implementations/EntryPoints/AutomaticRestartEntryPoint.cs
deleted file mode 100644
index d69b0909d..000000000
--- a/Emby.Server.Implementations/EntryPoints/AutomaticRestartEntryPoint.cs
+++ /dev/null
@@ -1,127 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Controller.Plugins;
-using MediaBrowser.Controller.Session;
-using MediaBrowser.Model.LiveTv;
-using MediaBrowser.Model.Tasks;
-using Microsoft.Extensions.Logging;
-
-namespace Emby.Server.Implementations.EntryPoints
-{
- public class AutomaticRestartEntryPoint : IServerEntryPoint
- {
- private readonly IServerApplicationHost _appHost;
- private readonly ILogger _logger;
- private readonly ITaskManager _iTaskManager;
- private readonly ISessionManager _sessionManager;
- private readonly IServerConfigurationManager _config;
- private readonly ILiveTvManager _liveTvManager;
-
- private Timer _timer;
-
- public AutomaticRestartEntryPoint(IServerApplicationHost appHost, ILogger logger, ITaskManager iTaskManager, ISessionManager sessionManager, IServerConfigurationManager config, ILiveTvManager liveTvManager)
- {
- _appHost = appHost;
- _logger = logger;
- _iTaskManager = iTaskManager;
- _sessionManager = sessionManager;
- _config = config;
- _liveTvManager = liveTvManager;
- }
-
- public Task RunAsync()
- {
- if (_appHost.CanSelfRestart)
- {
- _appHost.HasPendingRestartChanged += _appHost_HasPendingRestartChanged;
- }
-
- return Task.CompletedTask;
- }
-
- void _appHost_HasPendingRestartChanged(object sender, EventArgs e)
- {
- DisposeTimer();
-
- if (_appHost.HasPendingRestart)
- {
- _timer = new Timer(TimerCallback, null, TimeSpan.FromMinutes(15), TimeSpan.FromMinutes(15));
- }
- }
-
- private async void TimerCallback(object state)
- {
- if (_config.Configuration.EnableAutomaticRestart)
- {
- var isIdle = await IsIdle().ConfigureAwait(false);
-
- if (isIdle)
- {
- DisposeTimer();
-
- _logger.LogInformation("Automatically restarting the system because it is idle and a restart is required.");
-
- try
- {
- _appHost.Restart();
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error restarting server");
- }
- }
- }
- }
-
- private async Task<bool> IsIdle()
- {
- if (_iTaskManager.ScheduledTasks.Any(i => i.State != TaskState.Idle))
- {
- return false;
- }
-
- if (_liveTvManager.Services.Count == 1)
- {
- try
- {
- var timers = await _liveTvManager.GetTimers(new TimerQuery(), CancellationToken.None).ConfigureAwait(false);
- if (timers.Items.Any(i => i.Status == RecordingStatus.InProgress))
- {
- return false;
- }
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error getting timers");
- }
- }
-
- var now = DateTime.UtcNow;
-
- return !_sessionManager.Sessions.Any(i => (now - i.LastActivityDate).TotalMinutes < 30);
- }
-
- public void Dispose()
- {
- _appHost.HasPendingRestartChanged -= _appHost_HasPendingRestartChanged;
-
- DisposeTimer();
- }
-
- private void DisposeTimer()
- {
- if (_timer != null)
- {
- _timer.Dispose();
- _timer = null;
- }
- }
- }
-}
diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
index 5f938e59a..8e3236407 100644
--- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
@@ -15,7 +15,6 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Events;
-using MediaBrowser.Model.Extensions;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.EntryPoints
@@ -56,7 +55,12 @@ namespace Emby.Server.Implementations.EntryPoints
private readonly IProviderManager _providerManager;
- public LibraryChangedNotifier(ILibraryManager libraryManager, ISessionManager sessionManager, IUserManager userManager, ILogger logger, IProviderManager providerManager)
+ public LibraryChangedNotifier(
+ ILibraryManager libraryManager,
+ ISessionManager sessionManager,
+ IUserManager userManager,
+ ILogger<LibraryChangedNotifier> logger,
+ IProviderManager providerManager)
{
_libraryManager = libraryManager;
_sessionManager = sessionManager;
diff --git a/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs b/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs
index dbb3503c4..41c0c5115 100644
--- a/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs
@@ -12,14 +12,18 @@ using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.EntryPoints
{
- public class RecordingNotifier : IServerEntryPoint
+ public sealed class RecordingNotifier : IServerEntryPoint
{
private readonly ILiveTvManager _liveTvManager;
private readonly ISessionManager _sessionManager;
private readonly IUserManager _userManager;
private readonly ILogger _logger;
- public RecordingNotifier(ISessionManager sessionManager, IUserManager userManager, ILogger logger, ILiveTvManager liveTvManager)
+ public RecordingNotifier(
+ ISessionManager sessionManager,
+ IUserManager userManager,
+ ILogger<RecordingNotifier> logger,
+ ILiveTvManager liveTvManager)
{
_sessionManager = sessionManager;
_userManager = userManager;
@@ -27,32 +31,33 @@ namespace Emby.Server.Implementations.EntryPoints
_liveTvManager = liveTvManager;
}
+ /// <inheritdoc />
public Task RunAsync()
{
- _liveTvManager.TimerCancelled += _liveTvManager_TimerCancelled;
- _liveTvManager.SeriesTimerCancelled += _liveTvManager_SeriesTimerCancelled;
- _liveTvManager.TimerCreated += _liveTvManager_TimerCreated;
- _liveTvManager.SeriesTimerCreated += _liveTvManager_SeriesTimerCreated;
+ _liveTvManager.TimerCancelled += OnLiveTvManagerTimerCancelled;
+ _liveTvManager.SeriesTimerCancelled += OnLiveTvManagerSeriesTimerCancelled;
+ _liveTvManager.TimerCreated += OnLiveTvManagerTimerCreated;
+ _liveTvManager.SeriesTimerCreated += OnLiveTvManagerSeriesTimerCreated;
return Task.CompletedTask;
}
- private void _liveTvManager_SeriesTimerCreated(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e)
+ private void OnLiveTvManagerSeriesTimerCreated(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e)
{
SendMessage("SeriesTimerCreated", e.Argument);
}
- private void _liveTvManager_TimerCreated(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e)
+ private void OnLiveTvManagerTimerCreated(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e)
{
SendMessage("TimerCreated", e.Argument);
}
- private void _liveTvManager_SeriesTimerCancelled(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e)
+ private void OnLiveTvManagerSeriesTimerCancelled(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e)
{
SendMessage("SeriesTimerCancelled", e.Argument);
}
- private void _liveTvManager_TimerCancelled(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e)
+ private void OnLiveTvManagerTimerCancelled(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e)
{
SendMessage("TimerCancelled", e.Argument);
}
@@ -63,11 +68,7 @@ namespace Emby.Server.Implementations.EntryPoints
try
{
- await _sessionManager.SendMessageToUserSessions(users, name, info, CancellationToken.None);
- }
- catch (ObjectDisposedException)
- {
- // TODO Log exception or Investigate and properly fix.
+ await _sessionManager.SendMessageToUserSessions(users, name, info, CancellationToken.None).ConfigureAwait(false);
}
catch (Exception ex)
{
@@ -75,12 +76,13 @@ namespace Emby.Server.Implementations.EntryPoints
}
}
+ /// <inheritdoc />
public void Dispose()
{
- _liveTvManager.TimerCancelled -= _liveTvManager_TimerCancelled;
- _liveTvManager.SeriesTimerCancelled -= _liveTvManager_SeriesTimerCancelled;
- _liveTvManager.TimerCreated -= _liveTvManager_TimerCreated;
- _liveTvManager.SeriesTimerCreated -= _liveTvManager_SeriesTimerCreated;
+ _liveTvManager.TimerCancelled -= OnLiveTvManagerTimerCancelled;
+ _liveTvManager.SeriesTimerCancelled -= OnLiveTvManagerSeriesTimerCancelled;
+ _liveTvManager.TimerCreated -= OnLiveTvManagerTimerCreated;
+ _liveTvManager.SeriesTimerCreated -= OnLiveTvManagerSeriesTimerCreated;
}
}
}
diff --git a/Emby.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs b/Emby.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs
index f00996b5f..54f4b67e6 100644
--- a/Emby.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs
+++ b/Emby.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs
@@ -6,7 +6,6 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Tasks;
-using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.EntryPoints
{
@@ -15,21 +14,17 @@ namespace Emby.Server.Implementations.EntryPoints
/// </summary>
public class RefreshUsersMetadata : IScheduledTask, IConfigurableScheduledTask
{
- private readonly ILogger _logger;
-
/// <summary>
/// The user manager.
/// </summary>
private readonly IUserManager _userManager;
-
- private IFileSystem _fileSystem;
+ private readonly IFileSystem _fileSystem;
/// <summary>
/// Initializes a new instance of the <see cref="RefreshUsersMetadata" /> class.
/// </summary>
- public RefreshUsersMetadata(ILogger logger, IUserManager userManager, IFileSystem fileSystem)
+ public RefreshUsersMetadata(IUserManager userManager, IFileSystem fileSystem)
{
- _logger = logger;
_userManager = userManager;
_fileSystem = fileSystem;
}
diff --git a/Emby.Server.Implementations/EntryPoints/StartupWizard.cs b/Emby.Server.Implementations/EntryPoints/StartupWizard.cs
index 161788c63..5f2d629fe 100644
--- a/Emby.Server.Implementations/EntryPoints/StartupWizard.cs
+++ b/Emby.Server.Implementations/EntryPoints/StartupWizard.cs
@@ -3,37 +3,28 @@ using Emby.Server.Implementations.Browser;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Plugins;
-using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.EntryPoints
{
/// <summary>
/// Class StartupWizard.
/// </summary>
- public class StartupWizard : IServerEntryPoint
+ public sealed class StartupWizard : IServerEntryPoint
{
/// <summary>
/// The app host.
/// </summary>
private readonly IServerApplicationHost _appHost;
-
- /// <summary>
- /// The user manager.
- /// </summary>
- private readonly ILogger _logger;
-
- private IServerConfigurationManager _config;
+ private readonly IServerConfigurationManager _config;
/// <summary>
/// Initializes a new instance of the <see cref="StartupWizard"/> class.
/// </summary>
/// <param name="appHost">The application host.</param>
- /// <param name="logger">The logger.</param>
/// <param name="config">The configuration manager.</param>
- public StartupWizard(IServerApplicationHost appHost, ILogger logger, IServerConfigurationManager config)
+ public StartupWizard(IServerApplicationHost appHost, IServerConfigurationManager config)
{
_appHost = appHost;
- _logger = logger;
_config = config;
}
diff --git a/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs b/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs
index 9ee219854..50ba0f8fa 100644
--- a/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs
+++ b/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs
@@ -1,10 +1,8 @@
-using System;
+using System.Threading;
using System.Threading.Tasks;
using Emby.Server.Implementations.Udp;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Plugins;
-using MediaBrowser.Model.Net;
-using MediaBrowser.Model.Serialization;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.EntryPoints
@@ -12,7 +10,7 @@ namespace Emby.Server.Implementations.EntryPoints
/// <summary>
/// Class UdpServerEntryPoint.
/// </summary>
- public class UdpServerEntryPoint : IServerEntryPoint
+ public sealed class UdpServerEntryPoint : IServerEntryPoint
{
/// <summary>
/// The port of the UDP server.
@@ -23,69 +21,50 @@ namespace Emby.Server.Implementations.EntryPoints
/// The logger.
/// </summary>
private readonly ILogger _logger;
- private readonly ISocketFactory _socketFactory;
private readonly IServerApplicationHost _appHost;
- private readonly IJsonSerializer _json;
/// <summary>
/// The UDP server.
/// </summary>
private UdpServer _udpServer;
+ private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
+ private bool _disposed = false;
/// <summary>
/// Initializes a new instance of the <see cref="UdpServerEntryPoint" /> class.
/// </summary>
public UdpServerEntryPoint(
- ILogger logger,
- IServerApplicationHost appHost,
- IJsonSerializer json,
- ISocketFactory socketFactory)
+ ILogger<UdpServerEntryPoint> logger,
+ IServerApplicationHost appHost)
{
_logger = logger;
_appHost = appHost;
- _json = json;
- _socketFactory = socketFactory;
- }
-
- /// <inheritdoc />
- public Task RunAsync()
- {
- var udpServer = new UdpServer(_logger, _appHost, _json, _socketFactory);
- try
- {
- udpServer.Start(PortNumber);
-
- _udpServer = udpServer;
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Failed to start UDP Server");
- }
- return Task.CompletedTask;
}
/// <inheritdoc />
- public void Dispose()
+ public async Task RunAsync()
{
- Dispose(true);
- GC.SuppressFinalize(this);
+ _udpServer = new UdpServer(_logger, _appHost);
+ _udpServer.Start(PortNumber, _cancellationTokenSource.Token);
}
- /// <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)
+ /// <inheritdoc />
+ public void Dispose()
{
- if (dispose)
+ if (_disposed)
{
- if (_udpServer != null)
- {
- _udpServer.Dispose();
- }
+ return;
}
+
+ _cancellationTokenSource.Cancel();
+ _udpServer.Dispose();
+ _cancellationTokenSource.Dispose();
+ _cancellationTokenSource = null;
+ _udpServer = null;
+
+ _disposed = true;
}
}
}
diff --git a/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs
index e431da148..3618b88c5 100644
--- a/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs
@@ -12,39 +12,38 @@ using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Session;
-using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.EntryPoints
{
- public class UserDataChangeNotifier : IServerEntryPoint
+ public sealed class UserDataChangeNotifier : IServerEntryPoint
{
+ private const int UpdateDuration = 500;
+
private readonly ISessionManager _sessionManager;
- private readonly ILogger _logger;
private readonly IUserDataManager _userDataManager;
private readonly IUserManager _userManager;
+ private readonly Dictionary<Guid, List<BaseItem>> _changedItems = new Dictionary<Guid, List<BaseItem>>();
+
private readonly object _syncLock = new object();
- private Timer UpdateTimer { get; set; }
- private const int UpdateDuration = 500;
+ private Timer _updateTimer;
- private readonly Dictionary<Guid, List<BaseItem>> _changedItems = new Dictionary<Guid, List<BaseItem>>();
- public UserDataChangeNotifier(IUserDataManager userDataManager, ISessionManager sessionManager, ILogger logger, IUserManager userManager)
+ public UserDataChangeNotifier(IUserDataManager userDataManager, ISessionManager sessionManager, IUserManager userManager)
{
_userDataManager = userDataManager;
_sessionManager = sessionManager;
- _logger = logger;
_userManager = userManager;
}
public Task RunAsync()
{
- _userDataManager.UserDataSaved += _userDataManager_UserDataSaved;
+ _userDataManager.UserDataSaved += OnUserDataManagerUserDataSaved;
return Task.CompletedTask;
}
- void _userDataManager_UserDataSaved(object sender, UserDataSaveEventArgs e)
+ void OnUserDataManagerUserDataSaved(object sender, UserDataSaveEventArgs e)
{
if (e.SaveReason == UserDataSaveReason.PlaybackProgress)
{
@@ -53,14 +52,17 @@ namespace Emby.Server.Implementations.EntryPoints
lock (_syncLock)
{
- if (UpdateTimer == null)
+ if (_updateTimer == null)
{
- UpdateTimer = new Timer(UpdateTimerCallback, null, UpdateDuration,
- Timeout.Infinite);
+ _updateTimer = new Timer(
+ UpdateTimerCallback,
+ null,
+ UpdateDuration,
+ Timeout.Infinite);
}
else
{
- UpdateTimer.Change(UpdateDuration, Timeout.Infinite);
+ _updateTimer.Change(UpdateDuration, Timeout.Infinite);
}
if (!_changedItems.TryGetValue(e.UserId, out List<BaseItem> keys))
@@ -96,10 +98,10 @@ namespace Emby.Server.Implementations.EntryPoints
var task = SendNotifications(changes, CancellationToken.None);
- if (UpdateTimer != null)
+ if (_updateTimer != null)
{
- UpdateTimer.Dispose();
- UpdateTimer = null;
+ _updateTimer.Dispose();
+ _updateTimer = null;
}
}
}
@@ -144,13 +146,13 @@ namespace Emby.Server.Implementations.EntryPoints
public void Dispose()
{
- if (UpdateTimer != null)
+ if (_updateTimer != null)
{
- UpdateTimer.Dispose();
- UpdateTimer = null;
+ _updateTimer.Dispose();
+ _updateTimer = null;
}
- _userDataManager.UserDataSaved -= _userDataManager_UserDataSaved;
+ _userDataManager.UserDataSaved -= OnUserDataManagerUserDataSaved;
}
}
}
diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs
index 50233ea48..45fa03cdd 100644
--- a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs
+++ b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs
@@ -78,7 +78,7 @@ namespace Emby.Server.Implementations.HttpClientManager
if (!string.IsNullOrWhiteSpace(userInfo))
{
_logger.LogWarning("Found userInfo in url: {0} ... url: {1}", userInfo, url);
- url = url.Replace(userInfo + '@', string.Empty);
+ url = url.Replace(userInfo + '@', string.Empty, StringComparison.Ordinal);
}
var request = new HttpRequestMessage(method, url);
@@ -197,7 +197,7 @@ namespace Emby.Server.Implementations.HttpClientManager
if (File.Exists(responseCachePath)
&& _fileSystem.GetLastWriteTimeUtc(responseCachePath).Add(cacheLength) > DateTime.UtcNow)
{
- var stream = _fileSystem.GetFileStream(responseCachePath, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read, true);
+ var stream = new FileStream(responseCachePath, FileMode.Open, FileAccess.Read, FileShare.Read, IODefaults.FileStreamBufferSize, true);
return new HttpResponseInfo
{
@@ -220,7 +220,7 @@ namespace Emby.Server.Implementations.HttpClientManager
FileMode.Create,
FileAccess.Write,
FileShare.None,
- StreamDefaults.DefaultFileStreamBufferSize,
+ IODefaults.FileStreamBufferSize,
true))
{
await response.Content.CopyToAsync(fileStream).ConfigureAwait(false);
diff --git a/Emby.Server.Implementations/HttpServer/FileWriter.cs b/Emby.Server.Implementations/HttpServer/FileWriter.cs
index c1c8c3eb3..82f1e5b52 100644
--- a/Emby.Server.Implementations/HttpServer/FileWriter.cs
+++ b/Emby.Server.Implementations/HttpServer/FileWriter.cs
@@ -71,7 +71,7 @@ namespace Emby.Server.Implementations.HttpServer
SetRangeValues();
}
- FileShare = FileShareMode.Read;
+ FileShare = FileShare.Read;
Cookies = new List<Cookie>();
}
@@ -93,7 +93,7 @@ namespace Emby.Server.Implementations.HttpServer
public List<Cookie> Cookies { get; private set; }
- public FileShareMode FileShare { get; set; }
+ public FileShare FileShare { get; set; }
/// <summary>
/// Gets the options.
@@ -221,17 +221,17 @@ namespace Emby.Server.Implementations.HttpServer
}
}
- public async Task TransmitFile(Stream stream, string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken)
+ public async Task TransmitFile(Stream stream, string path, long offset, long count, FileShare fileShare, CancellationToken cancellationToken)
{
- var fileOpenOptions = FileOpenOptions.SequentialScan;
+ var fileOptions = FileOptions.SequentialScan;
// use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
- fileOpenOptions |= FileOpenOptions.Asynchronous;
+ fileOptions |= FileOptions.Asynchronous;
}
- using (var fs = _fileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, fileShareMode, fileOpenOptions))
+ using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, fileShare, IODefaults.FileStreamBufferSize, fileOptions))
{
if (offset > 0)
{
@@ -244,7 +244,7 @@ namespace Emby.Server.Implementations.HttpServer
}
else
{
- await fs.CopyToAsync(stream, StreamDefaults.DefaultCopyToBufferSize, cancellationToken).ConfigureAwait(false);
+ await fs.CopyToAsync(stream, IODefaults.CopyToBufferSize, cancellationToken).ConfigureAwait(false);
}
}
}
diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
index 2aefc9fe5..93572b8bf 100644
--- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
+++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
@@ -39,9 +39,9 @@ namespace Emby.Server.Implementations.HttpServer
private readonly Func<Type, Func<string, object>> _funcParseFn;
private readonly string _defaultRedirectPath;
private readonly string _baseUrlPrefix;
- private readonly Dictionary<Type, Type> ServiceOperationsMap = new Dictionary<Type, Type>();
- private IWebSocketListener[] _webSocketListeners = Array.Empty<IWebSocketListener>();
+ private readonly Dictionary<Type, Type> _serviceOperationsMap = new Dictionary<Type, Type>();
private readonly List<IWebSocketConnection> _webSocketConnections = new List<IWebSocketConnection>();
+ private IWebSocketListener[] _webSocketListeners = Array.Empty<IWebSocketListener>();
private bool _disposed = false;
public HttpListenerHost(
@@ -71,6 +71,8 @@ namespace Emby.Server.Implementations.HttpServer
ResponseFilters = Array.Empty<Action<IRequest, HttpResponse, object>>();
}
+ public event EventHandler<GenericEventArgs<IWebSocketConnection>> WebSocketConnected;
+
public Action<IRequest, HttpResponse, object>[] ResponseFilters { get; set; }
public static HttpListenerHost Instance { get; protected set; }
@@ -81,8 +83,6 @@ namespace Emby.Server.Implementations.HttpServer
public ServiceController ServiceController { get; private set; }
- public event EventHandler<GenericEventArgs<IWebSocketConnection>> WebSocketConnected;
-
public object CreateInstance(Type type)
{
return _appHost.CreateInstance(type);
@@ -90,7 +90,7 @@ namespace Emby.Server.Implementations.HttpServer
private static string NormalizeUrlPath(string path)
{
- if (path.StartsWith("/"))
+ if (path.Length > 0 && path[0] == '/')
{
// If the path begins with a leading slash, just return it as-is
return path;
@@ -130,13 +130,13 @@ namespace Emby.Server.Implementations.HttpServer
public Type GetServiceTypeByRequest(Type requestType)
{
- ServiceOperationsMap.TryGetValue(requestType, out var serviceType);
+ _serviceOperationsMap.TryGetValue(requestType, out var serviceType);
return serviceType;
}
public void AddServiceInfo(Type serviceType, Type requestType)
{
- ServiceOperationsMap[requestType] = serviceType;
+ _serviceOperationsMap[requestType] = serviceType;
}
private List<IHasRequestFilter> GetRequestFilterAttributes(Type requestDtoType)
@@ -198,7 +198,7 @@ namespace Emby.Server.Implementations.HttpServer
else
{
var inners = agg.InnerExceptions;
- if (inners != null && inners.Count > 0)
+ if (inners.Count > 0)
{
return GetActualException(inners[0]);
}
@@ -218,7 +218,6 @@ namespace Emby.Server.Implementations.HttpServer
case FileNotFoundException _:
case ResourceNotFoundException _: return 404;
case MethodNotAllowedException _: return 405;
- case RemoteServiceUnavailableException _: return 502;
default: return 500;
}
}
@@ -362,7 +361,7 @@ namespace Emby.Server.Implementations.HttpServer
return true;
}
- host = host ?? string.Empty;
+ host ??= string.Empty;
if (_networkManager.IsInPrivateAddressSpace(host))
{
@@ -433,7 +432,7 @@ namespace Emby.Server.Implementations.HttpServer
}
/// <summary>
- /// Overridable method that can be used to implement a custom hnandler
+ /// Overridable method that can be used to implement a custom handler.
/// </summary>
public async Task RequestHandler(IHttpRequest httpReq, string urlString, string host, string localPath, CancellationToken cancellationToken)
{
@@ -492,7 +491,7 @@ namespace Emby.Server.Implementations.HttpServer
|| string.Equals(localPath, _baseUrlPrefix, StringComparison.OrdinalIgnoreCase)
|| string.Equals(localPath, "/", StringComparison.OrdinalIgnoreCase)
|| string.IsNullOrEmpty(localPath)
- || !localPath.StartsWith(_baseUrlPrefix))
+ || !localPath.StartsWith(_baseUrlPrefix, StringComparison.OrdinalIgnoreCase))
{
// Always redirect back to the default path if the base prefix is invalid or missing
_logger.LogDebug("Normalizing a URL at {0}", localPath);
@@ -693,7 +692,10 @@ namespace Emby.Server.Implementations.HttpServer
protected virtual void Dispose(bool disposing)
{
- if (_disposed) return;
+ if (_disposed)
+ {
+ return;
+ }
if (disposing)
{
diff --git a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
index a62b4e7af..b42662420 100644
--- a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
+++ b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
@@ -439,7 +439,7 @@ namespace Emby.Server.Implementations.HttpServer
public Task<object> GetStaticFileResult(IRequest requestContext,
string path,
- FileShareMode fileShare = FileShareMode.Read)
+ FileShare fileShare = FileShare.Read)
{
if (string.IsNullOrEmpty(path))
{
@@ -463,7 +463,7 @@ namespace Emby.Server.Implementations.HttpServer
throw new ArgumentException("Path can't be empty.", nameof(options));
}
- if (fileShare != FileShareMode.Read && fileShare != FileShareMode.ReadWrite)
+ if (fileShare != FileShare.Read && fileShare != FileShare.ReadWrite)
{
throw new ArgumentException("FileShare must be either Read or ReadWrite");
}
@@ -491,9 +491,9 @@ namespace Emby.Server.Implementations.HttpServer
/// <param name="path">The path.</param>
/// <param name="fileShare">The file share.</param>
/// <returns>Stream.</returns>
- private Stream GetFileStream(string path, FileShareMode fileShare)
+ private Stream GetFileStream(string path, FileShare fileShare)
{
- return _fileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, fileShare);
+ return new FileStream(path, FileMode.Open, FileAccess.Read, fileShare);
}
public Task<object> GetStaticResult(IRequest requestContext,
diff --git a/Emby.Server.Implementations/IO/ExtendedFileSystemInfo.cs b/Emby.Server.Implementations/IO/ExtendedFileSystemInfo.cs
index 3150f3367..545d73e05 100644
--- a/Emby.Server.Implementations/IO/ExtendedFileSystemInfo.cs
+++ b/Emby.Server.Implementations/IO/ExtendedFileSystemInfo.cs
@@ -5,7 +5,9 @@ namespace Emby.Server.Implementations.IO
public class ExtendedFileSystemInfo
{
public bool IsHidden { get; set; }
+
public bool IsReadOnly { get; set; }
+
public bool Exists { get; set; }
}
}
diff --git a/Emby.Server.Implementations/IO/FileRefresher.cs b/Emby.Server.Implementations/IO/FileRefresher.cs
index 4b5b11f01..ef93779aa 100644
--- a/Emby.Server.Implementations/IO/FileRefresher.cs
+++ b/Emby.Server.Implementations/IO/FileRefresher.cs
@@ -14,27 +14,29 @@ namespace Emby.Server.Implementations.IO
{
public class FileRefresher : IDisposable
{
- private ILogger Logger { get; set; }
- private ILibraryManager LibraryManager { get; set; }
- private IServerConfigurationManager ConfigurationManager { get; set; }
+ private readonly ILogger _logger;
+ private readonly ILibraryManager _libraryManager;
+ private readonly IServerConfigurationManager _configurationManager;
+
private readonly List<string> _affectedPaths = new List<string>();
- private Timer _timer;
private readonly object _timerLock = new object();
- public string Path { get; private set; }
-
- public event EventHandler<EventArgs> Completed;
+ private Timer _timer;
public FileRefresher(string path, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, ILogger logger)
{
logger.LogDebug("New file refresher created for {0}", path);
Path = path;
- ConfigurationManager = configurationManager;
- LibraryManager = libraryManager;
- Logger = logger;
+ _configurationManager = configurationManager;
+ _libraryManager = libraryManager;
+ _logger = logger;
AddPath(path);
}
+ public event EventHandler<EventArgs> Completed;
+
+ public string Path { get; private set; }
+
private void AddAffectedPath(string path)
{
if (string.IsNullOrEmpty(path))
@@ -79,11 +81,11 @@ namespace Emby.Server.Implementations.IO
if (_timer == null)
{
- _timer = new Timer(OnTimerCallback, null, TimeSpan.FromSeconds(ConfigurationManager.Configuration.LibraryMonitorDelay), TimeSpan.FromMilliseconds(-1));
+ _timer = new Timer(OnTimerCallback, null, TimeSpan.FromSeconds(_configurationManager.Configuration.LibraryMonitorDelay), TimeSpan.FromMilliseconds(-1));
}
else
{
- _timer.Change(TimeSpan.FromSeconds(ConfigurationManager.Configuration.LibraryMonitorDelay), TimeSpan.FromMilliseconds(-1));
+ _timer.Change(TimeSpan.FromSeconds(_configurationManager.Configuration.LibraryMonitorDelay), TimeSpan.FromMilliseconds(-1));
}
}
}
@@ -92,7 +94,7 @@ namespace Emby.Server.Implementations.IO
{
lock (_timerLock)
{
- Logger.LogDebug("Resetting file refresher from {0} to {1}", Path, path);
+ _logger.LogDebug("Resetting file refresher from {0} to {1}", Path, path);
Path = path;
AddAffectedPath(path);
@@ -115,7 +117,7 @@ namespace Emby.Server.Implementations.IO
paths = _affectedPaths.ToList();
}
- Logger.LogDebug("Timer stopped.");
+ _logger.LogDebug("Timer stopped.");
DisposeTimer();
Completed?.Invoke(this, EventArgs.Empty);
@@ -126,7 +128,7 @@ namespace Emby.Server.Implementations.IO
}
catch (Exception ex)
{
- Logger.LogError(ex, "Error processing directory changes");
+ _logger.LogError(ex, "Error processing directory changes");
}
}
@@ -146,7 +148,7 @@ namespace Emby.Server.Implementations.IO
continue;
}
- Logger.LogInformation("{name} ({path}) will be refreshed.", item.Name, item.Path);
+ _logger.LogInformation("{name} ({path}) will be refreshed.", item.Name, item.Path);
try
{
@@ -157,11 +159,11 @@ namespace Emby.Server.Implementations.IO
// For now swallow and log.
// Research item: If an IOException occurs, the item may be in a disconnected state (media unavailable)
// Should we remove it from it's parent?
- Logger.LogError(ex, "Error refreshing {name}", item.Name);
+ _logger.LogError(ex, "Error refreshing {name}", item.Name);
}
catch (Exception ex)
{
- Logger.LogError(ex, "Error refreshing {name}", item.Name);
+ _logger.LogError(ex, "Error refreshing {name}", item.Name);
}
}
}
@@ -177,7 +179,7 @@ namespace Emby.Server.Implementations.IO
while (item == null && !string.IsNullOrEmpty(path))
{
- item = LibraryManager.FindByPath(path, null);
+ item = _libraryManager.FindByPath(path, null);
path = System.IO.Path.GetDirectoryName(path);
}
diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs
index 442fbabd1..48599beb7 100644
--- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs
+++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs
@@ -2,8 +2,8 @@
using System;
using System.Collections.Generic;
-using System.Globalization;
using System.Diagnostics;
+using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
@@ -16,7 +16,7 @@ using OperatingSystem = MediaBrowser.Common.System.OperatingSystem;
namespace Emby.Server.Implementations.IO
{
/// <summary>
- /// Class ManagedFileSystem
+ /// Class ManagedFileSystem.
/// </summary>
public class ManagedFileSystem : IFileSystem
{
@@ -79,20 +79,20 @@ namespace Emby.Server.Implementations.IO
public virtual string MakeAbsolutePath(string folderPath, string filePath)
{
- if (string.IsNullOrWhiteSpace(filePath)
- // stream
- || filePath.Contains("://"))
+ // path is actually a stream
+ if (string.IsNullOrWhiteSpace(filePath) || filePath.Contains("://", StringComparison.Ordinal))
{
return filePath;
}
if (filePath.Length > 3 && filePath[1] == ':' && filePath[2] == '/')
{
- return filePath; // absolute local path
+ // absolute local path
+ return filePath;
}
// unc path
- if (filePath.StartsWith("\\\\"))
+ if (filePath.StartsWith("\\\\", StringComparison.Ordinal))
{
return filePath;
}
@@ -100,16 +100,19 @@ namespace Emby.Server.Implementations.IO
var firstChar = filePath[0];
if (firstChar == '/')
{
- // For this we don't really know.
+ // for this we don't really know
return filePath;
}
- if (firstChar == '\\') //relative path
+
+ // relative path
+ if (firstChar == '\\')
{
filePath = filePath.Substring(1);
}
+
try
{
- return Path.Combine(Path.GetFullPath(folderPath), filePath);
+ return Path.GetFullPath(Path.Combine(folderPath, filePath));
}
catch (ArgumentException)
{
@@ -130,11 +133,7 @@ namespace Emby.Server.Implementations.IO
/// </summary>
/// <param name="shortcutPath">The shortcut path.</param>
/// <param name="target">The target.</param>
- /// <exception cref="ArgumentNullException">
- /// shortcutPath
- /// or
- /// target
- /// </exception>
+ /// <exception cref="ArgumentNullException">The shortcutPath or target is null.</exception>
public virtual void CreateShortcut(string shortcutPath, string target)
{
if (string.IsNullOrEmpty(shortcutPath))
@@ -280,11 +279,11 @@ namespace Emby.Server.Implementations.IO
}
/// <summary>
- /// Takes a filename and removes invalid characters
+ /// Takes a filename and removes invalid characters.
/// </summary>
/// <param name="filename">The filename.</param>
/// <returns>System.String.</returns>
- /// <exception cref="ArgumentNullException">filename</exception>
+ /// <exception cref="ArgumentNullException">The filename is null.</exception>
public virtual string GetValidFilename(string filename)
{
var builder = new StringBuilder(filename);
@@ -365,90 +364,9 @@ namespace Emby.Server.Implementations.IO
return GetLastWriteTimeUtc(GetFileSystemInfo(path));
}
- /// <summary>
- /// Gets the file stream.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <param name="mode">The mode.</param>
- /// <param name="access">The access.</param>
- /// <param name="share">The share.</param>
- /// <param name="isAsync">if set to <c>true</c> [is asynchronous].</param>
- /// <returns>FileStream.</returns>
- public virtual Stream GetFileStream(string path, FileOpenMode mode, FileAccessMode access, FileShareMode share, bool isAsync = false)
- {
- if (isAsync)
- {
- return GetFileStream(path, mode, access, share, FileOpenOptions.Asynchronous);
- }
-
- return GetFileStream(path, mode, access, share, FileOpenOptions.None);
- }
-
- public virtual Stream GetFileStream(string path, FileOpenMode mode, FileAccessMode access, FileShareMode share, FileOpenOptions fileOpenOptions)
- => new FileStream(path, GetFileMode(mode), GetFileAccess(access), GetFileShare(share), 4096, GetFileOptions(fileOpenOptions));
-
- private static FileOptions GetFileOptions(FileOpenOptions mode)
- {
- var val = (int)mode;
- return (FileOptions)val;
- }
-
- private static FileMode GetFileMode(FileOpenMode mode)
- {
- switch (mode)
- {
- //case FileOpenMode.Append:
- // return FileMode.Append;
- case FileOpenMode.Create:
- return FileMode.Create;
- case FileOpenMode.CreateNew:
- return FileMode.CreateNew;
- case FileOpenMode.Open:
- return FileMode.Open;
- case FileOpenMode.OpenOrCreate:
- return FileMode.OpenOrCreate;
- //case FileOpenMode.Truncate:
- // return FileMode.Truncate;
- default:
- throw new Exception("Unrecognized FileOpenMode");
- }
- }
-
- private static FileAccess GetFileAccess(FileAccessMode mode)
- {
- switch (mode)
- {
- //case FileAccessMode.ReadWrite:
- // return FileAccess.ReadWrite;
- case FileAccessMode.Write:
- return FileAccess.Write;
- case FileAccessMode.Read:
- return FileAccess.Read;
- default:
- throw new Exception("Unrecognized FileAccessMode");
- }
- }
-
- private static FileShare GetFileShare(FileShareMode mode)
- {
- switch (mode)
- {
- case FileShareMode.ReadWrite:
- return FileShare.ReadWrite;
- case FileShareMode.Write:
- return FileShare.Write;
- case FileShareMode.Read:
- return FileShare.Read;
- case FileShareMode.None:
- return FileShare.None;
- default:
- throw new Exception("Unrecognized FileShareMode");
- }
- }
-
public virtual void SetHidden(string path, bool isHidden)
{
- if (OperatingSystem.Id != MediaBrowser.Model.System.OperatingSystemId.Windows)
+ if (OperatingSystem.Id != OperatingSystemId.Windows)
{
return;
}
@@ -472,7 +390,7 @@ namespace Emby.Server.Implementations.IO
public virtual void SetReadOnly(string path, bool isReadOnly)
{
- if (OperatingSystem.Id != MediaBrowser.Model.System.OperatingSystemId.Windows)
+ if (OperatingSystem.Id != OperatingSystemId.Windows)
{
return;
}
@@ -496,7 +414,7 @@ namespace Emby.Server.Implementations.IO
public virtual void SetAttributes(string path, bool isHidden, bool isReadOnly)
{
- if (OperatingSystem.Id != MediaBrowser.Model.System.OperatingSystemId.Windows)
+ if (OperatingSystem.Id != OperatingSystemId.Windows)
{
return;
}
@@ -779,7 +697,7 @@ namespace Emby.Server.Implementations.IO
public virtual void SetExecutable(string path)
{
- if (OperatingSystem.Id == MediaBrowser.Model.System.OperatingSystemId.Darwin)
+ if (OperatingSystem.Id == OperatingSystemId.Darwin)
{
RunProcess("chmod", "+x \"" + path + "\"", Path.GetDirectoryName(path));
}
diff --git a/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs b/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs
index 94f60ea62..ab036eca7 100644
--- a/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs
+++ b/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs
@@ -70,9 +70,9 @@ namespace Emby.Server.Implementations.Library
byte[] calculatedHash = _cryptographyProvider.ComputeHash(
readyHash.Id,
passwordbytes,
- readyHash.Salt);
+ readyHash.Salt.ToArray());
- if (calculatedHash.SequenceEqual(readyHash.Hash))
+ if (readyHash.Hash.SequenceEqual(calculatedHash))
{
success = true;
}
@@ -148,17 +148,18 @@ namespace Emby.Server.Implementations.Library
// TODO: make use of iterations parameter?
PasswordHash passwordHash = PasswordHash.Parse(user.Password);
+ var salt = passwordHash.Salt.ToArray();
return new PasswordHash(
passwordHash.Id,
_cryptographyProvider.ComputeHash(
passwordHash.Id,
Encoding.UTF8.GetBytes(str),
- passwordHash.Salt),
- passwordHash.Salt,
+ salt),
+ salt,
passwordHash.Parameters.ToDictionary(x => x.Key, y => y.Value)).ToString();
}
- public byte[] GetHashed(User user, string str)
+ public ReadOnlySpan<byte> GetHashed(User user, string str)
{
if (string.IsNullOrEmpty(user.Password))
{
@@ -170,7 +171,7 @@ namespace Emby.Server.Implementations.Library
return _cryptographyProvider.ComputeHash(
passwordHash.Id,
Encoding.UTF8.GetBytes(str),
- passwordHash.Salt);
+ passwordHash.Salt.ToArray());
}
}
}
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index cee51479e..8ec4d08be 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -28,14 +28,15 @@ using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
+using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Configuration;
+using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Library;
using MediaBrowser.Model.Net;
@@ -53,6 +54,9 @@ namespace Emby.Server.Implementations.Library
/// </summary>
public class LibraryManager : ILibraryManager
{
+ private NamingOptions _namingOptions;
+ private string[] _videoFileExtensions;
+
/// <summary>
/// Gets or sets the postscan tasks.
/// </summary>
@@ -138,6 +142,7 @@ namespace Emby.Server.Implementations.Library
public bool IsScanRunning { get; private set; }
private IServerApplicationHost _appHost;
+ private readonly IMediaEncoder _mediaEncoder;
/// <summary>
/// The _library items cache
@@ -171,7 +176,8 @@ namespace Emby.Server.Implementations.Library
Func<ILibraryMonitor> libraryMonitorFactory,
IFileSystem fileSystem,
Func<IProviderManager> providerManagerFactory,
- Func<IUserViewManager> userviewManager)
+ Func<IUserViewManager> userviewManager,
+ IMediaEncoder mediaEncoder)
{
_appHost = appHost;
_logger = loggerFactory.CreateLogger(nameof(LibraryManager));
@@ -183,6 +189,7 @@ namespace Emby.Server.Implementations.Library
_fileSystem = fileSystem;
_providerManagerFactory = providerManagerFactory;
_userviewManager = userviewManager;
+ _mediaEncoder = mediaEncoder;
_libraryItemsCache = new ConcurrentDictionary<Guid, BaseItem>();
@@ -707,10 +714,10 @@ namespace Emby.Server.Implementations.Library
}
/// <summary>
- /// Creates the root media folder
+ /// Creates the root media folder.
/// </summary>
/// <returns>AggregateFolder.</returns>
- /// <exception cref="InvalidOperationException">Cannot create the root folder until plugins have loaded</exception>
+ /// <exception cref="InvalidOperationException">Cannot create the root folder until plugins have loaded.</exception>
public AggregateFolder CreateRootFolder()
{
var rootFolderPath = ConfigurationManager.ApplicationPaths.RootFolderPath;
@@ -821,7 +828,6 @@ namespace Emby.Server.Implementations.Library
{
// If this returns multiple items it could be tricky figuring out which one is correct.
// In most cases, the newest one will be and the others obsolete but not yet cleaned up
-
if (string.IsNullOrEmpty(path))
{
throw new ArgumentNullException(nameof(path));
@@ -841,7 +847,7 @@ namespace Emby.Server.Implementations.Library
}
/// <summary>
- /// Gets a Person
+ /// Gets the person.
/// </summary>
/// <param name="name">The name.</param>
/// <returns>Task{Person}.</returns>
@@ -851,7 +857,7 @@ namespace Emby.Server.Implementations.Library
}
/// <summary>
- /// Gets a Studio
+ /// Gets the studio.
/// </summary>
/// <param name="name">The name.</param>
/// <returns>Task{Studio}.</returns>
@@ -876,7 +882,7 @@ namespace Emby.Server.Implementations.Library
}
/// <summary>
- /// Gets a Genre
+ /// Gets the genre.
/// </summary>
/// <param name="name">The name.</param>
/// <returns>Task{Genre}.</returns>
@@ -886,7 +892,7 @@ namespace Emby.Server.Implementations.Library
}
/// <summary>
- /// Gets the genre.
+ /// Gets the music genre.
/// </summary>
/// <param name="name">The name.</param>
/// <returns>Task{MusicGenre}.</returns>
@@ -896,7 +902,7 @@ namespace Emby.Server.Implementations.Library
}
/// <summary>
- /// Gets a Year
+ /// Gets the year.
/// </summary>
/// <param name="value">The value.</param>
/// <returns>Task{Year}.</returns>
@@ -937,7 +943,6 @@ namespace Emby.Server.Implementations.Library
IncludeItemTypes = new[] { typeof(T).Name },
Name = name,
DtoOptions = options
-
}).Cast<MusicArtist>()
.OrderBy(i => i.IsAccessedByName ? 1 : 0)
.Cast<T>()
@@ -1073,9 +1078,9 @@ namespace Emby.Server.Implementations.Library
var innerProgress = new ActionableProgress<double>();
- innerProgress.RegisterAction(pct => progress.Report(pct * .96));
+ innerProgress.RegisterAction(pct => progress.Report(pct * 0.96));
- // Now validate the entire media library
+ // Validate the entire media library
await RootFolder.ValidateChildren(innerProgress, cancellationToken, new MetadataRefreshOptions(new DirectoryService(_fileSystem)), recursive: true).ConfigureAwait(false);
progress.Report(96);
@@ -1084,7 +1089,6 @@ namespace Emby.Server.Implementations.Library
innerProgress.RegisterAction(pct => progress.Report(96 + (pct * .04)));
- // Run post-scan tasks
await RunPostScanTasks(innerProgress, cancellationToken).ConfigureAwait(false);
progress.Report(100);
@@ -1135,7 +1139,7 @@ namespace Emby.Server.Implementations.Library
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error running postscan task");
+ _logger.LogError(ex, "Error running post-scan task");
}
numComplete++;
@@ -1168,7 +1172,6 @@ namespace Emby.Server.Implementations.Library
return _fileSystem.GetDirectoryPaths(ConfigurationManager.ApplicationPaths.DefaultUserViewsPath)
.Select(dir => GetVirtualFolderInfo(dir, topLibraryFolders, refreshQueue))
- .OrderBy(i => i.Name)
.ToList();
}
@@ -1400,25 +1403,32 @@ namespace Emby.Server.Implementations.Library
private void SetTopParentOrAncestorIds(InternalItemsQuery query)
{
- if (query.AncestorIds.Length == 0)
+ var ancestorIds = query.AncestorIds;
+ int len = ancestorIds.Length;
+ if (len == 0)
{
return;
}
- var parents = query.AncestorIds.Select(i => GetItemById(i)).ToList();
-
- if (parents.All(i => i is ICollectionFolder || i is UserView))
+ var parents = new BaseItem[len];
+ for (int i = 0; i < len; i++)
{
- // Optimize by querying against top level views
- query.TopParentIds = parents.SelectMany(i => GetTopParentIdsForQuery(i, query.User)).ToArray();
- query.AncestorIds = Array.Empty<Guid>();
-
- // Prevent searching in all libraries due to empty filter
- if (query.TopParentIds.Length == 0)
+ parents[i] = GetItemById(ancestorIds[i]);
+ if (!(parents[i] is ICollectionFolder || parents[i] is UserView))
{
- query.TopParentIds = new[] { Guid.NewGuid() };
+ return;
}
}
+
+ // Optimize by querying against top level views
+ query.TopParentIds = parents.SelectMany(i => GetTopParentIdsForQuery(i, query.User)).ToArray();
+ query.AncestorIds = Array.Empty<Guid>();
+
+ // Prevent searching in all libraries due to empty filter
+ if (query.TopParentIds.Length == 0)
+ {
+ query.TopParentIds = new[] { Guid.NewGuid() };
+ }
}
public QueryResult<(BaseItem, ItemCounts)> GetAlbumArtists(InternalItemsQuery query)
@@ -1579,7 +1589,7 @@ namespace Emby.Server.Implementations.Library
public async Task<IEnumerable<Video>> GetIntros(BaseItem item, User user)
{
var tasks = IntroProviders
- .OrderBy(i => i.GetType().Name.IndexOf("Default", StringComparison.OrdinalIgnoreCase) == -1 ? 0 : 1)
+ .OrderBy(i => i.GetType().Name.Contains("Default", StringComparison.OrdinalIgnoreCase) ? 1 : 0)
.Take(1)
.Select(i => GetIntros(i, item, user));
@@ -2357,33 +2367,22 @@ namespace Emby.Server.Implementations.Library
new SubtitleResolver(BaseItem.LocalizationManager, _fileSystem).AddExternalSubtitleStreams(streams, videoPath, streams.Count, files);
}
- public bool IsVideoFile(string path, LibraryOptions libraryOptions)
+ /// <inheritdoc />
+ public bool IsVideoFile(string path)
{
var resolver = new VideoResolver(GetNamingOptions());
return resolver.IsVideoFile(path);
}
- public bool IsVideoFile(string path)
- {
- return IsVideoFile(path, new LibraryOptions());
- }
-
- public bool IsAudioFile(string path, LibraryOptions libraryOptions)
- {
- var parser = new AudioFileParser(GetNamingOptions());
- return parser.IsAudioFile(path);
- }
-
+ /// <inheritdoc />
public bool IsAudioFile(string path)
- {
- return IsAudioFile(path, new LibraryOptions());
- }
+ => AudioFileParser.IsAudioFile(path, GetNamingOptions());
+ /// <inheritdoc />
public int? GetSeasonNumberFromPath(string path)
- {
- return new SeasonPathParser().Parse(path, true, true).SeasonNumber;
- }
+ => SeasonPathParser.Parse(path, true, true).SeasonNumber;
+ /// <inheritdoc />
public bool FillMissingEpisodeNumbersFromPath(Episode episode, bool forceRefresh)
{
var series = episode.Series;
@@ -2407,6 +2406,38 @@ namespace Emby.Server.Implementations.Library
episodeInfo = new Naming.TV.EpisodeInfo();
}
+ try
+ {
+ var libraryOptions = GetLibraryOptions(episode);
+ if (libraryOptions.EnableEmbeddedEpisodeInfos && string.Equals(episodeInfo.Container, "mp4", StringComparison.OrdinalIgnoreCase))
+ {
+ // Read from metadata
+ var mediaInfo = _mediaEncoder.GetMediaInfo(new MediaInfoRequest
+ {
+ MediaSource = episode.GetMediaSources(false)[0],
+ MediaType = DlnaProfileType.Video
+ }, CancellationToken.None).GetAwaiter().GetResult();
+ if (mediaInfo.ParentIndexNumber > 0)
+ {
+ episodeInfo.SeasonNumber = mediaInfo.ParentIndexNumber;
+ }
+
+ if (mediaInfo.IndexNumber > 0)
+ {
+ episodeInfo.EpisodeNumber = mediaInfo.IndexNumber;
+ }
+
+ if (!string.IsNullOrEmpty(mediaInfo.ShowName))
+ {
+ episodeInfo.SeriesName = mediaInfo.ShowName;
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error reading the episode informations with ffprobe. Episode: {EpisodeInfo}", episodeInfo.Path);
+ }
+
var changed = false;
if (episodeInfo.IsByDate)
@@ -2508,20 +2539,10 @@ namespace Emby.Server.Implementations.Library
public NamingOptions GetNamingOptions()
{
- return GetNamingOptionsInternal();
- }
-
- private NamingOptions _namingOptions;
- private string[] _videoFileExtensions;
-
- private NamingOptions GetNamingOptionsInternal()
- {
if (_namingOptions == null)
{
- var options = new NamingOptions();
-
- _namingOptions = options;
- _videoFileExtensions = _namingOptions.VideoFileExtensions.ToArray();
+ _namingOptions = new NamingOptions();
+ _videoFileExtensions = _namingOptions.VideoFileExtensions;
}
return _namingOptions;
@@ -2532,11 +2553,10 @@ namespace Emby.Server.Implementations.Library
var resolver = new VideoResolver(GetNamingOptions());
var result = resolver.CleanDateTime(name);
- var cleanName = resolver.CleanString(result.Name);
return new ItemLookupInfo
{
- Name = cleanName.Name,
+ Name = resolver.TryCleanString(result.Name, out var newName) ? newName.ToString() : result.Name,
Year = result.Year
};
}
@@ -2558,7 +2578,7 @@ namespace Emby.Server.Implementations.Library
if (currentVideo != null)
{
- files.AddRange(currentVideo.Extras.Where(i => string.Equals(i.ExtraType, "trailer", StringComparison.OrdinalIgnoreCase)).Select(i => _fileSystem.GetFileInfo(i.Path)));
+ files.AddRange(currentVideo.Extras.Where(i => i.ExtraType == ExtraType.Trailer).Select(i => _fileSystem.GetFileInfo(i.Path)));
}
var resolvers = new IItemResolver[]
@@ -2608,7 +2628,7 @@ namespace Emby.Server.Implementations.Library
if (currentVideo != null)
{
- files.AddRange(currentVideo.Extras.Where(i => !string.Equals(i.ExtraType, "trailer", StringComparison.OrdinalIgnoreCase)).Select(i => _fileSystem.GetFileInfo(i.Path)));
+ files.AddRange(currentVideo.Extras.Where(i => i.ExtraType != ExtraType.Trailer).Select(i => _fileSystem.GetFileInfo(i.Path)));
}
return ResolvePaths(files, directoryService, null, new LibraryOptions(), null)
@@ -2712,7 +2732,7 @@ namespace Emby.Server.Implementations.Library
if (!string.Equals(newPath, path, StringComparison.Ordinal))
{
- if (to.IndexOf('/') != -1)
+ if (to.IndexOf('/', StringComparison.Ordinal) != -1)
{
newPath = newPath.Replace('\\', '/');
}
@@ -2733,30 +2753,7 @@ namespace Emby.Server.Implementations.Library
var result = resolver.GetExtraInfo(item.Path);
- if (string.Equals(result.ExtraType, "deletedscene", StringComparison.OrdinalIgnoreCase))
- {
- item.ExtraType = ExtraType.DeletedScene;
- }
- else if (string.Equals(result.ExtraType, "behindthescenes", StringComparison.OrdinalIgnoreCase))
- {
- item.ExtraType = ExtraType.BehindTheScenes;
- }
- else if (string.Equals(result.ExtraType, "interview", StringComparison.OrdinalIgnoreCase))
- {
- item.ExtraType = ExtraType.Interview;
- }
- else if (string.Equals(result.ExtraType, "scene", StringComparison.OrdinalIgnoreCase))
- {
- item.ExtraType = ExtraType.Scene;
- }
- else if (string.Equals(result.ExtraType, "sample", StringComparison.OrdinalIgnoreCase))
- {
- item.ExtraType = ExtraType.Sample;
- }
- else
- {
- item.ExtraType = ExtraType.Clip;
- }
+ item.ExtraType = result.ExtraType;
}
public List<PersonInfo> GetPeople(InternalPeopleQuery query)
diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs
index 22193c997..70d5bd9f4 100644
--- a/Emby.Server.Implementations/Library/MediaSourceManager.cs
+++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs
@@ -130,7 +130,22 @@ namespace Emby.Server.Implementations.Library
return streams;
}
- public async Task<List<MediaSourceInfo>> GetPlayackMediaSources(BaseItem item, User user, bool allowMediaProbe, bool enablePathSubstitution, CancellationToken cancellationToken)
+ /// <inheritdoc />
+ public List<MediaAttachment> GetMediaAttachments(MediaAttachmentQuery query)
+ {
+ return _itemRepo.GetMediaAttachments(query);
+ }
+
+ /// <inheritdoc />
+ public List<MediaAttachment> GetMediaAttachments(Guid itemId)
+ {
+ return GetMediaAttachments(new MediaAttachmentQuery
+ {
+ ItemId = itemId
+ });
+ }
+
+ public async Task<List<MediaSourceInfo>> GetPlaybackMediaSources(BaseItem item, User user, bool allowMediaProbe, bool enablePathSubstitution, CancellationToken cancellationToken)
{
var mediaSources = GetStaticMediaSources(item, enablePathSubstitution, user);
@@ -292,7 +307,7 @@ namespace Emby.Server.Implementations.Library
return await GetLiveStream(liveStreamId, cancellationToken).ConfigureAwait(false);
}
- var sources = await GetPlayackMediaSources(item, null, false, enablePathSubstitution, cancellationToken).ConfigureAwait(false);
+ var sources = await GetPlaybackMediaSources(item, null, false, enablePathSubstitution, cancellationToken).ConfigureAwait(false);
return sources.FirstOrDefault(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase));
}
diff --git a/Emby.Server.Implementations/Library/ResolverHelper.cs b/Emby.Server.Implementations/Library/ResolverHelper.cs
index 96d1bff92..34dcbbe28 100644
--- a/Emby.Server.Implementations/Library/ResolverHelper.cs
+++ b/Emby.Server.Implementations/Library/ResolverHelper.cs
@@ -9,7 +9,7 @@ using MediaBrowser.Model.IO;
namespace Emby.Server.Implementations.Library
{
/// <summary>
- /// Class ResolverHelper
+ /// Class ResolverHelper.
/// </summary>
public static class ResolverHelper
{
diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
index 9d4bd9e59..fefc8e789 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
@@ -72,7 +72,6 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
{
// Return audio if the path is a file and has a matching extension
- var libraryOptions = args.GetLibraryOptions();
var collectionType = args.GetCollectionType();
var isBooksCollectionType = string.Equals(collectionType, CollectionType.Books, StringComparison.OrdinalIgnoreCase);
@@ -91,7 +90,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
return FindAudio<AudioBook>(args, args.Path, args.Parent, files, args.DirectoryService, collectionType, false);
}
- if (LibraryManager.IsAudioFile(args.Path, libraryOptions))
+ if (LibraryManager.IsAudioFile(args.Path))
{
var extension = Path.GetExtension(args.Path);
@@ -104,7 +103,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
var isMixedCollectionType = string.IsNullOrEmpty(collectionType);
// For conflicting extensions, give priority to videos
- if (isMixedCollectionType && LibraryManager.IsVideoFile(args.Path, libraryOptions))
+ if (isMixedCollectionType && LibraryManager.IsVideoFile(args.Path))
{
return null;
}
@@ -120,7 +119,6 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
{
item = new MediaBrowser.Controller.Entities.Audio.Audio();
}
-
else if (isBooksCollectionType)
{
item = new AudioBook();
diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
index 4a2d210d5..85b1b6e32 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
@@ -5,7 +5,6 @@ using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Resolvers;
-using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using Microsoft.Extensions.Logging;
@@ -76,15 +75,15 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
}
/// <summary>
- /// Determine if the supplied file data points to a music album
+ /// Determine if the supplied file data points to a music album.
/// </summary>
- public bool IsMusicAlbum(string path, IDirectoryService directoryService, LibraryOptions libraryOptions)
+ public bool IsMusicAlbum(string path, IDirectoryService directoryService)
{
- return ContainsMusic(directoryService.GetFileSystemEntries(path), true, directoryService, _logger, _fileSystem, libraryOptions, _libraryManager);
+ return ContainsMusic(directoryService.GetFileSystemEntries(path), true, directoryService, _logger, _fileSystem, _libraryManager);
}
/// <summary>
- /// Determine if the supplied resolve args should be considered a music album
+ /// Determine if the supplied resolve args should be considered a music album.
/// </summary>
/// <param name="args">The args.</param>
/// <returns><c>true</c> if [is music album] [the specified args]; otherwise, <c>false</c>.</returns>
@@ -94,7 +93,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
if (args.IsDirectory)
{
// if (args.Parent is MusicArtist) return true; //saves us from testing children twice
- if (ContainsMusic(args.FileSystemChildren, true, args.DirectoryService, _logger, _fileSystem, args.GetLibraryOptions(), _libraryManager))
+ if (ContainsMusic(args.FileSystemChildren, true, args.DirectoryService, _logger, _fileSystem, _libraryManager))
{
return true;
}
@@ -104,7 +103,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
}
/// <summary>
- /// Determine if the supplied list contains what we should consider music
+ /// Determine if the supplied list contains what we should consider music.
/// </summary>
private bool ContainsMusic(
IEnumerable<FileSystemMetadata> list,
@@ -112,12 +111,13 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
IDirectoryService directoryService,
ILogger logger,
IFileSystem fileSystem,
- LibraryOptions libraryOptions,
ILibraryManager libraryManager)
{
var discSubfolderCount = 0;
var notMultiDisc = false;
+ var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions();
+ var parser = new AlbumParser(namingOptions);
foreach (var fileSystemInfo in list)
{
if (fileSystemInfo.IsDirectory)
@@ -130,11 +130,11 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
}
var path = fileSystemInfo.FullName;
- var hasMusic = ContainsMusic(directoryService.GetFileSystemEntries(path), false, directoryService, logger, fileSystem, libraryOptions, libraryManager);
+ var hasMusic = ContainsMusic(directoryService.GetFileSystemEntries(path), false, directoryService, logger, fileSystem, libraryManager);
if (hasMusic)
{
- if (IsMultiDiscFolder(path, libraryOptions))
+ if (parser.IsMultiPart(path))
{
logger.LogDebug("Found multi-disc folder: " + path);
discSubfolderCount++;
@@ -151,7 +151,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
{
var fullName = fileSystemInfo.FullName;
- if (libraryManager.IsAudioFile(fullName, libraryOptions))
+ if (libraryManager.IsAudioFile(fullName))
{
return true;
}
@@ -165,15 +165,5 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
return discSubfolderCount > 0;
}
-
- private bool IsMultiDiscFolder(string path, LibraryOptions libraryOptions)
- {
- var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions();
-
- var parser = new AlbumParser(namingOptions);
- var result = parser.ParseMultiPart(path);
-
- return result.IsMultiPart;
- }
}
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs
index ee7e84929..681db4896 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs
@@ -27,7 +27,11 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
/// <param name="fileSystem">The file system.</param>
/// <param name="libraryManager">The library manager.</param>
/// <param name="config">The configuration manager.</param>
- public MusicArtistResolver(ILogger logger, IFileSystem fileSystem, ILibraryManager libraryManager, IServerConfigurationManager config)
+ public MusicArtistResolver(
+ ILogger<MusicArtistResolver> logger,
+ IFileSystem fileSystem,
+ ILibraryManager libraryManager,
+ IServerConfigurationManager config)
{
_logger = logger;
_fileSystem = fileSystem;
@@ -80,14 +84,17 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
}
// Avoid mis-identifying top folders
- if (args.Parent.IsRoot) return null;
+ if (args.Parent.IsRoot)
+ {
+ return null;
+ }
var directoryService = args.DirectoryService;
var albumResolver = new MusicAlbumResolver(_logger, _fileSystem, _libraryManager);
// If we contain an album assume we are an artist folder
- return args.FileSystemChildren.Where(i => i.IsDirectory).Any(i => albumResolver.IsMusicAlbum(i.FullName, directoryService, args.GetLibraryOptions())) ? new MusicArtist() : null;
+ return args.FileSystemChildren.Where(i => i.IsDirectory).Any(i => albumResolver.IsMusicAlbum(i.FullName, directoryService)) ? new MusicArtist() : null;
}
}
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
index c4bb861b8..fb75593bd 100644
--- a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
@@ -79,6 +79,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
};
break;
}
+
if (IsBluRayDirectory(child.FullName, filename, args.DirectoryService))
{
videoInfo = parser.ResolveDirectory(args.Path);
@@ -136,7 +137,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
return null;
}
- if (LibraryManager.IsVideoFile(args.Path, args.GetLibraryOptions()) || videoInfo.IsStub)
+ if (LibraryManager.IsVideoFile(args.Path) || videoInfo.IsStub)
{
var path = args.Path;
diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
index 6c7690055..cb67c8aa7 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
@@ -21,6 +21,28 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
/// </summary>
public class MovieResolver : BaseVideoResolver<Video>, IMultiItemResolver
{
+ private string[] _validCollectionTypes = new[]
+ {
+ CollectionType.Movies,
+ CollectionType.HomeVideos,
+ CollectionType.MusicVideos,
+ CollectionType.Movies,
+ CollectionType.Photos
+ };
+
+ private readonly IImageProcessor _imageProcessor;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MovieResolver"/> class.
+ /// </summary>
+ /// <param name="libraryManager">The library manager.</param>
+ /// <param name="imageProcessor">The image processor.</param>
+ public MovieResolver(ILibraryManager libraryManager, IImageProcessor imageProcessor)
+ : base(libraryManager)
+ {
+ _imageProcessor = imageProcessor;
+ }
+
/// <summary>
/// Gets the priority.
/// </summary>
@@ -144,7 +166,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
foreach (var video in resolverResult)
{
- var firstVideo = video.Files.First();
+ var firstVideo = video.Files[0];
var videoItem = new T
{
@@ -230,7 +252,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
// Owned items will be caught by the plain video resolver
if (args.Parent == null)
{
- //return FindMovie<Video>(args.Path, args.Parent, files, args.DirectoryService, collectionType);
+ // return FindMovie<Video>(args.Path, args.Parent, files, args.DirectoryService, collectionType);
return null;
}
@@ -275,7 +297,6 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
{
item = ResolveVideo<Movie>(args, true);
}
-
else if (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase) ||
string.Equals(collectionType, CollectionType.Photos, StringComparison.OrdinalIgnoreCase))
{
@@ -319,7 +340,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
{
if (item is Movie || item is MusicVideo)
{
- //we need to only look at the name of this actual item (not parents)
+ // We need to only look at the name of this actual item (not parents)
var justName = item.IsInMixedFolder ? Path.GetFileName(item.Path) : Path.GetFileName(item.ContainingFolderPath);
if (!string.IsNullOrEmpty(justName))
@@ -347,9 +368,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
}
/// <summary>
- /// Finds a movie based on a child file system entries
+ /// Finds a movie based on a child file system entries.
/// </summary>
- /// <typeparam name="T"></typeparam>
/// <returns>Movie.</returns>
private T FindMovie<T>(ItemResolveArgs args, string path, Folder parent, List<FileSystemMetadata> fileSystemEntries, IDirectoryService directoryService, string collectionType, bool parseName)
where T : Video, new()
@@ -377,6 +397,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
Set3DFormat(movie);
return movie;
}
+
if (IsBluRayDirectory(child.FullName, filename, directoryService))
{
var movie = new T
@@ -407,15 +428,15 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
}
// TODO: Allow GetMultiDiscMovie in here
- const bool supportsMultiVersion = true;
+ const bool SupportsMultiVersion = true;
- var result = ResolveVideos<T>(parent, fileSystemEntries, directoryService, supportsMultiVersion, collectionType, parseName) ??
+ var result = ResolveVideos<T>(parent, fileSystemEntries, directoryService, SupportsMultiVersion, collectionType, parseName) ??
new MultiItemResolverResult();
if (result.Items.Count == 1)
{
var videoPath = result.Items[0].Path;
- var hasPhotos = photos.Any(i => !PhotoResolver.IsOwnedByResolvedMedia(LibraryManager, libraryOptions, videoPath, i.Name));
+ var hasPhotos = photos.Any(i => !PhotoResolver.IsOwnedByResolvedMedia(LibraryManager, videoPath, i.Name));
if (!hasPhotos)
{
@@ -425,8 +446,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
return movie;
}
}
-
- if (result.Items.Count == 0 && multiDiscFolders.Count > 0)
+ else if (result.Items.Count == 0 && multiDiscFolders.Count > 0)
{
return GetMultiDiscMovie<T>(multiDiscFolders, directoryService);
}
@@ -437,7 +457,6 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
/// <summary>
/// Gets the multi disc movie.
/// </summary>
- /// <typeparam name="T"></typeparam>
/// <param name="multiDiscFolders">The folders.</param>
/// <param name="directoryService">The directory service.</param>
/// <returns>``0.</returns>
@@ -451,7 +470,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
var subFileEntries = directoryService.GetFileSystemEntries(i);
var subfolders = subFileEntries
- .Where(e => e.IsDirectory)
+ .Where(e => e.IsDirectory)
.ToList();
if (subfolders.Any(s => IsDvdDirectory(s.FullName, s.Name, directoryService)))
@@ -459,6 +478,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
videoTypes.Add(VideoType.Dvd);
return true;
}
+
if (subfolders.Any(s => IsBluRayDirectory(s.FullName, s.Name, directoryService)))
{
videoTypes.Add(VideoType.BluRay);
@@ -476,7 +496,6 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
}
return false;
-
}).OrderBy(i => i).ToList();
// If different video types were found, don't allow this
@@ -491,24 +510,24 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
}
var namingOptions = ((LibraryManager)LibraryManager).GetNamingOptions();
- var resolver = new StackResolver(namingOptions);
- var result = resolver.ResolveDirectories(folderPaths);
+ var result = new StackResolver(namingOptions).ResolveDirectories(folderPaths).ToList();
- if (result.Stacks.Count != 1)
+ if (result.Count != 1)
{
return null;
}
+ int additionalPartsLen = folderPaths.Count - 1;
+ var additionalParts = new string[additionalPartsLen];
+ folderPaths.CopyTo(1, additionalParts, 0, additionalPartsLen);
+
var returnVideo = new T
{
Path = folderPaths[0],
-
- AdditionalParts = folderPaths.Skip(1).ToArray(),
-
+ AdditionalParts = additionalParts,
VideoType = videoTypes[0],
-
- Name = result.Stacks[0].Name
+ Name = result[0].Name
};
SetIsoType(returnVideo);
@@ -516,15 +535,6 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
return returnVideo;
}
- private string[] ValidCollectionTypes = new[]
- {
- CollectionType.Movies,
- CollectionType.HomeVideos,
- CollectionType.MusicVideos,
- CollectionType.Movies,
- CollectionType.Photos
- };
-
private bool IsInvalid(Folder parent, string collectionType)
{
if (parent != null)
@@ -540,20 +550,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
return false;
}
- return !ValidCollectionTypes.Contains(collectionType, StringComparer.OrdinalIgnoreCase);
- }
-
- private IImageProcessor _imageProcessor;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="MovieResolver"/> class.
- /// </summary>
- /// <param name="libraryManager">The library manager.</param>
- /// <param name="imageProcessor">The image processor.</param>
- public MovieResolver(ILibraryManager libraryManager, IImageProcessor imageProcessor)
- : base(libraryManager)
- {
- _imageProcessor = imageProcessor;
+ return !_validCollectionTypes.Contains(collectionType, StringComparer.OrdinalIgnoreCase);
}
}
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/PhotoAlbumResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PhotoAlbumResolver.cs
index 4536b0aaa..3ac837057 100644
--- a/Emby.Server.Implementations/Library/Resolvers/PhotoAlbumResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/PhotoAlbumResolver.cs
@@ -63,13 +63,12 @@ namespace Emby.Server.Implementations.Library.Resolvers
{
if (!file.IsDirectory && PhotoResolver.IsImageFile(file.FullName, _imageProcessor))
{
- var libraryOptions = args.GetLibraryOptions();
var filename = file.Name;
var ownedByMedia = false;
foreach (var siblingFile in files)
{
- if (PhotoResolver.IsOwnedByMedia(_libraryManager, libraryOptions, siblingFile.FullName, filename))
+ if (PhotoResolver.IsOwnedByMedia(_libraryManager, siblingFile.FullName, filename))
{
ownedByMedia = true;
break;
diff --git a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs
index a71ae8250..bcfcee9c6 100644
--- a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs
@@ -7,7 +7,6 @@ using System.Linq;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
namespace Emby.Server.Implementations.Library.Resolvers
@@ -56,11 +55,10 @@ namespace Emby.Server.Implementations.Library.Resolvers
// Make sure the image doesn't belong to a video file
var files = args.DirectoryService.GetFiles(Path.GetDirectoryName(args.Path));
- var libraryOptions = args.GetLibraryOptions();
foreach (var file in files)
{
- if (IsOwnedByMedia(_libraryManager, libraryOptions, file.FullName, filename))
+ if (IsOwnedByMedia(_libraryManager, file.FullName, filename))
{
return null;
}
@@ -77,17 +75,17 @@ namespace Emby.Server.Implementations.Library.Resolvers
return null;
}
- internal static bool IsOwnedByMedia(ILibraryManager libraryManager, LibraryOptions libraryOptions, string file, string imageFilename)
+ internal static bool IsOwnedByMedia(ILibraryManager libraryManager, string file, string imageFilename)
{
- if (libraryManager.IsVideoFile(file, libraryOptions))
+ if (libraryManager.IsVideoFile(file))
{
- return IsOwnedByResolvedMedia(libraryManager, libraryOptions, file, imageFilename);
+ return IsOwnedByResolvedMedia(libraryManager, file, imageFilename);
}
return false;
}
- internal static bool IsOwnedByResolvedMedia(ILibraryManager libraryManager, LibraryOptions libraryOptions, string file, string imageFilename)
+ internal static bool IsOwnedByResolvedMedia(ILibraryManager libraryManager, string file, string imageFilename)
=> imageFilename.StartsWith(Path.GetFileNameWithoutExtension(file), StringComparison.OrdinalIgnoreCase);
internal static bool IsImageFile(string path, IImageProcessor imageProcessor)
diff --git a/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs
index a68562fc2..41561916f 100644
--- a/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs
@@ -5,60 +5,66 @@ using System.IO;
using System.Linq;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Playlists;
+using MediaBrowser.Controller.Resolvers;
+using MediaBrowser.LocalMetadata.Savers;
using MediaBrowser.Model.Entities;
namespace Emby.Server.Implementations.Library.Resolvers
{
+ /// <summary>
+ /// <see cref="IItemResolver"/> for <see cref="Playlist"/> library items.
+ /// </summary>
public class PlaylistResolver : FolderResolver<Playlist>
{
- private string[] SupportedCollectionTypes = new string[] {
-
+ private string[] _musicPlaylistCollectionTypes = new string[] {
string.Empty,
CollectionType.Music
};
- /// <summary>
- /// Resolves the specified args.
- /// </summary>
- /// <param name="args">The args.</param>
- /// <returns>BoxSet.</returns>
+ /// <inheritdoc/>
protected override Playlist Resolve(ItemResolveArgs args)
{
- // It's a boxset if all of the following conditions are met:
- // Is a Directory
- // Contains [playlist] in the path
if (args.IsDirectory)
{
- var filename = Path.GetFileName(args.Path);
-
- if (string.IsNullOrEmpty(filename))
+ // It's a boxset if the path is a directory with [playlist] in it's the name
+ // TODO: Should this use Path.GetDirectoryName() instead?
+ bool isBoxSet = Path.GetFileName(args.Path)
+ ?.Contains("[playlist]", StringComparison.OrdinalIgnoreCase)
+ ?? false;
+ if (isBoxSet)
{
- return null;
+ return new Playlist
+ {
+ Path = args.Path,
+ Name = Path.GetFileName(args.Path).Replace("[playlist]", string.Empty, StringComparison.OrdinalIgnoreCase).Trim()
+ };
}
- if (filename.IndexOf("[playlist]", StringComparison.OrdinalIgnoreCase) != -1)
+ // It's a directory-based playlist if the directory contains a playlist file
+ var filePaths = Directory.EnumerateFiles(args.Path);
+ if (filePaths.Any(f => f.EndsWith(PlaylistXmlSaver.DefaultPlaylistFilename, StringComparison.OrdinalIgnoreCase)))
{
return new Playlist
{
Path = args.Path,
- Name = Path.GetFileName(args.Path).Replace("[playlist]", string.Empty, StringComparison.OrdinalIgnoreCase).Trim()
+ Name = Path.GetFileName(args.Path)
};
}
}
- else
+
+ // Check if this is a music playlist file
+ // It should have the correct collection type and a supported file extension
+ else if (_musicPlaylistCollectionTypes.Contains(args.CollectionType ?? string.Empty, StringComparer.OrdinalIgnoreCase))
{
- if (SupportedCollectionTypes.Contains(args.CollectionType ?? string.Empty, StringComparer.OrdinalIgnoreCase))
+ var extension = Path.GetExtension(args.Path);
+ if (Playlist.SupportedExtensions.Contains(extension ?? string.Empty, StringComparer.OrdinalIgnoreCase))
{
- var extension = Path.GetExtension(args.Path);
- if (Playlist.SupportedExtensions.Contains(extension ?? string.Empty, StringComparer.OrdinalIgnoreCase))
+ return new Playlist
{
- return new Playlist
- {
- Path = args.Path,
- Name = Path.GetFileNameWithoutExtension(args.Path),
- IsInMixedFolder = true
- };
- }
+ Path = args.Path,
+ Name = Path.GetFileNameWithoutExtension(args.Path),
+ IsInMixedFolder = true
+ };
}
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
index 3b9e48d97..18145b7f1 100644
--- a/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
@@ -9,17 +9,12 @@ using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Library.Resolvers.TV
{
/// <summary>
- /// Class SeasonResolver
+ /// Class SeasonResolver.
/// </summary>
public class SeasonResolver : FolderResolver<Season>
{
- /// <summary>
- /// The _config
- /// </summary>
private readonly IServerConfigurationManager _config;
-
private readonly ILibraryManager _libraryManager;
- private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
private readonly ILocalizationManager _localization;
private readonly ILogger _logger;
@@ -30,7 +25,11 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
/// <param name="libraryManager">The library manager.</param>
/// <param name="localization">The localization</param>
/// <param name="logger">The logger</param>
- public SeasonResolver(IServerConfigurationManager config, ILibraryManager libraryManager, ILocalizationManager localization, ILogger logger)
+ public SeasonResolver(
+ IServerConfigurationManager config,
+ ILibraryManager libraryManager,
+ ILocalizationManager localization,
+ ILogger<SeasonResolver> logger)
{
_config = config;
_libraryManager = libraryManager;
@@ -45,14 +44,13 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
/// <returns>Season.</returns>
protected override Season Resolve(ItemResolveArgs args)
{
- if (args.Parent is Series && args.IsDirectory)
+ if (args.Parent is Series series && args.IsDirectory)
{
var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions();
- var series = ((Series)args.Parent);
var path = args.Path;
- var seasonParserResult = new SeasonPathParser().Parse(path, true, true);
+ var seasonParserResult = SeasonPathParser.Parse(path, true, true);
var season = new Season
{
@@ -74,7 +72,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
{
if (episodeInfo.EpisodeNumber.HasValue && episodeInfo.SeasonNumber.HasValue)
{
- _logger.LogDebug("Found folder underneath series with episode number: {0}. Season {1}. Episode {2}",
+ _logger.LogDebug(
+ "Found folder underneath series with episode number: {0}. Season {1}. Episode {2}",
path,
episodeInfo.SeasonNumber.Value,
episodeInfo.EpisodeNumber.Value);
@@ -90,7 +89,11 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
season.Name = seasonNumber == 0 ?
args.LibraryOptions.SeasonZeroDisplayName :
- string.Format(_localization.GetLocalizedString("NameSeasonNumber"), seasonNumber.ToString(UsCulture), args.GetLibraryOptions().PreferredMetadataLanguage);
+ string.Format(
+ CultureInfo.InvariantCulture,
+ _localization.GetLocalizedString("NameSeasonNumber"),
+ seasonNumber,
+ args.GetLibraryOptions().PreferredMetadataLanguage);
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
index 7cc9eabc8..c759e7115 100644
--- a/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
@@ -30,7 +30,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
/// <param name="fileSystem">The file system.</param>
/// <param name="logger">The logger.</param>
/// <param name="libraryManager">The library manager.</param>
- public SeriesResolver(IFileSystem fileSystem, ILogger logger, ILibraryManager libraryManager)
+ public SeriesResolver(IFileSystem fileSystem, ILogger<SeriesResolver> logger, ILibraryManager libraryManager)
{
_fileSystem = fileSystem;
_logger = logger;
@@ -101,7 +101,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
return null;
}
- if (IsSeriesFolder(args.Path, args.FileSystemChildren, args.DirectoryService, _fileSystem, _logger, _libraryManager, args.GetLibraryOptions(), false))
+ if (IsSeriesFolder(args.Path, args.FileSystemChildren, args.DirectoryService, _fileSystem, _logger, _libraryManager, false))
{
return new Series
{
@@ -122,24 +122,10 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
IFileSystem fileSystem,
ILogger logger,
ILibraryManager libraryManager,
- LibraryOptions libraryOptions,
bool isTvContentType)
{
foreach (var child in fileSystemChildren)
{
- //if ((attributes & FileAttributes.Hidden) == FileAttributes.Hidden)
- //{
- // //logger.LogDebug("Igoring series file or folder marked hidden: {0}", child.FullName);
- // continue;
- //}
-
- // Can't enforce this because files saved by Bitcasa are always marked System
- //if ((attributes & FileAttributes.System) == FileAttributes.System)
- //{
- // logger.LogDebug("Igoring series subfolder marked system: {0}", child.FullName);
- // continue;
- //}
-
if (child.IsDirectory)
{
if (IsSeasonFolder(child.FullName, isTvContentType, libraryManager))
@@ -151,7 +137,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
else
{
string fullName = child.FullName;
- if (libraryManager.IsVideoFile(fullName, libraryOptions))
+ if (libraryManager.IsVideoFile(fullName))
{
if (isTvContentType)
{
@@ -202,7 +188,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
/// <returns><c>true</c> if [is season folder] [the specified path]; otherwise, <c>false</c>.</returns>
private static bool IsSeasonFolder(string path, bool isTvContentType, ILibraryManager libraryManager)
{
- var seasonNumber = new SeasonPathParser().Parse(path, isTvContentType, isTvContentType).SeasonNumber;
+ var seasonNumber = SeasonPathParser.Parse(path, isTvContentType, isTvContentType).SeasonNumber;
return seasonNumber.HasValue;
}
diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs
index 1b9c317d8..25d733a65 100644
--- a/Emby.Server.Implementations/Library/UserManager.cs
+++ b/Emby.Server.Implementations/Library/UserManager.cs
@@ -290,10 +290,11 @@ namespace Emby.Server.Implementations.Library
&& authenticationProvider != null
&& !(authenticationProvider is DefaultAuthenticationProvider))
{
- // We should trust the user that the authprovider says, not what was typed
+ // Trust the username returned by the authentication provider
username = updatedUsername;
- // Search the database for the user again; the authprovider might have created it
+ // Search the database for the user again
+ // the authentication provider might have created it
user = Users
.FirstOrDefault(i => string.Equals(username, i.Name, StringComparison.OrdinalIgnoreCase));
@@ -485,7 +486,7 @@ namespace Emby.Server.Implementations.Library
var hash = _cryptoProvider.ComputeHash(
passwordHash.Id,
Encoding.UTF8.GetBytes(password),
- passwordHash.Salt);
+ passwordHash.Salt.ToArray());
success = passwordHash.Hash.SequenceEqual(hash);
}
@@ -666,7 +667,7 @@ namespace Emby.Server.Implementations.Library
throw new ArgumentException("Invalid username", nameof(newName));
}
- if (user.Name.Equals(newName, StringComparison.OrdinalIgnoreCase))
+ if (user.Name.Equals(newName, StringComparison.Ordinal))
{
throw new ArgumentException("The new and old names must be different.");
}
diff --git a/Emby.Server.Implementations/Library/Validators/ArtistsPostScanTask.cs b/Emby.Server.Implementations/Library/Validators/ArtistsPostScanTask.cs
index 61a07d0d6..2af8ff5cb 100644
--- a/Emby.Server.Implementations/Library/Validators/ArtistsPostScanTask.cs
+++ b/Emby.Server.Implementations/Library/Validators/ArtistsPostScanTask.cs
@@ -25,7 +25,10 @@ namespace Emby.Server.Implementations.Library.Validators
/// <param name="libraryManager">The library manager.</param>
/// <param name="logger">The logger.</param>
/// <param name="itemRepo">The item repository.</param>
- public ArtistsPostScanTask(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo)
+ public ArtistsPostScanTask(
+ ILibraryManager libraryManager,
+ ILogger<ArtistsPostScanTask> logger,
+ IItemRepository itemRepo)
{
_libraryManager = libraryManager;
_logger = logger;
diff --git a/Emby.Server.Implementations/Library/Validators/GenresPostScanTask.cs b/Emby.Server.Implementations/Library/Validators/GenresPostScanTask.cs
index 06d1dd89d..251785dfd 100644
--- a/Emby.Server.Implementations/Library/Validators/GenresPostScanTask.cs
+++ b/Emby.Server.Implementations/Library/Validators/GenresPostScanTask.cs
@@ -25,7 +25,10 @@ namespace Emby.Server.Implementations.Library.Validators
/// <param name="libraryManager">The library manager.</param>
/// <param name="logger">The logger.</param>
/// <param name="itemRepo">The item repository.</param>
- public GenresPostScanTask(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo)
+ public GenresPostScanTask(
+ ILibraryManager libraryManager,
+ ILogger<GenresPostScanTask> logger,
+ IItemRepository itemRepo)
{
_libraryManager = libraryManager;
_logger = logger;
diff --git a/Emby.Server.Implementations/Library/Validators/MusicGenresPostScanTask.cs b/Emby.Server.Implementations/Library/Validators/MusicGenresPostScanTask.cs
index 58549e9d7..9d8690116 100644
--- a/Emby.Server.Implementations/Library/Validators/MusicGenresPostScanTask.cs
+++ b/Emby.Server.Implementations/Library/Validators/MusicGenresPostScanTask.cs
@@ -25,7 +25,10 @@ namespace Emby.Server.Implementations.Library.Validators
/// <param name="libraryManager">The library manager.</param>
/// <param name="logger">The logger.</param>
/// <param name="itemRepo">The item repository.</param>
- public MusicGenresPostScanTask(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo)
+ public MusicGenresPostScanTask(
+ ILibraryManager libraryManager,
+ ILogger<MusicGenresPostScanTask> logger,
+ IItemRepository itemRepo)
{
_libraryManager = libraryManager;
_logger = logger;
diff --git a/Emby.Server.Implementations/Library/Validators/StudiosPostScanTask.cs b/Emby.Server.Implementations/Library/Validators/StudiosPostScanTask.cs
index 00899c336..2f8f906b9 100644
--- a/Emby.Server.Implementations/Library/Validators/StudiosPostScanTask.cs
+++ b/Emby.Server.Implementations/Library/Validators/StudiosPostScanTask.cs
@@ -26,7 +26,10 @@ namespace Emby.Server.Implementations.Library.Validators
/// <param name="libraryManager">The library manager.</param>
/// <param name="logger">The logger.</param>
/// <param name="itemRepo">The item repository.</param>
- public StudiosPostScanTask(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo)
+ public StudiosPostScanTask(
+ ILibraryManager libraryManager,
+ ILogger<StudiosPostScanTask> logger,
+ IItemRepository itemRepo)
{
_libraryManager = libraryManager;
_logger = logger;
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs
index 84e8c31f9..9c4f5fe3d 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs
@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
using System;
using System.IO;
using System.Net.Http;
@@ -15,14 +18,12 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
private readonly ILogger _logger;
private readonly IHttpClient _httpClient;
- private readonly IFileSystem _fileSystem;
private readonly IStreamHelper _streamHelper;
- public DirectRecorder(ILogger logger, IHttpClient httpClient, IFileSystem fileSystem, IStreamHelper streamHelper)
+ public DirectRecorder(ILogger logger, IHttpClient httpClient, IStreamHelper streamHelper)
{
_logger = logger;
_httpClient = httpClient;
- _fileSystem = fileSystem;
_streamHelper = streamHelper;
}
@@ -45,7 +46,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
Directory.CreateDirectory(Path.GetDirectoryName(targetFile));
- using (var output = _fileSystem.GetFileStream(targetFile, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
+ using (var output = new FileStream(targetFile, FileMode.Create, FileAccess.Write, FileShare.Read))
{
onStarted();
@@ -81,7 +82,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
Directory.CreateDirectory(Path.GetDirectoryName(targetFile));
- using (var output = _fileSystem.GetFileStream(targetFile, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
+ using (var output = new FileStream(targetFile, FileMode.Create, FileAccess.Write, FileShare.Read))
{
onStarted();
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
index 3b6bfce6a..139aa19a4 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@@ -27,7 +29,6 @@ using MediaBrowser.Model.Diagnostics;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Events;
-using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.MediaInfo;
@@ -40,6 +41,10 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
public class EmbyTV : ILiveTvService, ISupportsDirectStreamProvider, ISupportsNewTimerIds, IDisposable
{
+ public const string DateAddedFormat = "yyyy-MM-dd HH:mm:ss";
+
+ private const int TunerDiscoveryDurationMs = 3000;
+
private readonly IServerApplicationHost _appHost;
private readonly ILogger _logger;
private readonly IHttpClient _httpClient;
@@ -57,22 +62,24 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
private readonly IProviderManager _providerManager;
private readonly IMediaEncoder _mediaEncoder;
private readonly IProcessFactory _processFactory;
- private IMediaSourceManager _mediaSourceManager;
-
- public static EmbyTV Current;
-
- public event EventHandler<GenericEventArgs<TimerInfo>> TimerCreated;
- public event EventHandler<GenericEventArgs<string>> TimerCancelled;
+ private readonly IMediaSourceManager _mediaSourceManager;
+ private readonly IStreamHelper _streamHelper;
private readonly ConcurrentDictionary<string, ActiveRecordingInfo> _activeRecordings =
new ConcurrentDictionary<string, ActiveRecordingInfo>(StringComparer.OrdinalIgnoreCase);
- private readonly IStreamHelper _streamHelper;
+ private readonly ConcurrentDictionary<string, EpgChannelData> _epgChannels =
+ new ConcurrentDictionary<string, EpgChannelData>(StringComparer.OrdinalIgnoreCase);
+
+ private readonly SemaphoreSlim _recordingDeleteSemaphore = new SemaphoreSlim(1, 1);
- public EmbyTV(IServerApplicationHost appHost,
+ private bool _disposed = false;
+
+ public EmbyTV(
+ IServerApplicationHost appHost,
IStreamHelper streamHelper,
IMediaSourceManager mediaSourceManager,
- ILogger logger,
+ ILogger<EmbyTV> logger,
IJsonSerializer jsonSerializer,
IHttpClient httpClient,
IServerConfigurationManager config,
@@ -103,12 +110,40 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
_seriesTimerProvider = new SeriesTimerManager(jsonSerializer, _logger, Path.Combine(DataPath, "seriestimers.json"));
_timerProvider = new TimerManager(jsonSerializer, _logger, Path.Combine(DataPath, "timers.json"));
- _timerProvider.TimerFired += _timerProvider_TimerFired;
+ _timerProvider.TimerFired += OnTimerProviderTimerFired;
- _config.NamedConfigurationUpdated += _config_NamedConfigurationUpdated;
+ _config.NamedConfigurationUpdated += OnNamedConfigurationUpdated;
}
- private void _config_NamedConfigurationUpdated(object sender, ConfigurationUpdateEventArgs e)
+ public event EventHandler<GenericEventArgs<TimerInfo>> TimerCreated;
+
+ public event EventHandler<GenericEventArgs<string>> TimerCancelled;
+
+ public static EmbyTV Current { get; private set; }
+
+ /// <inheritdoc />
+ public string Name => "Emby";
+
+ public string DataPath => Path.Combine(_config.CommonApplicationPaths.DataPath, "livetv");
+
+ /// <inheritdoc />
+ public string HomePageUrl => "https://github.com/jellyfin/jellyfin";
+
+ private string DefaultRecordingPath => Path.Combine(DataPath, "recordings");
+
+ private string RecordingPath
+ {
+ get
+ {
+ var path = GetConfiguration().RecordingPath;
+
+ return string.IsNullOrWhiteSpace(path)
+ ? DefaultRecordingPath
+ : path;
+ }
+ }
+
+ private void OnNamedConfigurationUpdated(object sender, ConfigurationUpdateEventArgs e)
{
if (string.Equals(e.Key, "livetv", StringComparison.OrdinalIgnoreCase))
{
@@ -116,11 +151,11 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
}
- public async Task Start()
+ public Task Start()
{
_timerProvider.RestartTimers();
- await CreateRecordingFolders().ConfigureAwait(false);
+ return CreateRecordingFolders();
}
private async void OnRecordingFoldersChanged()
@@ -132,8 +167,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
try
{
- var recordingFolders = GetRecordingFolders();
-
+ var recordingFolders = GetRecordingFolders().ToArray();
var virtualFolders = _libraryManager.GetVirtualFolders()
.ToList();
@@ -241,26 +275,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
}
- public string Name => "Emby";
-
- public string DataPath => Path.Combine(_config.CommonApplicationPaths.DataPath, "livetv");
-
- private string DefaultRecordingPath => Path.Combine(DataPath, "recordings");
-
- private string RecordingPath
- {
- get
- {
- var path = GetConfiguration().RecordingPath;
-
- return string.IsNullOrWhiteSpace(path)
- ? DefaultRecordingPath
- : path;
- }
- }
-
- public string HomePageUrl => "https://github.com/jellyfin/jellyfin";
-
public async Task RefreshSeriesTimers(CancellationToken cancellationToken)
{
var seriesTimers = await GetSeriesTimersAsync(cancellationToken).ConfigureAwait(false);
@@ -339,7 +353,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
catch (NotSupportedException)
{
-
}
catch (Exception ex)
{
@@ -351,7 +364,12 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return list;
}
- private async Task AddMetadata(IListingsProvider provider, ListingsProviderInfo info, List<ChannelInfo> tunerChannels, bool enableCache, CancellationToken cancellationToken)
+ private async Task AddMetadata(
+ IListingsProvider provider,
+ ListingsProviderInfo info,
+ IEnumerable<ChannelInfo> tunerChannels,
+ bool enableCache,
+ CancellationToken cancellationToken)
{
var epgChannels = await GetEpgChannels(provider, info, enableCache, cancellationToken).ConfigureAwait(false);
@@ -363,8 +381,9 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
if (!string.IsNullOrWhiteSpace(epgChannel.Name))
{
- //tunerChannel.Name = epgChannel.Name;
+ // tunerChannel.Name = epgChannel.Name;
}
+
if (!string.IsNullOrWhiteSpace(epgChannel.ImageUrl))
{
tunerChannel.ImageUrl = epgChannel.ImageUrl;
@@ -373,10 +392,11 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
}
- private readonly ConcurrentDictionary<string, EpgChannelData> _epgChannels =
- new ConcurrentDictionary<string, EpgChannelData>(StringComparer.OrdinalIgnoreCase);
-
- private async Task<EpgChannelData> GetEpgChannels(IListingsProvider provider, ListingsProviderInfo info, bool enableCache, CancellationToken cancellationToken)
+ private async Task<EpgChannelData> GetEpgChannels(
+ IListingsProvider provider,
+ ListingsProviderInfo info,
+ bool enableCache,
+ CancellationToken cancellationToken)
{
if (!enableCache || !_epgChannels.TryGetValue(info.Id, out var result))
{
@@ -394,59 +414,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return result;
}
- private class EpgChannelData
- {
- public EpgChannelData(List<ChannelInfo> channels)
- {
- ChannelsById = new Dictionary<string, ChannelInfo>(StringComparer.OrdinalIgnoreCase);
- ChannelsByNumber = new Dictionary<string, ChannelInfo>(StringComparer.OrdinalIgnoreCase);
- ChannelsByName = new Dictionary<string, ChannelInfo>(StringComparer.OrdinalIgnoreCase);
-
- foreach (var channel in channels)
- {
- ChannelsById[channel.Id] = channel;
-
- if (!string.IsNullOrEmpty(channel.Number))
- {
- ChannelsByNumber[channel.Number] = channel;
- }
-
- var normalizedName = NormalizeName(channel.Name ?? string.Empty);
- if (!string.IsNullOrWhiteSpace(normalizedName))
- {
- ChannelsByName[normalizedName] = channel;
- }
- }
- }
-
- private Dictionary<string, ChannelInfo> ChannelsById { get; set; }
- private Dictionary<string, ChannelInfo> ChannelsByNumber { get; set; }
- private Dictionary<string, ChannelInfo> ChannelsByName { get; set; }
-
- public ChannelInfo GetChannelById(string id)
- {
- ChannelInfo result = null;
-
- ChannelsById.TryGetValue(id, out result);
-
- return result;
- }
-
- public ChannelInfo GetChannelByNumber(string number)
- {
- ChannelsByNumber.TryGetValue(number, out var result);
-
- return result;
- }
-
- public ChannelInfo GetChannelByName(string name)
- {
- ChannelsByName.TryGetValue(name, out var result);
-
- return result;
- }
- }
-
private async Task<ChannelInfo> GetEpgChannelFromTunerChannel(IListingsProvider provider, ListingsProviderInfo info, ChannelInfo tunerChannel, CancellationToken cancellationToken)
{
var epgChannels = await GetEpgChannels(provider, info, true, cancellationToken).ConfigureAwait(false);
@@ -458,11 +425,12 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
foreach (NameValuePair mapping in mappings)
{
- if (StringHelper.EqualsIgnoreCase(mapping.Name, channelId))
+ if (string.Equals(mapping.Name, channelId, StringComparison.OrdinalIgnoreCase))
{
return mapping.Value;
}
}
+
return channelId;
}
@@ -476,7 +444,10 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return GetEpgChannelFromTunerChannel(info.ChannelMappings, tunerChannel, epgChannels);
}
- private ChannelInfo GetEpgChannelFromTunerChannel(NameValuePair[] mappings, ChannelInfo tunerChannel, EpgChannelData epgChannelData)
+ private ChannelInfo GetEpgChannelFromTunerChannel(
+ NameValuePair[] mappings,
+ ChannelInfo tunerChannel,
+ EpgChannelData epgChannelData)
{
if (!string.IsNullOrWhiteSpace(tunerChannel.Id))
{
@@ -537,7 +508,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
if (!string.IsNullOrWhiteSpace(tunerChannel.Name))
{
- var normalizedName = NormalizeName(tunerChannel.Name);
+ var normalizedName = EpgChannelData.NormalizeName(tunerChannel.Name);
var channel = epgChannelData.GetChannelByName(normalizedName);
@@ -550,11 +521,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return null;
}
- private static string NormalizeName(string value)
- {
- return value.Replace(" ", string.Empty).Replace("-", string.Empty);
- }
-
public async Task<List<ChannelInfo>> GetChannelsForListingsProvider(ListingsProviderInfo listingsProvider, CancellationToken cancellationToken)
{
var list = new List<ChannelInfo>();
@@ -600,6 +566,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
_seriesTimerProvider.Delete(remove);
}
+
return Task.CompletedTask;
}
@@ -689,6 +656,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
programInfo = GetProgramInfoFromCache(timer);
}
+
if (programInfo == null)
{
_logger.LogInformation("Unable to find program with Id {0}. Will search using start date", timer.ProgramId);
@@ -703,10 +671,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
timer.IsManual = true;
_timerProvider.Add(timer);
- if (TimerCreated != null)
- {
- TimerCreated(this, new GenericEventArgs<TimerInfo>(timer));
- }
+ TimerCreated?.Invoke(this, new GenericEventArgs<TimerInfo>(timer));
return Task.FromResult(timer.Id);
}
@@ -800,7 +765,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
// Only update if not currently active
- if (!_activeRecordings.TryGetValue(updatedTimer.Id, out var activeRecordingInfo))
+ if (!_activeRecordings.TryGetValue(updatedTimer.Id, out _))
{
existingTimer.PrePaddingSeconds = updatedTimer.PrePaddingSeconds;
existingTimer.PostPaddingSeconds = updatedTimer.PostPaddingSeconds;
@@ -846,6 +811,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
return info.Path;
}
+
return null;
}
@@ -870,9 +836,11 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
return null;
}
+
return recording;
}
}
+
return null;
}
@@ -1061,13 +1029,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
mediaSource.Id = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture) + "_" + mediaSource.Id;
- //if (mediaSource.DateLiveStreamOpened.HasValue && enableStreamSharing)
- //{
- // var ticks = (DateTime.UtcNow - mediaSource.DateLiveStreamOpened.Value).Ticks - TimeSpan.FromSeconds(10).Ticks;
- // ticks = Math.Max(0, ticks);
- // mediaSource.Path += "?t=" + ticks.ToString(CultureInfo.InvariantCulture) + "&s=" + mediaSource.DateLiveStreamOpened.Value.Ticks.ToString(CultureInfo.InvariantCulture);
- //}
-
return mediaSource;
}
@@ -1091,7 +1052,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
catch (NotImplementedException)
{
-
}
}
@@ -1142,7 +1102,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return Task.CompletedTask;
}
- async void _timerProvider_TimerFired(object sender, GenericEventArgs<TimerInfo> e)
+ private async void OnTimerProviderTimerFired(object sender, GenericEventArgs<TimerInfo> e)
{
var timer = e.Argument;
@@ -1177,7 +1137,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
catch (OperationCanceledException)
{
-
}
catch (Exception ex)
{
@@ -1221,7 +1180,10 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
if (timer.SeasonNumber.HasValue)
{
- folderName = string.Format("Season {0}", timer.SeasonNumber.Value.ToString(CultureInfo.InvariantCulture));
+ folderName = string.Format(
+ CultureInfo.InvariantCulture,
+ "Season {0}",
+ timer.SeasonNumber.Value);
recordPath = Path.Combine(recordPath, folderName);
}
}
@@ -1275,6 +1237,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
recordPath = Path.Combine(recordPath, "Sports");
}
+
recordPath = Path.Combine(recordPath, _fileSystem.GetValidFilename(timer.Name).Trim());
}
else
@@ -1283,6 +1246,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
recordPath = Path.Combine(recordPath, "Other");
}
+
recordPath = Path.Combine(recordPath, _fileSystem.GetValidFilename(timer.Name).Trim());
}
@@ -1304,6 +1268,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
programInfo = GetProgramInfoFromCache(timer);
}
+
if (programInfo == null)
{
_logger.LogInformation("Unable to find program with Id {0}. Will search using start date", timer.ProgramId);
@@ -1315,9 +1280,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
CopyProgramInfoToTimerInfo(programInfo, timer);
}
- string seriesPath = null;
var remoteMetadata = await FetchInternetMetadata(timer, CancellationToken.None).ConfigureAwait(false);
- var recordPath = GetRecordingPath(timer, remoteMetadata, out seriesPath);
+ var recordPath = GetRecordingPath(timer, remoteMetadata, out string seriesPath);
var recordingStatus = RecordingStatus.New;
string liveStreamId = null;
@@ -1326,19 +1290,20 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
try
{
- var allMediaSources = await _mediaSourceManager.GetPlayackMediaSources(channelItem, null, true, false, CancellationToken.None).ConfigureAwait(false);
+ var allMediaSources = await _mediaSourceManager.GetPlaybackMediaSources(channelItem, null, true, false, CancellationToken.None).ConfigureAwait(false);
var mediaStreamInfo = allMediaSources[0];
IDirectStreamProvider directStreamProvider = null;
if (mediaStreamInfo.RequiresOpening)
{
- var liveStreamResponse = await _mediaSourceManager.OpenLiveStreamInternal(new LiveStreamRequest
- {
- ItemId = channelItem.Id,
- OpenToken = mediaStreamInfo.OpenToken
-
- }, CancellationToken.None).ConfigureAwait(false);
+ var liveStreamResponse = await _mediaSourceManager.OpenLiveStreamInternal(
+ new LiveStreamRequest
+ {
+ ItemId = channelItem.Id,
+ OpenToken = mediaStreamInfo.OpenToken
+ },
+ CancellationToken.None).ConfigureAwait(false);
mediaStreamInfo = liveStreamResponse.Item1.MediaSource;
liveStreamId = mediaStreamInfo.LiveStreamId;
@@ -1412,12 +1377,12 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
if (recordingStatus != RecordingStatus.Completed && DateTime.UtcNow < timer.EndDate && timer.RetryCount < 10)
{
- const int retryIntervalSeconds = 60;
- _logger.LogInformation("Retrying recording in {0} seconds.", retryIntervalSeconds);
+ const int RetryIntervalSeconds = 60;
+ _logger.LogInformation("Retrying recording in {0} seconds.", RetryIntervalSeconds);
timer.Status = RecordingStatus.New;
timer.PrePaddingSeconds = 0;
- timer.StartDate = DateTime.UtcNow.AddSeconds(retryIntervalSeconds);
+ timer.StartDate = DateTime.UtcNow.AddSeconds(RetryIntervalSeconds);
timer.RetryCount++;
_timerProvider.AddOrUpdate(timer);
}
@@ -1538,6 +1503,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
return;
}
+
if (string.IsNullOrWhiteSpace(seriesPath))
{
return;
@@ -1576,21 +1542,21 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
DeleteLibraryItemsForTimers(timersToDelete);
var librarySeries = _libraryManager.FindByPath(seriesPath, true) as Folder;
-
if (librarySeries == null)
{
return;
}
- var episodesToDelete = librarySeries.GetItemList(new InternalItemsQuery
- {
- OrderBy = new[] { (ItemSortBy.DateCreated, SortOrder.Descending) },
- IsVirtualItem = false,
- IsFolder = false,
- Recursive = true,
- DtoOptions = new DtoOptions(true)
+ var episodesToDelete = librarySeries.GetItemList(
+ new InternalItemsQuery
+ {
+ OrderBy = new[] { (ItemSortBy.DateCreated, SortOrder.Descending) },
+ IsVirtualItem = false,
+ IsFolder = false,
+ Recursive = true,
+ DtoOptions = new DtoOptions(true)
- })
+ })
.Where(i => i.IsFileProtocol && File.Exists(i.Path))
.Skip(seriesTimer.KeepUpTo - 1)
.ToList();
@@ -1599,11 +1565,13 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
try
{
- _libraryManager.DeleteItem(item, new DeleteOptions
- {
- DeleteFileLocation = true
-
- }, true);
+ _libraryManager.DeleteItem(
+ item,
+ new DeleteOptions
+ {
+ DeleteFileLocation = true
+ },
+ true);
}
catch (Exception ex)
{
@@ -1617,7 +1585,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
}
- private readonly SemaphoreSlim _recordingDeleteSemaphore = new SemaphoreSlim(1, 1);
private void DeleteLibraryItemsForTimers(List<TimerInfo> timers)
{
foreach (var timer in timers)
@@ -1644,22 +1611,17 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
if (libraryItem != null)
{
- _libraryManager.DeleteItem(libraryItem, new DeleteOptions
- {
- DeleteFileLocation = true
-
- }, true);
+ _libraryManager.DeleteItem(
+ libraryItem,
+ new DeleteOptions
+ {
+ DeleteFileLocation = true
+ },
+ true);
}
- else
+ else if (File.Exists(timer.RecordingPath))
{
- try
- {
- _fileSystem.DeleteFile(timer.RecordingPath);
- }
- catch (IOException)
- {
-
- }
+ _fileSystem.DeleteFile(timer.RecordingPath);
}
_timerProvider.Delete(timer);
@@ -1690,26 +1652,20 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return true;
}
- var hasRecordingAtPath = _activeRecordings
+ return _activeRecordings
.Values
.ToList()
.Any(i => string.Equals(i.Path, path, StringComparison.OrdinalIgnoreCase) && !string.Equals(i.Timer.Id, timerId, StringComparison.OrdinalIgnoreCase));
-
- if (hasRecordingAtPath)
- {
- return true;
- }
- return false;
}
private IRecorder GetRecorder(MediaSourceInfo mediaSource)
{
if (mediaSource.RequiresLooping || !(mediaSource.Container ?? string.Empty).EndsWith("ts", StringComparison.OrdinalIgnoreCase) || (mediaSource.Protocol != MediaProtocol.File && mediaSource.Protocol != MediaProtocol.Http))
{
- return new EncodedRecorder(_logger, _fileSystem, _mediaEncoder, _config.ApplicationPaths, _jsonSerializer, _processFactory, _config);
+ return new EncodedRecorder(_logger, _mediaEncoder, _config.ApplicationPaths, _jsonSerializer, _processFactory, _config);
}
- return new DirectRecorder(_logger, _httpClient, _fileSystem, _streamHelper);
+ return new DirectRecorder(_logger, _httpClient, _streamHelper);
}
private void OnSuccessfulRecording(TimerInfo timer, string path)
@@ -1756,17 +1712,12 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
private void Process_Exited(object sender, EventArgs e)
{
- var process = (IProcess)sender;
- try
+ using (var process = (IProcess)sender)
{
_logger.LogInformation("Recording post-processing script completed with exit code {ExitCode}", process.ExitCode);
- }
- catch
- {
+ process.Dispose();
}
-
- process.Dispose();
}
private async Task SaveRecordingImage(string recordingPath, LiveTvProgram program, ItemImageInfo image)
@@ -1776,44 +1727,16 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
image = await _libraryManager.ConvertImageToLocal(program, image, 0).ConfigureAwait(false);
}
- string imageSaveFilenameWithoutExtension = null;
-
- switch (image.Type)
+ string imageSaveFilenameWithoutExtension = image.Type switch
{
- case ImageType.Primary:
-
- if (program.IsSeries)
- {
- imageSaveFilenameWithoutExtension = Path.GetFileNameWithoutExtension(recordingPath) + "-thumb";
- }
- else
- {
- imageSaveFilenameWithoutExtension = "poster";
- }
-
- break;
- case ImageType.Logo:
- imageSaveFilenameWithoutExtension = "logo";
- break;
- case ImageType.Thumb:
- if (program.IsSeries)
- {
- imageSaveFilenameWithoutExtension = Path.GetFileNameWithoutExtension(recordingPath) + "-thumb";
- }
- else
- {
- imageSaveFilenameWithoutExtension = "landscape";
- }
-
- break;
- case ImageType.Backdrop:
- imageSaveFilenameWithoutExtension = "fanart";
- break;
- default:
- break;
- }
+ ImageType.Primary => program.IsSeries ? Path.GetFileNameWithoutExtension(recordingPath) + "-thumb" : "poster",
+ ImageType.Logo => "logo",
+ ImageType.Thumb => program.IsSeries ? Path.GetFileNameWithoutExtension(recordingPath) + "-thumb" : "landscape",
+ ImageType.Backdrop => "fanart",
+ _ => null
+ };
- if (string.IsNullOrWhiteSpace(imageSaveFilenameWithoutExtension))
+ if (imageSaveFilenameWithoutExtension == null)
{
return;
}
@@ -1897,7 +1820,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
Limit = 1,
ExternalId = timer.ProgramId,
DtoOptions = new DtoOptions(true)
-
}).FirstOrDefault() as LiveTvProgram;
// dummy this up
@@ -1921,11 +1843,13 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
program.AddGenre("Sports");
}
+
if (timer.IsKids)
{
program.AddGenre("Kids");
program.AddGenre("Children");
}
+
if (timer.IsNews)
{
program.AddGenre("News");
@@ -1962,7 +1886,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return;
}
- using (var stream = _fileSystem.GetFileStream(nfoPath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
+ using (var stream = new FileStream(nfoPath, FileMode.Create, FileAccess.Write, FileShare.Read))
{
var settings = new XmlWriterSettings
{
@@ -1980,14 +1904,17 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
writer.WriteElementString("id", id);
}
+
if (timer.SeriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out id))
{
writer.WriteElementString("imdb_id", id);
}
+
if (timer.SeriesProviderIds.TryGetValue(MetadataProviders.Tmdb.ToString(), out id))
{
writer.WriteElementString("tmdbid", id);
}
+
if (timer.SeriesProviderIds.TryGetValue(MetadataProviders.Zap2It.ToString(), out id))
{
writer.WriteElementString("zap2itid", id);
@@ -2014,7 +1941,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
}
- public const string DateAddedFormat = "yyyy-MM-dd HH:mm:ss";
private void SaveVideoNfo(TimerInfo timer, string recordingPath, BaseItem item, bool lockData)
{
var nfoPath = Path.ChangeExtension(recordingPath, ".nfo");
@@ -2024,7 +1950,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return;
}
- using (var stream = _fileSystem.GetFileStream(nfoPath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
+ using (var stream = new FileStream(nfoPath, FileMode.Create, FileAccess.Write, FileShare.Read))
{
var settings = new XmlWriterSettings
{
@@ -2056,7 +1982,9 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
var formatString = options.ReleaseDateFormat;
- writer.WriteElementString("aired", premiereDate.Value.ToLocalTime().ToString(formatString));
+ writer.WriteElementString(
+ "aired",
+ premiereDate.Value.ToLocalTime().ToString(formatString, CultureInfo.InvariantCulture));
}
if (item.IndexNumber.HasValue)
@@ -2087,12 +2015,18 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
var formatString = options.ReleaseDateFormat;
- writer.WriteElementString("premiered", item.PremiereDate.Value.ToLocalTime().ToString(formatString));
- writer.WriteElementString("releasedate", item.PremiereDate.Value.ToLocalTime().ToString(formatString));
+ writer.WriteElementString(
+ "premiered",
+ item.PremiereDate.Value.ToLocalTime().ToString(formatString, CultureInfo.InvariantCulture));
+ writer.WriteElementString(
+ "releasedate",
+ item.PremiereDate.Value.ToLocalTime().ToString(formatString, CultureInfo.InvariantCulture));
}
}
- writer.WriteElementString("dateadded", DateTime.UtcNow.ToLocalTime().ToString(DateAddedFormat));
+ writer.WriteElementString(
+ "dateadded",
+ DateTime.UtcNow.ToLocalTime().ToString(DateAddedFormat, CultureInfo.InvariantCulture));
if (item.ProductionYear.HasValue)
{
@@ -2106,7 +2040,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
var overview = (item.Overview ?? string.Empty)
.StripHtml()
- .Replace("&quot;", "'");
+ .Replace("&quot;", "'", StringComparison.Ordinal);
writer.WriteElementString("plot", overview);
@@ -2214,17 +2148,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
private static bool IsPersonType(PersonInfo person, string type)
- {
- return string.Equals(person.Type, type, StringComparison.OrdinalIgnoreCase) || string.Equals(person.Role, type, StringComparison.OrdinalIgnoreCase);
- }
-
- private void AddGenre(List<string> genres, string genre)
- {
- if (!genres.Contains(genre, StringComparer.OrdinalIgnoreCase))
- {
- genres.Add(genre);
- }
- }
+ => string.Equals(person.Type, type, StringComparison.OrdinalIgnoreCase)
+ || string.Equals(person.Role, type, StringComparison.OrdinalIgnoreCase);
private LiveTvProgram GetProgramInfoFromCache(string programId)
{
@@ -2283,25 +2208,19 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return false;
}
- if (!seriesTimer.RecordAnyTime)
+ if (!seriesTimer.RecordAnyTime
+ && Math.Abs(seriesTimer.StartDate.TimeOfDay.Ticks - timer.StartDate.TimeOfDay.Ticks) >= TimeSpan.FromMinutes(10).Ticks)
{
- if (Math.Abs(seriesTimer.StartDate.TimeOfDay.Ticks - timer.StartDate.TimeOfDay.Ticks) >= TimeSpan.FromMinutes(10).Ticks)
- {
- return true;
- }
+ return true;
}
- //if (!seriesTimer.Days.Contains(timer.StartDate.ToLocalTime().DayOfWeek))
- //{
- // return true;
- //}
-
if (seriesTimer.RecordNewOnly && timer.IsRepeat)
{
return true;
}
- if (!seriesTimer.RecordAnyChannel && !string.Equals(timer.ChannelId, seriesTimer.ChannelId, StringComparison.OrdinalIgnoreCase))
+ if (!seriesTimer.RecordAnyChannel
+ && !string.Equals(timer.ChannelId, seriesTimer.ChannelId, StringComparison.OrdinalIgnoreCase))
{
return true;
}
@@ -2346,7 +2265,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
var allTimers = GetTimersForSeries(seriesTimer).ToList();
-
var enabledTimersForSeries = new List<TimerInfo>();
foreach (var timer in allTimers)
{
@@ -2369,10 +2287,12 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
enabledTimersForSeries.Add(timer);
}
+
_timerProvider.Add(timer);
TimerCreated?.Invoke(this, new GenericEventArgs<TimerInfo>(timer));
}
+
// Only update if not currently active - test both new timer and existing in case Id's are different
// Id's could be different if the timer was created manually prior to series timer creation
else if (!_activeRecordings.TryGetValue(timer.Id, out _) && !_activeRecordings.TryGetValue(existingTimer.Id, out _))
@@ -2508,13 +2428,13 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
if (!tempChannelCache.TryGetValue(parent.ChannelId, out LiveTvChannel channel))
{
- channel = _libraryManager.GetItemList(new InternalItemsQuery
- {
- IncludeItemTypes = new string[] { typeof(LiveTvChannel).Name },
- ItemIds = new[] { parent.ChannelId },
- DtoOptions = new DtoOptions()
-
- }).Cast<LiveTvChannel>().FirstOrDefault();
+ channel = _libraryManager.GetItemList(
+ new InternalItemsQuery
+ {
+ IncludeItemTypes = new string[] { typeof(LiveTvChannel).Name },
+ ItemIds = new[] { parent.ChannelId },
+ DtoOptions = new DtoOptions()
+ }).FirstOrDefault() as LiveTvChannel;
if (channel != null && !string.IsNullOrWhiteSpace(channel.ExternalId))
{
@@ -2567,13 +2487,13 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
if (!tempChannelCache.TryGetValue(programInfo.ChannelId, out LiveTvChannel channel))
{
- channel = _libraryManager.GetItemList(new InternalItemsQuery
- {
- IncludeItemTypes = new string[] { typeof(LiveTvChannel).Name },
- ItemIds = new[] { programInfo.ChannelId },
- DtoOptions = new DtoOptions()
-
- }).Cast<LiveTvChannel>().FirstOrDefault();
+ channel = _libraryManager.GetItemList(
+ new InternalItemsQuery
+ {
+ IncludeItemTypes = new string[] { typeof(LiveTvChannel).Name },
+ ItemIds = new[] { programInfo.ChannelId },
+ DtoOptions = new DtoOptions()
+ }).FirstOrDefault() as LiveTvChannel;
if (channel != null && !string.IsNullOrWhiteSpace(channel.ExternalId))
{
@@ -2618,10 +2538,10 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
foreach (var providerId in timerInfo.ProviderIds)
{
- var srch = "Series";
- if (providerId.Key.StartsWith(srch, StringComparison.OrdinalIgnoreCase))
+ const string Search = "Series";
+ if (providerId.Key.StartsWith(Search, StringComparison.OrdinalIgnoreCase))
{
- seriesProviderIds[providerId.Key.Substring(srch.Length)] = providerId.Value;
+ seriesProviderIds[providerId.Key.Substring(Search.Length)] = providerId.Value;
}
}
@@ -2632,12 +2552,12 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
if ((program.EpisodeNumber.HasValue && program.SeasonNumber.HasValue) || !string.IsNullOrWhiteSpace(program.EpisodeTitle))
{
- var seriesIds = _libraryManager.GetItemIds(new InternalItemsQuery
- {
- IncludeItemTypes = new[] { typeof(Series).Name },
- Name = program.Name
-
- }).ToArray();
+ var seriesIds = _libraryManager.GetItemIds(
+ new InternalItemsQuery
+ {
+ IncludeItemTypes = new[] { typeof(Series).Name },
+ Name = program.Name
+ }).ToArray();
if (seriesIds.Length == 0)
{
@@ -2666,59 +2586,70 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return false;
}
- private bool _disposed;
+ /// <inheritdoc />
public void Dispose()
{
- _disposed = true;
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (_disposed)
+ {
+ return;
+ }
+
+ if (disposing)
+ {
+ _recordingDeleteSemaphore.Dispose();
+ }
+
foreach (var pair in _activeRecordings.ToList())
{
pair.Value.CancellationTokenSource.Cancel();
}
+
+ _disposed = true;
}
- public List<VirtualFolderInfo> GetRecordingFolders()
+ public IEnumerable<VirtualFolderInfo> GetRecordingFolders()
{
- var list = new List<VirtualFolderInfo>();
-
var defaultFolder = RecordingPath;
var defaultName = "Recordings";
if (Directory.Exists(defaultFolder))
{
- list.Add(new VirtualFolderInfo
+ yield return new VirtualFolderInfo
{
Locations = new string[] { defaultFolder },
Name = defaultName
- });
+ };
}
var customPath = GetConfiguration().MovieRecordingPath;
- if ((!string.IsNullOrWhiteSpace(customPath) && !string.Equals(customPath, defaultFolder, StringComparison.OrdinalIgnoreCase)) && Directory.Exists(customPath))
+ if (!string.IsNullOrWhiteSpace(customPath) && !string.Equals(customPath, defaultFolder, StringComparison.OrdinalIgnoreCase) && Directory.Exists(customPath))
{
- list.Add(new VirtualFolderInfo
+ yield return new VirtualFolderInfo
{
Locations = new string[] { customPath },
Name = "Recorded Movies",
CollectionType = CollectionType.Movies
- });
+ };
}
customPath = GetConfiguration().SeriesRecordingPath;
- if ((!string.IsNullOrWhiteSpace(customPath) && !string.Equals(customPath, defaultFolder, StringComparison.OrdinalIgnoreCase)) && Directory.Exists(customPath))
+ if (!string.IsNullOrWhiteSpace(customPath) && !string.Equals(customPath, defaultFolder, StringComparison.OrdinalIgnoreCase) && Directory.Exists(customPath))
{
- list.Add(new VirtualFolderInfo
+ yield return new VirtualFolderInfo
{
Locations = new string[] { customPath },
Name = "Recorded Shows",
CollectionType = CollectionType.TvShows
- });
+ };
}
-
- return list;
}
- private const int TunerDiscoveryDurationMs = 3000;
-
public async Task<List<TunerHostInfo>> DiscoverTuners(bool newDevicesOnly, CancellationToken cancellationToken)
{
var list = new List<TunerHostInfo>();
@@ -2737,6 +2668,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
discoveredDevices = discoveredDevices.Where(d => !configuredDeviceIds.Contains(d.DeviceId, StringComparer.OrdinalIgnoreCase))
.ToList();
}
+
list.AddRange(discoveredDevices);
}
@@ -2773,11 +2705,11 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
}
- private async Task<List<TunerHostInfo>> DiscoverDevices(ITunerHost host, int discoveryDuationMs, CancellationToken cancellationToken)
+ private async Task<List<TunerHostInfo>> DiscoverDevices(ITunerHost host, int discoveryDurationMs, CancellationToken cancellationToken)
{
try
{
- var discoveredDevices = await host.DiscoverDevices(discoveryDuationMs, cancellationToken).ConfigureAwait(false);
+ var discoveredDevices = await host.DiscoverDevices(discoveryDurationMs, cancellationToken).ConfigureAwait(false);
foreach (var device in discoveredDevices)
{
@@ -2794,11 +2726,4 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
}
}
- public static class ConfigurationExtension
- {
- public static XbmcMetadataOptions GetNfoConfiguration(this IConfigurationManager manager)
- {
- return manager.GetConfiguration<XbmcMetadataOptions>("xbmcmetadata");
- }
- }
}
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
index cc9c8e5d2..8590c56df 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
@@ -1,8 +1,10 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
-using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@@ -14,7 +16,6 @@ using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Diagnostics;
using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
using Microsoft.Extensions.Logging;
@@ -24,7 +25,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
public class EncodedRecorder : IRecorder
{
private readonly ILogger _logger;
- private readonly IFileSystem _fileSystem;
private readonly IMediaEncoder _mediaEncoder;
private readonly IServerApplicationPaths _appPaths;
private bool _hasExited;
@@ -38,7 +38,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
public EncodedRecorder(
ILogger logger,
- IFileSystem fileSystem,
IMediaEncoder mediaEncoder,
IServerApplicationPaths appPaths,
IJsonSerializer json,
@@ -46,7 +45,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
IServerConfigurationManager config)
{
_logger = logger;
- _fileSystem = fileSystem;
_mediaEncoder = mediaEncoder;
_appPaths = appPaths;
_json = json;
@@ -107,7 +105,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
Directory.CreateDirectory(Path.GetDirectoryName(logFilePath));
// FFMpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory.
- _logFileStream = _fileSystem.GetFileStream(logFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true);
+ _logFileStream = new FileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true);
var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(_json.SerializeToString(mediaSource) + Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine);
_logFileStream.Write(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length);
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs
index 9c9ba09f5..a716b6240 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs
@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
using System.Threading.Tasks;
using MediaBrowser.Controller.Plugins;
@@ -5,11 +8,13 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
public class EntryPoint : IServerEntryPoint
{
+ /// <inheritdoc />
public Task RunAsync()
{
return EmbyTV.Current.Start();
}
+ /// <inheritdoc />
public void Dispose()
{
}
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EpgChannelData.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EpgChannelData.cs
new file mode 100644
index 000000000..463d0ed0a
--- /dev/null
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EpgChannelData.cs
@@ -0,0 +1,67 @@
+#pragma warning disable CS1591
+
+using System;
+using System.Collections.Generic;
+using MediaBrowser.Controller.LiveTv;
+
+namespace Emby.Server.Implementations.LiveTv.EmbyTV
+{
+
+ internal class EpgChannelData
+ {
+ public EpgChannelData(IEnumerable<ChannelInfo> channels)
+ {
+ ChannelsById = new Dictionary<string, ChannelInfo>(StringComparer.OrdinalIgnoreCase);
+ ChannelsByNumber = new Dictionary<string, ChannelInfo>(StringComparer.OrdinalIgnoreCase);
+ ChannelsByName = new Dictionary<string, ChannelInfo>(StringComparer.OrdinalIgnoreCase);
+
+ foreach (var channel in channels)
+ {
+ ChannelsById[channel.Id] = channel;
+
+ if (!string.IsNullOrEmpty(channel.Number))
+ {
+ ChannelsByNumber[channel.Number] = channel;
+ }
+
+ var normalizedName = NormalizeName(channel.Name ?? string.Empty);
+ if (!string.IsNullOrWhiteSpace(normalizedName))
+ {
+ ChannelsByName[normalizedName] = channel;
+ }
+ }
+ }
+
+ private Dictionary<string, ChannelInfo> ChannelsById { get; set; }
+
+ private Dictionary<string, ChannelInfo> ChannelsByNumber { get; set; }
+
+ private Dictionary<string, ChannelInfo> ChannelsByName { get; set; }
+
+ public ChannelInfo GetChannelById(string id)
+ {
+ ChannelsById.TryGetValue(id, out var result);
+
+ return result;
+ }
+
+ public ChannelInfo GetChannelByNumber(string number)
+ {
+ ChannelsByNumber.TryGetValue(number, out var result);
+
+ return result;
+ }
+
+ public ChannelInfo GetChannelByName(string name)
+ {
+ ChannelsByName.TryGetValue(name, out var result);
+
+ return result;
+ }
+
+ public static string NormalizeName(string value)
+ {
+ return value.Replace(" ", string.Empty, StringComparison.Ordinal).Replace("-", string.Empty, StringComparison.Ordinal);
+ }
+ }
+}
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs
index 6eced3050..d6a1aee38 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs
@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
using System;
using System.Threading;
using System.Threading.Tasks;
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs
index 9055a70a6..6d42a58f4 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs
@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
using System;
using System.Collections.Generic;
using System.IO;
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/NfoConfigurationExtensions.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/NfoConfigurationExtensions.cs
new file mode 100644
index 000000000..83f5e8413
--- /dev/null
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/NfoConfigurationExtensions.cs
@@ -0,0 +1,19 @@
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Model.Configuration;
+
+namespace Emby.Server.Implementations.LiveTv.EmbyTV
+{
+ /// <summary>
+ /// Class containing extension methods for working with the nfo configuration.
+ /// </summary>
+ public static class NfoConfigurationExtensions
+ {
+ /// <summary>
+ /// Gets the nfo configuration.
+ /// </summary>
+ /// <param name="configurationManager">The configuration manager.</param>
+ /// <returns>The nfo configuration.</returns>
+ public static XbmcMetadataOptions GetNfoConfiguration(this IConfigurationManager configurationManager)
+ => configurationManager.GetConfiguration<XbmcMetadataOptions>("xbmcmetadata");
+ }
+}
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs
index ded3c7607..4cb9f6fe8 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs
@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
using System;
using System.Globalization;
using MediaBrowser.Controller.LiveTv;
@@ -21,7 +24,11 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
if (info.SeasonNumber.HasValue && info.EpisodeNumber.HasValue)
{
- name += string.Format(" S{0}E{1}", info.SeasonNumber.Value.ToString("00", CultureInfo.InvariantCulture), info.EpisodeNumber.Value.ToString("00", CultureInfo.InvariantCulture));
+ name += string.Format(
+ CultureInfo.InvariantCulture,
+ " S{0}E{1}",
+ info.SeasonNumber.Value.ToString("00", CultureInfo.InvariantCulture),
+ info.EpisodeNumber.Value.ToString("00", CultureInfo.InvariantCulture));
addHyphen = false;
}
else if (info.OriginalAirDate.HasValue)
@@ -32,7 +39,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
else
{
- name += " " + info.OriginalAirDate.Value.ToLocalTime().ToString("yyyy-MM-dd");
+ name += " " + info.OriginalAirDate.Value.ToLocalTime().ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
}
}
else
@@ -67,14 +74,15 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
date = date.ToLocalTime();
- return string.Format("{0}_{1}_{2}_{3}_{4}_{5}",
+ return string.Format(
+ CultureInfo.InvariantCulture,
+ "{0}_{1}_{2}_{3}_{4}_{5}",
date.Year.ToString("0000", CultureInfo.InvariantCulture),
date.Month.ToString("00", CultureInfo.InvariantCulture),
date.Day.ToString("00", CultureInfo.InvariantCulture),
date.Hour.ToString("00", CultureInfo.InvariantCulture),
date.Minute.ToString("00", CultureInfo.InvariantCulture),
- date.Second.ToString("00", CultureInfo.InvariantCulture)
- );
+ date.Second.ToString("00", CultureInfo.InvariantCulture));
}
}
}
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs
index 520b44404..9cc53fddc 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs
@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
using System;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.Serialization;
@@ -12,6 +15,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
}
+ /// <inheritdoc />
public override void Add(SeriesTimerInfo item)
{
if (string.IsNullOrEmpty(item.Id))
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
index d09b281d4..330e881ef 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
using System;
using System.Collections.Concurrent;
using System.Globalization;
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
index 1dd794da0..e9d3105bf 100644
--- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@@ -30,7 +33,11 @@ namespace Emby.Server.Implementations.LiveTv.Listings
private const string ApiUrl = "https://json.schedulesdirect.org/20141201";
- public SchedulesDirect(ILogger logger, IJsonSerializer jsonSerializer, IHttpClient httpClient, IApplicationHost appHost)
+ public SchedulesDirect(
+ ILogger<SchedulesDirect> logger,
+ IJsonSerializer jsonSerializer,
+ IHttpClient httpClient,
+ IApplicationHost appHost)
{
_logger = logger;
_jsonSerializer = jsonSerializer;
diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
index 1f38de2d8..c159b60a9 100644
--- a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
+++ b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -31,7 +34,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
public XmlTvListingsProvider(
IServerConfigurationManager config,
IHttpClient httpClient,
- ILogger logger,
+ ILogger<XmlTvListingsProvider> logger,
IFileSystem fileSystem,
IZipClient zipClient)
{
@@ -91,12 +94,12 @@ namespace Emby.Server.Implementations.LiveTv.Listings
{
using (var gzStream = new GZipStream(stream, CompressionMode.Decompress))
{
- await gzStream.CopyToAsync(fileStream).ConfigureAwait(false);
+ await gzStream.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false);
}
}
else
{
- await stream.CopyToAsync(fileStream).ConfigureAwait(false);
+ await stream.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false);
}
}
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs b/Emby.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs
index f9b274acb..222fed9d9 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs
@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
using System.Collections.Generic;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.LiveTv;
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs b/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs
index e584664c9..14b627f82 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs
@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
using System;
using System.Globalization;
using System.Linq;
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
index ee7db1413..f20f6140e 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -35,7 +38,7 @@ using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.LiveTv
{
/// <summary>
- /// Class LiveTvManager
+ /// Class LiveTvManager.
/// </summary>
public class LiveTvManager : ILiveTvManager, IDisposable
{
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
index 52d60c004..33887bbfd 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
@@ -1,52 +1,48 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
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;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.MediaInfo;
-using MediaBrowser.Model.Serialization;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.LiveTv
{
public class LiveTvMediaSourceProvider : IMediaSourceProvider
{
+ // Do not use a pipe here because Roku http requests to the server will fail, without any explicit error message.
+ private const char StreamIdDelimeter = '_';
+ private const string StreamIdDelimeterString = "_";
+
private readonly ILiveTvManager _liveTvManager;
- private readonly IJsonSerializer _jsonSerializer;
private readonly ILogger _logger;
private readonly IMediaSourceManager _mediaSourceManager;
- private readonly IMediaEncoder _mediaEncoder;
private readonly IServerApplicationHost _appHost;
- private IApplicationPaths _appPaths;
- public LiveTvMediaSourceProvider(ILiveTvManager liveTvManager, IApplicationPaths appPaths, IJsonSerializer jsonSerializer, ILoggerFactory loggerFactory, IMediaSourceManager mediaSourceManager, IMediaEncoder mediaEncoder, IServerApplicationHost appHost)
+ public LiveTvMediaSourceProvider(ILiveTvManager liveTvManager, ILogger<LiveTvMediaSourceProvider> logger, IMediaSourceManager mediaSourceManager, IServerApplicationHost appHost)
{
_liveTvManager = liveTvManager;
- _jsonSerializer = jsonSerializer;
+ _logger = logger;
_mediaSourceManager = mediaSourceManager;
- _mediaEncoder = mediaEncoder;
_appHost = appHost;
- _logger = loggerFactory.CreateLogger(GetType().Name);
- _appPaths = appPaths;
}
public Task<IEnumerable<MediaSourceInfo>> GetMediaSources(BaseItem item, CancellationToken cancellationToken)
{
- var baseItem = (BaseItem)item;
-
- if (baseItem.SourceType == SourceType.LiveTV)
+ if (item.SourceType == SourceType.LiveTV)
{
var activeRecordingInfo = _liveTvManager.GetActiveRecordingInfo(item.Path);
- if (string.IsNullOrEmpty(baseItem.Path) || activeRecordingInfo != null)
+ if (string.IsNullOrEmpty(item.Path) || activeRecordingInfo != null)
{
return GetMediaSourcesInternal(item, activeRecordingInfo, cancellationToken);
}
@@ -55,10 +51,6 @@ namespace Emby.Server.Implementations.LiveTv
return Task.FromResult<IEnumerable<MediaSourceInfo>>(Array.Empty<MediaSourceInfo>());
}
- // Do not use a pipe here because Roku http requests to the server will fail, without any explicit error message.
- private const char StreamIdDelimeter = '_';
- private const string StreamIdDelimeterString = "_";
-
private async Task<IEnumerable<MediaSourceInfo>> GetMediaSourcesInternal(BaseItem item, ActiveRecordingInfo activeRecordingInfo, CancellationToken cancellationToken)
{
IEnumerable<MediaSourceInfo> sources;
@@ -91,7 +83,7 @@ namespace Emby.Server.Implementations.LiveTv
foreach (var source in list)
{
source.Type = MediaSourceType.Default;
- source.BufferMs = source.BufferMs ?? 1500;
+ source.BufferMs ??= 1500;
if (source.RequiresOpening || forceRequireOpening)
{
@@ -100,11 +92,14 @@ namespace Emby.Server.Implementations.LiveTv
if (source.RequiresOpening)
{
- var openKeys = new List<string>();
- openKeys.Add(item.GetType().Name);
- openKeys.Add(item.Id.ToString("N", CultureInfo.InvariantCulture));
- openKeys.Add(source.Id ?? string.Empty);
- source.OpenToken = string.Join(StreamIdDelimeterString, openKeys.ToArray());
+ var openKeys = new List<string>
+ {
+ item.GetType().Name,
+ item.Id.ToString("N", CultureInfo.InvariantCulture),
+ source.Id ?? string.Empty
+ };
+
+ source.OpenToken = string.Join(StreamIdDelimeterString, openKeys);
}
// Dummy this up so that direct play checks can still run
@@ -114,11 +109,12 @@ namespace Emby.Server.Implementations.LiveTv
}
}
- _logger.LogDebug("MediaSources: {0}", _jsonSerializer.SerializeToString(list));
+ _logger.LogDebug("MediaSources: {@MediaSources}", list);
return list;
}
+ /// <inheritdoc />
public async Task<ILiveStream> OpenMediaSource(string openToken, List<ILiveStream> currentLiveStreams, CancellationToken cancellationToken)
{
var keys = openToken.Split(new[] { StreamIdDelimeter }, 3);
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
index 715f600a1..419ec3635 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
index 06f27fa3e..a2d972d19 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -36,7 +39,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
public HdHomerunHost(
IServerConfigurationManager config,
- ILogger logger,
+ ILogger<HdHomerunHost> logger,
IJsonSerializer jsonSerializer,
IFileSystem fileSystem,
IHttpClient httpClient,
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
index 9702392b2..56864ab11 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
using System;
using System.Buffers;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
index 649becbd3..77669da39 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
using System;
using System.Collections.Generic;
using System.IO;
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs
index 1d55e7992..5354489f9 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs
@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -96,7 +99,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
FileMode.Open,
FileAccess.Read,
FileShare.ReadWrite,
- StreamDefaults.DefaultFileStreamBufferSize,
+ IODefaults.FileStreamBufferSize,
allowAsyncFileRead ? FileOptions.SequentialScan | FileOptions.Asynchronous : FileOptions.SequentialScan);
public Task DeleteTempFiles()
@@ -199,7 +202,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
await StreamHelper.CopyToAsync(
inputStream,
stream,
- StreamDefaults.DefaultCopyToBufferSize,
+ IODefaults.CopyToBufferSize,
emptyReadLimit,
cancellationToken).ConfigureAwait(false);
}
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
index df054f1eb..46c77e7b0 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
using System;
using System.Collections.Generic;
using System.Globalization;
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
index 3d2267e75..511af150b 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -10,7 +13,6 @@ using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Model.Extensions;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.LiveTv.TunerHosts
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
index 0d94f4b32..861518387 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
using System;
using System.Collections.Generic;
using System.IO;
@@ -127,12 +130,12 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
Logger.LogInformation("Beginning {0} stream to {1}", GetType().Name, TempFilePath);
using (response)
using (var stream = response.Content)
- using (var fileStream = FileSystem.GetFileStream(TempFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, FileOpenOptions.None))
+ using (var fileStream = new FileStream(TempFilePath, FileMode.Create, FileAccess.Write, FileShare.Read))
{
await StreamHelper.CopyToAsync(
stream,
fileStream,
- StreamDefaults.DefaultCopyToBufferSize,
+ IODefaults.CopyToBufferSize,
() => Resolve(openTaskCompletionSource),
cancellationToken).ConfigureAwait(false);
}
diff --git a/Emby.Server.Implementations/Localization/Core/ar.json b/Emby.Server.Implementations/Localization/Core/ar.json
index 4da3cdd3b..fa0e48baf 100644
--- a/Emby.Server.Implementations/Localization/Core/ar.json
+++ b/Emby.Server.Implementations/Localization/Core/ar.json
@@ -1,23 +1,23 @@
{
"Albums": "ألبومات",
"AppDeviceValues": "تطبيق: {0}, جهاز: {1}",
- "Application": "التطبيق",
- "Artists": "الفنان",
+ "Application": "تطبيق",
+ "Artists": "الفنانين",
"AuthenticationSucceededWithUserName": "{0} سجل الدخول بنجاح",
"Books": "كتب",
"CameraImageUploadedFrom": "صورة كاميرا جديدة تم رفعها من {0}",
"Channels": "القنوات",
- "ChapterNameValue": "الباب {0}",
+ "ChapterNameValue": "فصل {0}",
"Collections": "مجموعات",
- "DeviceOfflineWithName": "تم قطع الاتصال بـ{0}",
+ "DeviceOfflineWithName": "قُطِع الاتصال بـ{0}",
"DeviceOnlineWithName": "{0} متصل",
"FailedLoginAttemptWithUserName": "عملية تسجيل الدخول فشلت من {0}",
- "Favorites": "التفضيلات",
+ "Favorites": "المفضلة",
"Folders": "المجلدات",
- "Genres": "أنواع الأفلام",
+ "Genres": "الأنواع",
"HeaderAlbumArtists": "فناني الألبومات",
"HeaderCameraUploads": "تحميلات الكاميرا",
- "HeaderContinueWatching": "استئناف المشاهدة",
+ "HeaderContinueWatching": "استئناف",
"HeaderFavoriteAlbums": "الألبومات المفضلة",
"HeaderFavoriteArtists": "الفنانون المفضلون",
"HeaderFavoriteEpisodes": "الحلقات المفضلة",
@@ -31,28 +31,28 @@
"ItemAddedWithName": "تم إضافة {0} للمكتبة",
"ItemRemovedWithName": "تم إزالة {0} من المكتبة",
"LabelIpAddressValue": "عنوان الآي بي: {0}",
- "LabelRunningTimeValue": "وقت التشغيل: {0}",
+ "LabelRunningTimeValue": "المدة: {0}",
"Latest": "الأحدث",
- "MessageApplicationUpdated": "لقد تم تحديث خادم أمبي",
+ "MessageApplicationUpdated": "لقد تم تحديث خادم Jellyfin",
"MessageApplicationUpdatedTo": "تم تحديث سيرفر Jellyfin الى {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "تم تحديث إعدادات الخادم في قسم {0}",
"MessageServerConfigurationUpdated": "تم تحديث إعدادات الخادم",
- "MixedContent": "محتوى مخلوط",
+ "MixedContent": "محتوى مختلط",
"Movies": "الأفلام",
"Music": "الموسيقى",
"MusicVideos": "الفيديوهات الموسيقية",
"NameInstallFailed": "فشل التثبيت {0}",
"NameSeasonNumber": "الموسم {0}",
"NameSeasonUnknown": "الموسم غير معروف",
- "NewVersionIsAvailable": "نسخة حديثة من سيرفر Jellyfin متوفرة للتحميل .",
+ "NewVersionIsAvailable": "نسخة جديدة من سيرفر Jellyfin متوفرة للتحميل.",
"NotificationOptionApplicationUpdateAvailable": "يوجد تحديث للتطبيق",
"NotificationOptionApplicationUpdateInstalled": "تم تحديث التطبيق",
"NotificationOptionAudioPlayback": "بدأ تشغيل المقطع الصوتي",
"NotificationOptionAudioPlaybackStopped": "تم إيقاف تشغيل المقطع الصوتي",
- "NotificationOptionCameraImageUploaded": "تم رقع صورة الكاميرا",
+ "NotificationOptionCameraImageUploaded": "تم رفع صورة الكاميرا",
"NotificationOptionInstallationFailed": "فشل في التثبيت",
- "NotificationOptionNewLibraryContent": "تم إضافة محتوى جديد",
- "NotificationOptionPluginError": "فشل في الملحق",
+ "NotificationOptionNewLibraryContent": "أُضِيفَ محتوى جديد",
+ "NotificationOptionPluginError": "فشل في الـPlugin",
"NotificationOptionPluginInstalled": "تم تثبيت الملحق",
"NotificationOptionPluginUninstalled": "تمت إزالة الملحق",
"NotificationOptionPluginUpdateInstalled": "تم تثبيت تحديثات الملحق",
@@ -75,8 +75,8 @@
"Songs": "الأغاني",
"StartupEmbyServerIsLoading": "سيرفر Jellyfin قيد التشغيل . الرجاء المحاولة بعد قليل.",
"SubtitleDownloadFailureForItem": "عملية إنزال الترجمة فشلت لـ{0}",
- "SubtitleDownloadFailureFromForItem": "الترجمات فشلت في التحميل من {0} لـ {1}",
- "SubtitlesDownloadedForItem": "تم تحميل الترجمات لـ {0}",
+ "SubtitleDownloadFailureFromForItem": "الترجمات فشلت في التحميل من {0} الى {1}",
+ "SubtitlesDownloadedForItem": "تم تحميل الترجمات الى {0}",
"Sync": "مزامنة",
"System": "النظام",
"TvShows": "البرامج التلفزيونية",
@@ -88,7 +88,7 @@
"UserOfflineFromDevice": "تم قطع اتصال {0} من {1}",
"UserOnlineFromDevice": "{0} متصل عبر {1}",
"UserPasswordChangedWithName": "تم تغيير كلمة السر للمستخدم {0}",
- "UserPolicyUpdatedWithName": "سياسة المستخدمين تم تحديثها لـ {0}",
+ "UserPolicyUpdatedWithName": "تم تحديث سياسة المستخدم {0}",
"UserStartedPlayingItemWithValues": "قام {0} ببدء تشغيل {1} على {2}",
"UserStoppedPlayingItemWithValues": "قام {0} بإيقاف تشغيل {1} على {2}",
"ValueHasBeenAddedToLibrary": "{0} تم اضافتها الى مكتبة الوسائط",
diff --git a/Emby.Server.Implementations/Localization/Core/bg-BG.json b/Emby.Server.Implementations/Localization/Core/bg-BG.json
index 46c10d912..8a1bbaa16 100644
--- a/Emby.Server.Implementations/Localization/Core/bg-BG.json
+++ b/Emby.Server.Implementations/Localization/Core/bg-BG.json
@@ -16,7 +16,7 @@
"Folders": "Папки",
"Genres": "Жанрове",
"HeaderAlbumArtists": "Изпълнители на албуми",
- "HeaderCameraUploads": "",
+ "HeaderCameraUploads": "Качени от камера",
"HeaderContinueWatching": "Продължаване на гледането",
"HeaderFavoriteAlbums": "Любими албуми",
"HeaderFavoriteArtists": "Любими изпълнители",
@@ -25,26 +25,26 @@
"HeaderFavoriteSongs": "Любими песни",
"HeaderLiveTV": "Телевизия на живо",
"HeaderNextUp": "Следва",
- "HeaderRecordingGroups": "",
+ "HeaderRecordingGroups": "Запис групи",
"HomeVideos": "Домашни клипове",
"Inherit": "Наследяване",
"ItemAddedWithName": "{0} е добавено към библиотеката",
"ItemRemovedWithName": "{0} е премахнато от библиотеката",
"LabelIpAddressValue": "ИП адрес: {0}",
- "LabelRunningTimeValue": "",
+ "LabelRunningTimeValue": "Стартирано от: {0}",
"Latest": "Последни",
"MessageApplicationUpdated": "Сървърът е обновен",
- "MessageApplicationUpdatedTo": "",
- "MessageNamedServerConfigurationUpdatedWithValue": "",
- "MessageServerConfigurationUpdated": "",
+ "MessageApplicationUpdatedTo": "Сървърът е обновен до {0}",
+ "MessageNamedServerConfigurationUpdatedWithValue": "Секцията {0} от сървърната конфигурация се актуализира",
+ "MessageServerConfigurationUpdated": "Конфигурацията на сървъра се актуализира",
"MixedContent": "Смесено съдържание",
"Movies": "Филми",
"Music": "Музика",
"MusicVideos": "Музикални клипове",
- "NameInstallFailed": "",
+ "NameInstallFailed": "{0} не можа да се инсталира",
"NameSeasonNumber": "Сезон {0}",
"NameSeasonUnknown": "Неразпознат сезон",
- "NewVersionIsAvailable": "",
+ "NewVersionIsAvailable": "Нова версия на Jellyfin сървъра е достъпна за сваляне.",
"NotificationOptionApplicationUpdateAvailable": "Налично е обновление на програмата",
"NotificationOptionApplicationUpdateInstalled": "Обновлението на програмата е инсталирано",
"NotificationOptionAudioPlayback": "Възпроизвеждането на звук започна",
@@ -58,7 +58,7 @@
"NotificationOptionPluginUpdateInstalled": "Обновлението на приставката е инсталирано",
"NotificationOptionServerRestartRequired": "Нужно е повторно пускане на сървъра",
"NotificationOptionTaskFailed": "Грешка в планирана задача",
- "NotificationOptionUserLockedOut": "",
+ "NotificationOptionUserLockedOut": "Потребителя е заключен",
"NotificationOptionVideoPlayback": "Възпроизвеждането на видео започна",
"NotificationOptionVideoPlaybackStopped": "Възпроизвеждането на видео е спряно",
"Photos": "Снимки",
@@ -70,12 +70,12 @@
"ProviderValue": "Доставчик: {0}",
"ScheduledTaskFailedWithName": "{0} се провали",
"ScheduledTaskStartedWithName": "{0} започна",
- "ServerNameNeedsToBeRestarted": "",
+ "ServerNameNeedsToBeRestarted": "{0} е нужно да се рестартира",
"Shows": "Сериали",
"Songs": "Песни",
"StartupEmbyServerIsLoading": "Сървърът зарежда. Моля, опитайте отново след малко.",
"SubtitleDownloadFailureForItem": "Неуспешно изтегляне на субтитри за {0}",
- "SubtitleDownloadFailureFromForItem": "",
+ "SubtitleDownloadFailureFromForItem": "Поднадписите за {1} от {0} не можаха да се изтеглят",
"SubtitlesDownloadedForItem": "Изтеглени са субтитри за {0}",
"Sync": "Синхронизиране",
"System": "Система",
@@ -83,15 +83,15 @@
"User": "Потребител",
"UserCreatedWithName": "Потребителят {0} е създаден",
"UserDeletedWithName": "Потребителят {0} е изтрит",
- "UserDownloadingItemWithValues": "",
- "UserLockedOutWithName": "",
+ "UserDownloadingItemWithValues": "{0} изтегля {1}",
+ "UserLockedOutWithName": "Потребител {0} се заключи",
"UserOfflineFromDevice": "{0} се разкачи от {1}",
"UserOnlineFromDevice": "{0} е на линия от {1}",
"UserPasswordChangedWithName": "Паролата на потребителя {0} е променена",
- "UserPolicyUpdatedWithName": "",
+ "UserPolicyUpdatedWithName": "Потребителската политика за {0} се актуализира",
"UserStartedPlayingItemWithValues": "{0} пусна {1}",
"UserStoppedPlayingItemWithValues": "{0} спря {1}",
- "ValueHasBeenAddedToLibrary": "",
+ "ValueHasBeenAddedToLibrary": "{0} беше добавен във Вашата библиотека",
"ValueSpecialEpisodeName": "Специални - {0}",
"VersionNumber": "Версия {0}"
}
diff --git a/Emby.Server.Implementations/Localization/Core/bn.json b/Emby.Server.Implementations/Localization/Core/bn.json
new file mode 100644
index 000000000..a7219a725
--- /dev/null
+++ b/Emby.Server.Implementations/Localization/Core/bn.json
@@ -0,0 +1,96 @@
+{
+ "DeviceOnlineWithName": "{0}-এর সাথে সংযুক্ত হয়েছে",
+ "DeviceOfflineWithName": "{0}-এর সাথে সংযোগ বিচ্ছিন্ন হয়েছে",
+ "Collections": "সংকলন",
+ "ChapterNameValue": "অধ্যায় {0}",
+ "Channels": "চ্যানেল",
+ "CameraImageUploadedFrom": "একটি নতুন ক্যামেরার চিত্র আপলোড করা হয়েছে {0} থেকে",
+ "Books": "বই",
+ "AuthenticationSucceededWithUserName": "{0} যাচাই সফল",
+ "Artists": "শিল্পী",
+ "Application": "অ্যাপ্লিকেশন",
+ "Albums": "অ্যালবামগুলো",
+ "HeaderFavoriteEpisodes": "প্রিব পর্বগুলো",
+ "HeaderFavoriteArtists": "প্রিয় শিল্পীরা",
+ "HeaderFavoriteAlbums": "প্রিয় এলবামগুলো",
+ "HeaderContinueWatching": "দেখতে থাকুন",
+ "HeaderCameraUploads": "ক্যামেরার আপলোডগুলো",
+ "HeaderAlbumArtists": "এলবামের শিল্পী",
+ "Genres": "ঘরানা",
+ "Folders": "ফোল্ডারগুলো",
+ "Favorites": "ফেভারিটগুলো",
+ "FailedLoginAttemptWithUserName": "{0} থেকে লগিন করতে ব্যর্থ",
+ "AppDeviceValues": "এপ: {0}, ডিভাইস: {0}",
+ "VersionNumber": "সংস্করণ {0}",
+ "ValueSpecialEpisodeName": "বিশেষ - {0}",
+ "ValueHasBeenAddedToLibrary": "আপনার লাইব্রেরিতে {0} যোগ করা হয়েছে",
+ "UserStoppedPlayingItemWithValues": "{2}তে {1} বাজানো শেষ করেছেন {0}",
+ "UserStartedPlayingItemWithValues": "{2}তে {1} বাজাচ্ছেন {0}",
+ "UserPolicyUpdatedWithName": "{0} এর জন্য ব্যবহার নীতি আপডেট করা হয়েছে",
+ "UserPasswordChangedWithName": "ব্যবহারকারী {0} এর পাসওয়ার্ড পরিবর্তিত হয়েছে",
+ "UserOnlineFromDevice": "{0}, {1} থেকে অনলাইন",
+ "UserOfflineFromDevice": "{0} {1} থেকে বিযুক্ত হয়ে গেছে",
+ "UserLockedOutWithName": "ব্যবহারকারী {0} ঢুকতে পারছে না",
+ "UserDownloadingItemWithValues": "{0}, {1} ডাউনলোড করছে",
+ "UserDeletedWithName": "ব্যবহারকারী {0}কে বাদ দেয়া হয়েছে",
+ "UserCreatedWithName": "ব্যবহারকারী {0} সৃষ্টি করা হয়েছে",
+ "User": "ব্যবহারকারী",
+ "TvShows": "টিভি শোগুলো",
+ "System": "সিস্টেম",
+ "Sync": "সিংক",
+ "SubtitlesDownloadedForItem": "{0} এর জন্য সাবটাইটেল ডাউনলোড করা হয়েছে",
+ "SubtitleDownloadFailureFromForItem": "{2} থেকে {1} এর জন্য সাবটাইটেল ডাউনলোড ব্যর্থ",
+ "StartupEmbyServerIsLoading": "জেলিফিন সার্ভার লোড হচ্ছে। দয়া করে একটু পরে আবার চেষ্টা করুন।",
+ "Songs": "গানগুলো",
+ "Shows": "টিভি পর্ব",
+ "ServerNameNeedsToBeRestarted": "{0} রিস্টার্ট করা প্রয়োজন",
+ "ScheduledTaskStartedWithName": "{0} শুরু হয়েছে",
+ "ScheduledTaskFailedWithName": "{0} ব্যর্থ",
+ "ProviderValue": "প্রদানকারী: {0}",
+ "PluginUpdatedWithName": "{0} আপডেট করা হয়েছে",
+ "PluginUninstalledWithName": "{0} বাদ দেয়া হয়েছে",
+ "PluginInstalledWithName": "{0} ইন্সটল করা হয়েছে",
+ "Plugin": "প্লাগিন",
+ "Playlists": "প্লেলিস্ট",
+ "Photos": "ছবিগুলো",
+ "NotificationOptionVideoPlaybackStopped": "ভিডিও চলা বন্ধ",
+ "NotificationOptionVideoPlayback": "ভিডিও চলা শুরু হয়েছে",
+ "NotificationOptionUserLockedOut": "ব্যবহারকারী ঢুকতে পারছে না",
+ "NotificationOptionTaskFailed": "পরিকল্পিত কাজটি ব্যর্থ",
+ "NotificationOptionServerRestartRequired": "সার্ভার রিস্টার্ট বাধ্যতামূলক",
+ "NotificationOptionPluginUpdateInstalled": "প্লাগিন আপডেট ইন্সটল করা হয়েছে",
+ "NotificationOptionPluginUninstalled": "প্লাগিন বাদ দেয়া হয়েছে",
+ "NotificationOptionPluginInstalled": "প্লাগিন ইন্সটল করা হয়েছে",
+ "NotificationOptionPluginError": "প্লাগিন ব্যর্থ",
+ "NotificationOptionNewLibraryContent": "নতুন কন্টেন্ট যোগ করা হয়েছে",
+ "NotificationOptionInstallationFailed": "ইন্সটল ব্যর্থ",
+ "NotificationOptionCameraImageUploaded": "ক্যামেরার ছবি আপলোড হয়েছে",
+ "NotificationOptionAudioPlaybackStopped": "গান বাজা বন্ধ হয়েছে",
+ "NotificationOptionAudioPlayback": "গান বাজা শুরু হয়েছে",
+ "NotificationOptionApplicationUpdateInstalled": "এপ্লিকেশনের আপডেট ইনস্টল করা হয়েছে",
+ "NotificationOptionApplicationUpdateAvailable": "এপ্লিকেশনের আপডেট রয়েছে",
+ "NewVersionIsAvailable": "জেলিফিন সার্ভারের একটি নতুন ভার্শন ডাউনলোডের জন্য তৈরী",
+ "NameSeasonUnknown": "সিজন অজানা",
+ "NameSeasonNumber": "সিজন {0}",
+ "NameInstallFailed": "{0} ইন্সটল ব্যর্থ",
+ "MusicVideos": "গানের ভিডিও",
+ "Music": "গান",
+ "Movies": "সিনেমা",
+ "MixedContent": "মিশ্র কন্টেন্ট",
+ "MessageServerConfigurationUpdated": "সার্ভারের কনফিগারেশন হালনাগাদ করা হয়েছে",
+ "HeaderRecordingGroups": "রেকর্ডিং গ্রুপ",
+ "MessageNamedServerConfigurationUpdatedWithValue": "সার্ভারের {0} কনফিগারেসন অংশ আপডেট করা হয়েছে",
+ "MessageApplicationUpdatedTo": "জেলিফিন সার্ভার {0} তে হালনাগাদ করা হয়েছে",
+ "MessageApplicationUpdated": "জেলিফিন সার্ভার হালনাগাদ করা হয়েছে",
+ "Latest": "একদম নতুন",
+ "LabelRunningTimeValue": "চলার সময়: {0}",
+ "LabelIpAddressValue": "আইপি ঠিকানা: {0}",
+ "ItemRemovedWithName": "{0} লাইব্রেরি থেকে বাদ দেয়া হয়েছে",
+ "ItemAddedWithName": "{0} লাইব্রেরিতে যোগ করা হয়েছে",
+ "Inherit": "থেকে পাওয়া",
+ "HomeVideos": "বাসার ভিডিও",
+ "HeaderNextUp": "এরপরে আসছে",
+ "HeaderLiveTV": "লাইভ টিভি",
+ "HeaderFavoriteSongs": "প্রিয় গানগুলো",
+ "HeaderFavoriteShows": "প্রিয় শোগুলো"
+}
diff --git a/Emby.Server.Implementations/Localization/Core/ca.json b/Emby.Server.Implementations/Localization/Core/ca.json
index 9961b0984..44e7cf0ce 100644
--- a/Emby.Server.Implementations/Localization/Core/ca.json
+++ b/Emby.Server.Implementations/Localization/Core/ca.json
@@ -3,9 +3,9 @@
"AppDeviceValues": "Aplicació: {0}, Dispositiu: {1}",
"Application": "Aplicació",
"Artists": "Artistes",
- "AuthenticationSucceededWithUserName": "{0} s'ha autenticat correctament",
+ "AuthenticationSucceededWithUserName": "{0} s'ha autentificat correctament",
"Books": "Llibres",
- "CameraImageUploadedFrom": "Una nova imatge de càmera ha sigut pujada des de {0}",
+ "CameraImageUploadedFrom": "Una nova imatge de la càmera ha sigut pujada des de {0}",
"Channels": "Canals",
"ChapterNameValue": "Episodi {0}",
"Collections": "Col·leccions",
diff --git a/Emby.Server.Implementations/Localization/Core/da.json b/Emby.Server.Implementations/Localization/Core/da.json
index cc8b7dbd5..c421db87d 100644
--- a/Emby.Server.Implementations/Localization/Core/da.json
+++ b/Emby.Server.Implementations/Localization/Core/da.json
@@ -3,7 +3,7 @@
"AppDeviceValues": "App: {0}, Enhed: {1}",
"Application": "Applikation",
"Artists": "Kunstnere",
- "AuthenticationSucceededWithUserName": "{0} bekræftet med succes",
+ "AuthenticationSucceededWithUserName": "{0} succesfuldt autentificeret",
"Books": "Bøger",
"CameraImageUploadedFrom": "Et nyt kamerabillede er blevet uploadet fra {0}",
"Channels": "Kanaler",
diff --git a/Emby.Server.Implementations/Localization/Core/de.json b/Emby.Server.Implementations/Localization/Core/de.json
index 019736c47..fa1e16bed 100644
--- a/Emby.Server.Implementations/Localization/Core/de.json
+++ b/Emby.Server.Implementations/Localization/Core/de.json
@@ -1,15 +1,15 @@
{
"Albums": "Alben",
- "AppDeviceValues": "App: {0}, Gerät: {1}",
+ "AppDeviceValues": "Anw: {0}, Gerät: {1}",
"Application": "Anwendung",
"Artists": "Interpreten",
- "AuthenticationSucceededWithUserName": "{0} hat sich erfolgreich angemeldet",
+ "AuthenticationSucceededWithUserName": "{0} erfolgreich angemeldet",
"Books": "Bücher",
- "CameraImageUploadedFrom": "Ein neues Foto wurde hochgeladen von {0}",
+ "CameraImageUploadedFrom": "Ein neues Foto wurde von {0} hochgeladen",
"Channels": "Kanäle",
"ChapterNameValue": "Kapitel {0}",
"Collections": "Sammlungen",
- "DeviceOfflineWithName": "{0} wurde getrennt",
+ "DeviceOfflineWithName": "{0} hat die Verbindung getrennt",
"DeviceOnlineWithName": "{0} ist verbunden",
"FailedLoginAttemptWithUserName": "Fehlgeschlagener Anmeldeversuch von {0}",
"Favorites": "Favoriten",
@@ -17,7 +17,7 @@
"Genres": "Genres",
"HeaderAlbumArtists": "Album-Interpreten",
"HeaderCameraUploads": "Kamera-Uploads",
- "HeaderContinueWatching": "Weiterschauen",
+ "HeaderContinueWatching": "Fortsetzen",
"HeaderFavoriteAlbums": "Lieblingsalben",
"HeaderFavoriteArtists": "Lieblings-Interpreten",
"HeaderFavoriteEpisodes": "Lieblingsepisoden",
@@ -50,7 +50,7 @@
"NotificationOptionAudioPlayback": "Audiowiedergabe gestartet",
"NotificationOptionAudioPlaybackStopped": "Audiowiedergabe gestoppt",
"NotificationOptionCameraImageUploaded": "Foto hochgeladen",
- "NotificationOptionInstallationFailed": "Installationsfehler",
+ "NotificationOptionInstallationFailed": "Fehler bei der Installation",
"NotificationOptionNewLibraryContent": "Neuer Inhalt hinzugefügt",
"NotificationOptionPluginError": "Plugin-Fehler",
"NotificationOptionPluginInstalled": "Plugin installiert",
diff --git a/Emby.Server.Implementations/Localization/Core/es_DO.json b/Emby.Server.Implementations/Localization/Core/es_DO.json
new file mode 100644
index 000000000..1a7b57c53
--- /dev/null
+++ b/Emby.Server.Implementations/Localization/Core/es_DO.json
@@ -0,0 +1,21 @@
+{
+ "Channels": "Canales",
+ "Books": "Libros",
+ "Albums": "Álbumes",
+ "Collections": "Colecciones",
+ "Artists": "Artistas",
+ "DeviceOnlineWithName": "{0} está conectado",
+ "DeviceOfflineWithName": "{0} ha desconectado",
+ "ChapterNameValue": "Capítulo {0}",
+ "CameraImageUploadedFrom": "Se ha subido una nueva imagen de cámara desde {0}",
+ "AuthenticationSucceededWithUserName": "{0} autenticado con éxito",
+ "Application": "Aplicación",
+ "AppDeviceValues": "App: {0}, Dispositivo: {1}",
+ "HeaderContinueWatching": "Continuar Viendo",
+ "HeaderCameraUploads": "Subidas de Cámara",
+ "HeaderAlbumArtists": "Artistas del Álbum",
+ "Genres": "Géneros",
+ "Folders": "Carpetas",
+ "Favorites": "Favoritos",
+ "FailedLoginAttemptWithUserName": "Intento de inicio de sesión fallido de {0}"
+}
diff --git a/Emby.Server.Implementations/Localization/Core/fi.json b/Emby.Server.Implementations/Localization/Core/fi.json
index 15aa0a8ee..a38103d25 100644
--- a/Emby.Server.Implementations/Localization/Core/fi.json
+++ b/Emby.Server.Implementations/Localization/Core/fi.json
@@ -1,6 +1,6 @@
{
- "HeaderLiveTV": "Netti-TV",
- "NewVersionIsAvailable": "Uusi versio Jellyfin palvelimesta on ladattavissa",
+ "HeaderLiveTV": "TV-lähetykset",
+ "NewVersionIsAvailable": "Uusi versio Jellyfin palvelimesta on ladattavissa.",
"NameSeasonUnknown": "Tuntematon Kausi",
"NameSeasonNumber": "Kausi {0}",
"NameInstallFailed": "{0} asennus epäonnistui",
@@ -8,41 +8,89 @@
"Music": "Musiikki",
"Movies": "Elokuvat",
"MixedContent": "Sekoitettu sisältö",
- "MessageServerConfigurationUpdated": "Palvelimen konfiguraatio on päivitetty",
- "MessageNamedServerConfigurationUpdatedWithValue": "Palvelimen konfiguraatio-osa {0} on päivitetty",
- "MessageApplicationUpdatedTo": "Jellyfin palvelin on päivitetty {0}",
+ "MessageServerConfigurationUpdated": "Palvelimen asetukset on päivitetty",
+ "MessageNamedServerConfigurationUpdatedWithValue": "Palvelimen asetusryhmä {0} on päivitetty",
+ "MessageApplicationUpdatedTo": "Jellyfin palvelin on päivitetty versioon {0}",
"MessageApplicationUpdated": "Jellyfin palvelin on päivitetty",
"Latest": "Viimeisin",
- "LabelRunningTimeValue": "Kesto: {0}",
+ "LabelRunningTimeValue": "Toiston kesto: {0}",
"LabelIpAddressValue": "IP-osoite: {0}",
"ItemRemovedWithName": "{0} poistettiin kirjastosta",
"ItemAddedWithName": "{0} lisättiin kirjastoon",
- "Inherit": "Periä",
+ "Inherit": "Periytyä",
"HomeVideos": "Kotivideot",
- "HeaderRecordingGroups": "Äänitysryhmä",
+ "HeaderRecordingGroups": "Nauhoitusryhmät",
"HeaderNextUp": "Seuraavaksi",
"HeaderFavoriteSongs": "Lempikappaleet",
"HeaderFavoriteShows": "Lempisarjat",
"HeaderFavoriteEpisodes": "Lempijaksot",
- "HeaderCameraUploads": "Kamerasta Ladatut",
+ "HeaderCameraUploads": "Kameralataukset",
"HeaderFavoriteArtists": "Lempiartistit",
"HeaderFavoriteAlbums": "Lempialbumit",
- "HeaderContinueWatching": "Jatka Katsomista",
- "HeaderAlbumArtists": "Albumiartistit",
- "Genres": "Genret",
+ "HeaderContinueWatching": "Jatka katsomista",
+ "HeaderAlbumArtists": "Albumin esittäjä",
+ "Genres": "Tyylilajit",
"Folders": "Kansiot",
"Favorites": "Suosikit",
- "FailedLoginAttemptWithUserName": "Epäonnistunut kirjautumisyritys kohteesta {0}",
- "DeviceOnlineWithName": "{0} on yhdistynyt",
+ "FailedLoginAttemptWithUserName": "Kirjautuminen epäonnistui kohteesta {0}",
+ "DeviceOnlineWithName": "{0} on yhdistetty",
"DeviceOfflineWithName": "{0} on katkaissut yhteytensä",
"Collections": "Kokoelmat",
"ChapterNameValue": "Luku: {0}",
"Channels": "Kanavat",
- "CameraImageUploadedFrom": "Uusi kamerakuva on lähetetty kohteesta {0}",
+ "CameraImageUploadedFrom": "Uusi kamerakuva on ladattu {0}",
"Books": "Kirjat",
- "AuthenticationSucceededWithUserName": "{0} todennettu onnistuneesti",
- "Artists": "Artistit",
+ "AuthenticationSucceededWithUserName": "{0} todennus onnistui",
+ "Artists": "Esiintyjät",
"Application": "Sovellus",
"AppDeviceValues": "Sovellus: {0}, Laite: {1}",
- "Albums": "Albumit"
+ "Albums": "Albumit",
+ "User": "Käyttäjä",
+ "System": "Järjestelmä",
+ "ScheduledTaskFailedWithName": "{0} epäonnistui",
+ "PluginUpdatedWithName": "{0} päivitetty",
+ "PluginInstalledWithName": "{0} asennettu",
+ "Photos": "Kuvat",
+ "ScheduledTaskStartedWithName": "{0} aloitettu",
+ "PluginUninstalledWithName": "{0} poistettu",
+ "Playlists": "Soittolistat",
+ "VersionNumber": "Versio {0}",
+ "ValueSpecialEpisodeName": "Erikois - {0}",
+ "ValueHasBeenAddedToLibrary": "{0} lisättiin mediakirjastoon",
+ "UserStoppedPlayingItemWithValues": "{0} toistaminen valmistui {1} laitteella {2}",
+ "UserStartedPlayingItemWithValues": "{0} toistaa {1} laitteella {2}",
+ "UserPolicyUpdatedWithName": "Käyttöoikeudet päivitetty käyttäjälle {0}",
+ "UserPasswordChangedWithName": "Salasana vaihdettu käyttäjälle {0}",
+ "UserOnlineFromDevice": "{0} on paikalla osoitteesta {1}",
+ "UserOfflineFromDevice": "{0} yhteys katkaistu {1}",
+ "UserLockedOutWithName": "Käyttäjä {0} kirjautui ulos",
+ "UserDownloadingItemWithValues": "{0} latautumassa {1}",
+ "UserDeletedWithName": "Poistettiin käyttäjä {0}",
+ "UserCreatedWithName": "Luotiin käyttäjä {0}",
+ "TvShows": "TV-Ohjelmat",
+ "Sync": "Synkronoi",
+ "SubtitlesDownloadedForItem": "Tekstitys ladattu {0}",
+ "SubtitleDownloadFailureFromForItem": "Tekstityksen lataaminen epäonnistui {0} - {1}",
+ "StartupEmbyServerIsLoading": "Jellyfin palvelin latautuu. Kokeile hetken kuluttua uudelleen.",
+ "Songs": "Kappaleet",
+ "Shows": "Ohjelmat",
+ "ServerNameNeedsToBeRestarted": "{0} vaatii uudelleenkäynnistyksen",
+ "ProviderValue": "Palveluntarjoaja: {0}",
+ "Plugin": "Liitännäinen",
+ "NotificationOptionVideoPlaybackStopped": "Videon toistaminen pysäytetty",
+ "NotificationOptionVideoPlayback": "Videon toistaminen aloitettu",
+ "NotificationOptionUserLockedOut": "Käyttäjä kirjautui ulos",
+ "NotificationOptionTaskFailed": "Ajastetun tehtävän ongelma",
+ "NotificationOptionServerRestartRequired": "Palvelimen uudelleenkäynnistys vaaditaan",
+ "NotificationOptionPluginUpdateInstalled": "Liitännäinen päivitetty",
+ "NotificationOptionPluginUninstalled": "Liitännäinen poistettu",
+ "NotificationOptionPluginInstalled": "Liitännäinen asennettu",
+ "NotificationOptionPluginError": "Ongelma liitännäisessä",
+ "NotificationOptionNewLibraryContent": "Uutta sisältöä lisätty",
+ "NotificationOptionInstallationFailed": "Asennus epäonnistui",
+ "NotificationOptionCameraImageUploaded": "Kuva ladattu kamerasta",
+ "NotificationOptionAudioPlaybackStopped": "Audion toisto pysäytetty",
+ "NotificationOptionAudioPlayback": "Audion toisto aloitettu",
+ "NotificationOptionApplicationUpdateInstalled": "Ohjelmistopäivitys asennettu",
+ "NotificationOptionApplicationUpdateAvailable": "Ohjelmistopäivitys saatavilla"
}
diff --git a/Emby.Server.Implementations/Localization/Core/fil.json b/Emby.Server.Implementations/Localization/Core/fil.json
new file mode 100644
index 000000000..66db059d9
--- /dev/null
+++ b/Emby.Server.Implementations/Localization/Core/fil.json
@@ -0,0 +1,95 @@
+{
+ "VersionNumber": "Bersyon {0}",
+ "ValueSpecialEpisodeName": "Espesyal - {0}",
+ "ValueHasBeenAddedToLibrary": "Naidagdag na ang {0} sa iyong media library",
+ "UserStoppedPlayingItemWithValues": "Natapos ni {0} ang {1} sa {2}",
+ "UserStartedPlayingItemWithValues": "Si {0} ay nagplaplay ng {1} sa {2}",
+ "UserPolicyUpdatedWithName": "Ang user policy ay naiupdate para kay {0}",
+ "UserPasswordChangedWithName": "Napalitan na ang password ni {0}",
+ "UserOnlineFromDevice": "Si {0} ay nakakonekta galing sa {1}",
+ "UserOfflineFromDevice": "Si {0} ay nadiskonekta galing sa {1}",
+ "UserLockedOutWithName": "Si {0} ay nalock out",
+ "UserDownloadingItemWithValues": "Nagdadownload si {0} ng {1}",
+ "UserDeletedWithName": "Natanggal na is user {0}",
+ "UserCreatedWithName": "Nagawa na si user {0}",
+ "User": "User",
+ "TvShows": "Pelikula",
+ "System": "Sistema",
+ "Sync": "Pag-sync",
+ "SubtitlesDownloadedForItem": "Naidownload na ang subtitles {0}",
+ "SubtitleDownloadFailureFromForItem": "Hindi naidownload ang subtitles {0} para sa {1}",
+ "StartupEmbyServerIsLoading": "Nagloload ang Jellyfin Server. Sandaling maghintay.",
+ "Songs": "Kanta",
+ "Shows": "Pelikula",
+ "ServerNameNeedsToBeRestarted": "Kailangan irestart ang {0}",
+ "ScheduledTaskStartedWithName": "Nagsimula na ang {0}",
+ "ScheduledTaskFailedWithName": "Hindi gumana and {0}",
+ "ProviderValue": "Ang provider ay {0}",
+ "PluginUpdatedWithName": "Naiupdate na ang {0}",
+ "PluginUninstalledWithName": "Naiuninstall na ang {0}",
+ "PluginInstalledWithName": "Nainstall na ang {0}",
+ "Plugin": "Plugin",
+ "Playlists": "Playlists",
+ "Photos": "Larawan",
+ "NotificationOptionVideoPlaybackStopped": "Huminto na ang pelikula",
+ "NotificationOptionVideoPlayback": "Nagsimula na ang pelikula",
+ "NotificationOptionUserLockedOut": "Nakalock out ang user",
+ "NotificationOptionTaskFailed": "Hindi gumana ang scheduled task",
+ "NotificationOptionServerRestartRequired": "Kailangan irestart ang server",
+ "NotificationOptionPluginUpdateInstalled": "Naiupdate na ang plugin",
+ "NotificationOptionPluginUninstalled": "Naiuninstall na ang plugin",
+ "NotificationOptionPluginInstalled": "Nainstall na ang plugin",
+ "NotificationOptionPluginError": "Hindi gumagana ang plugin",
+ "NotificationOptionNewLibraryContent": "May bagong content na naidagdag",
+ "NotificationOptionInstallationFailed": "Hindi nainstall ng mabuti",
+ "NotificationOptionCameraImageUploaded": "Naiupload na ang picture",
+ "NotificationOptionAudioPlaybackStopped": "Huminto na ang patugtog",
+ "NotificationOptionAudioPlayback": "Nagsimula na ang patugtog",
+ "NotificationOptionApplicationUpdateInstalled": "Naiupdate na ang aplikasyon",
+ "NotificationOptionApplicationUpdateAvailable": "May bagong update ang aplikasyon",
+ "NewVersionIsAvailable": "May bagong version ng Jellyfin Server na pwede idownload.",
+ "NameSeasonUnknown": "Hindi alam ang season",
+ "NameSeasonNumber": "Season {0}",
+ "NameInstallFailed": "Hindi nainstall ang {0}",
+ "MusicVideos": "Music video",
+ "Music": "Kanta",
+ "Movies": "Pelikula",
+ "MixedContent": "Halo-halong content",
+ "MessageServerConfigurationUpdated": "Naiupdate na ang server configuration",
+ "MessageNamedServerConfigurationUpdatedWithValue": "Naiupdate na ang server configuration section {0}",
+ "MessageApplicationUpdatedTo": "Ang Jellyfin Server ay naiupdate to {0}",
+ "MessageApplicationUpdated": "Naiupdate na ang Jellyfin Server",
+ "Latest": "Pinakabago",
+ "LabelRunningTimeValue": "Oras: {0}",
+ "LabelIpAddressValue": "Ang IP Address ay {0}",
+ "ItemRemovedWithName": "Naitanggal ang {0} sa library",
+ "ItemAddedWithName": "Naidagdag ang {0} sa library",
+ "Inherit": "Manahin",
+ "HeaderRecordingGroups": "Pagtatalang Grupo",
+ "HeaderNextUp": "Susunod",
+ "HeaderLiveTV": "Live TV",
+ "HeaderFavoriteSongs": "Paboritong Kanta",
+ "HeaderFavoriteShows": "Paboritong Pelikula",
+ "HeaderFavoriteEpisodes": "Paboritong Episodes",
+ "HeaderFavoriteArtists": "Paboritong Artista",
+ "HeaderFavoriteAlbums": "Paboritong Albums",
+ "HeaderContinueWatching": "Ituloy Manood",
+ "HeaderCameraUploads": "Camera Uploads",
+ "HeaderAlbumArtists": "Artista ng Album",
+ "Genres": "Kategorya",
+ "Folders": "Folders",
+ "Favorites": "Paborito",
+ "FailedLoginAttemptWithUserName": "maling login galing {0}",
+ "DeviceOnlineWithName": "nakakonekta si {0}",
+ "DeviceOfflineWithName": "nadiskonekta si {0}",
+ "Collections": "Koleksyon",
+ "ChapterNameValue": "Kabanata {0}",
+ "Channels": "Channel",
+ "CameraImageUploadedFrom": "May bagong larawan na naupload galing {0}",
+ "Books": "Libro",
+ "AuthenticationSucceededWithUserName": "{0} na patunayan",
+ "Artists": "Artista",
+ "Application": "Aplikasyon",
+ "AppDeviceValues": "Aplikasyon: {0}, Aparato: {1}",
+ "Albums": "Albums"
+}
diff --git a/Emby.Server.Implementations/Localization/Core/fr.json b/Emby.Server.Implementations/Localization/Core/fr.json
index e1dce82ff..7dfee1085 100644
--- a/Emby.Server.Implementations/Localization/Core/fr.json
+++ b/Emby.Server.Implementations/Localization/Core/fr.json
@@ -3,19 +3,19 @@
"AppDeviceValues": "Application : {0}, Appareil : {1}",
"Application": "Application",
"Artists": "Artistes",
- "AuthenticationSucceededWithUserName": "{0} s'est authentifié avec succès",
+ "AuthenticationSucceededWithUserName": "{0} authentifié avec succès",
"Books": "Livres",
- "CameraImageUploadedFrom": "Une nouvelle image de caméra a été chargée depuis {0}",
+ "CameraImageUploadedFrom": "Une nouvelle photo a été chargée depuis {0}",
"Channels": "Chaînes",
"ChapterNameValue": "Chapitre {0}",
"Collections": "Collections",
"DeviceOfflineWithName": "{0} s'est déconnecté",
"DeviceOnlineWithName": "{0} est connecté",
- "FailedLoginAttemptWithUserName": "Échec d'une tentative de connexion de {0}",
+ "FailedLoginAttemptWithUserName": "Échec de connexion de {0}",
"Favorites": "Favoris",
"Folders": "Dossiers",
"Genres": "Genres",
- "HeaderAlbumArtists": "Artistes de l'album",
+ "HeaderAlbumArtists": "Artistes d'album",
"HeaderCameraUploads": "Photos transférées",
"HeaderContinueWatching": "Continuer à regarder",
"HeaderFavoriteAlbums": "Albums favoris",
@@ -34,7 +34,7 @@
"LabelRunningTimeValue": "Durée : {0}",
"Latest": "Derniers",
"MessageApplicationUpdated": "Le serveur Jellyfin a été mis à jour",
- "MessageApplicationUpdatedTo": "Le serveur Jellyfin a été mis à jour vers {0}",
+ "MessageApplicationUpdatedTo": "Le serveur Jellyfin a été mis à jour en version {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "La configuration de la section {0} du serveur a été mise à jour",
"MessageServerConfigurationUpdated": "La configuration du serveur a été mise à jour",
"MixedContent": "Contenu mixte",
diff --git a/Emby.Server.Implementations/Localization/Core/gl.json b/Emby.Server.Implementations/Localization/Core/gl.json
new file mode 100644
index 000000000..94034962d
--- /dev/null
+++ b/Emby.Server.Implementations/Localization/Core/gl.json
@@ -0,0 +1,3 @@
+{
+ "Albums": "Álbumes"
+}
diff --git a/Emby.Server.Implementations/Localization/Core/he.json b/Emby.Server.Implementations/Localization/Core/he.json
index b08c8966e..5618719dd 100644
--- a/Emby.Server.Implementations/Localization/Core/he.json
+++ b/Emby.Server.Implementations/Localization/Core/he.json
@@ -2,33 +2,33 @@
"Albums": "אלבומים",
"AppDeviceValues": "יישום: {0}, מכשיר: {1}",
"Application": "אפליקציה",
- "Artists": "אמנים",
- "AuthenticationSucceededWithUserName": "{0} זוהה בהצלחה",
+ "Artists": "אומנים",
+ "AuthenticationSucceededWithUserName": "{0} אומת בהצלחה",
"Books": "ספרים",
- "CameraImageUploadedFrom": "תמונה חדשה הועלתה מ{0}",
+ "CameraImageUploadedFrom": "תמונת מצלמה חדשה הועלתה מ {0}",
"Channels": "ערוצים",
"ChapterNameValue": "פרק {0}",
- "Collections": "קולקציות",
+ "Collections": "אוספים",
"DeviceOfflineWithName": "{0} התנתק",
"DeviceOnlineWithName": "{0} מחובר",
"FailedLoginAttemptWithUserName": "ניסיון כניסה שגוי מ{0}",
- "Favorites": "אהובים",
+ "Favorites": "מועדפים",
"Folders": "תיקיות",
"Genres": "ז'אנרים",
"HeaderAlbumArtists": "אמני האלבום",
"HeaderCameraUploads": "העלאות ממצלמה",
"HeaderContinueWatching": "המשך לצפות",
"HeaderFavoriteAlbums": "אלבומים שאהבתי",
- "HeaderFavoriteArtists": "אמנים שאהבתי",
- "HeaderFavoriteEpisodes": "פרקים אהובים",
- "HeaderFavoriteShows": "תוכניות אהובות",
- "HeaderFavoriteSongs": "שירים שאהבתי",
- "HeaderLiveTV": "טלוויזיה בשידור חי",
+ "HeaderFavoriteArtists": "אמנים מועדפים",
+ "HeaderFavoriteEpisodes": "פרקים מועדפים",
+ "HeaderFavoriteShows": "סדרות מועדפות",
+ "HeaderFavoriteSongs": "שירים מועדפים",
+ "HeaderLiveTV": "שידורים חיים",
"HeaderNextUp": "הבא",
"HeaderRecordingGroups": "קבוצות הקלטה",
"HomeVideos": "סרטונים בייתים",
"Inherit": "הורש",
- "ItemAddedWithName": "{0} was added to the library",
+ "ItemAddedWithName": "{0} הוסף לספרייה",
"ItemRemovedWithName": "{0} נמחק מהספרייה",
"LabelIpAddressValue": "Ip כתובת: {0}",
"LabelRunningTimeValue": "משך צפייה: {0}",
@@ -36,15 +36,15 @@
"MessageApplicationUpdated": "שרת הJellyfin עודכן",
"MessageApplicationUpdatedTo": "שרת הJellyfin עודכן לגרסא {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "הגדרת השרת {0} שונתה",
- "MessageServerConfigurationUpdated": "Server configuration has been updated",
+ "MessageServerConfigurationUpdated": "תצורת השרת עודכנה",
"MixedContent": "תוכן מעורב",
"Movies": "סרטים",
"Music": "מוזיקה",
- "MusicVideos": "Music videos",
- "NameInstallFailed": "{0} installation failed",
- "NameSeasonNumber": "Season {0}",
- "NameSeasonUnknown": "Season Unknown",
- "NewVersionIsAvailable": "A new version of Jellyfin Server is available for download.",
+ "MusicVideos": "קליפים",
+ "NameInstallFailed": "התקנת {0} נכשלה",
+ "NameSeasonNumber": "עונה {0}",
+ "NameSeasonUnknown": "עונה לא ידועה",
+ "NewVersionIsAvailable": "גרסה חדשה של שרת Jellyfin זמינה להורדה.",
"NotificationOptionApplicationUpdateAvailable": "Application update available",
"NotificationOptionApplicationUpdateInstalled": "Application update installed",
"NotificationOptionAudioPlayback": "Audio playback started",
@@ -53,10 +53,10 @@
"NotificationOptionInstallationFailed": "התקנה נכשלה",
"NotificationOptionNewLibraryContent": "New content added",
"NotificationOptionPluginError": "Plugin failure",
- "NotificationOptionPluginInstalled": "Plugin installed",
- "NotificationOptionPluginUninstalled": "Plugin uninstalled",
- "NotificationOptionPluginUpdateInstalled": "Plugin update installed",
- "NotificationOptionServerRestartRequired": "Server restart required",
+ "NotificationOptionPluginInstalled": "התוסף הותקן",
+ "NotificationOptionPluginUninstalled": "התוסף הוסר",
+ "NotificationOptionPluginUpdateInstalled": "העדכון לתוסף הותקן",
+ "NotificationOptionServerRestartRequired": "יש לאתחל את השרת",
"NotificationOptionTaskFailed": "Scheduled task failure",
"NotificationOptionUserLockedOut": "User locked out",
"NotificationOptionVideoPlayback": "Video playback started",
@@ -71,26 +71,26 @@
"ScheduledTaskFailedWithName": "{0} failed",
"ScheduledTaskStartedWithName": "{0} started",
"ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
- "Shows": "Shows",
- "Songs": "Songs",
- "StartupEmbyServerIsLoading": "Jellyfin Server is loading. Please try again shortly.",
+ "Shows": "סדרות",
+ "Songs": "שירים",
+ "StartupEmbyServerIsLoading": "שרת Jellyfin בהליכי טעינה. אנא נסה שנית בעוד זמן קצר.",
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
"SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
"SubtitlesDownloadedForItem": "Subtitles downloaded for {0}",
"Sync": "סנכרן",
"System": "System",
- "TvShows": "TV Shows",
+ "TvShows": "סדרות טלוויזיה",
"User": "User",
- "UserCreatedWithName": "User {0} has been created",
- "UserDeletedWithName": "User {0} has been deleted",
- "UserDownloadingItemWithValues": "{0} is downloading {1}",
+ "UserCreatedWithName": "המשתמש {0} נוצר",
+ "UserDeletedWithName": "המשתמש {0} הוסר",
+ "UserDownloadingItemWithValues": "{0} מוריד את {1}",
"UserLockedOutWithName": "User {0} has been locked out",
"UserOfflineFromDevice": "{0} has disconnected from {1}",
"UserOnlineFromDevice": "{0} is online from {1}",
"UserPasswordChangedWithName": "Password has been changed for user {0}",
"UserPolicyUpdatedWithName": "User policy has been updated for {0}",
- "UserStartedPlayingItemWithValues": "{0} is playing {1} on {2}",
- "UserStoppedPlayingItemWithValues": "{0} has finished playing {1} on {2}",
+ "UserStartedPlayingItemWithValues": "{0} מנגן את {1} על {2}",
+ "UserStoppedPlayingItemWithValues": "{0} סיים לנגן את {1} על {2}",
"ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
"ValueSpecialEpisodeName": "מיוחד- {0}",
"VersionNumber": "Version {0}"
diff --git a/Emby.Server.Implementations/Localization/Core/hu.json b/Emby.Server.Implementations/Localization/Core/hu.json
index 3a6852321..6017aa7f9 100644
--- a/Emby.Server.Implementations/Localization/Core/hu.json
+++ b/Emby.Server.Implementations/Localization/Core/hu.json
@@ -1,7 +1,7 @@
{
"Albums": "Albumok",
"AppDeviceValues": "Program: {0}, Eszköz: {1}",
- "Application": "Program",
+ "Application": "Alkalmazás",
"Artists": "Előadók",
"AuthenticationSucceededWithUserName": "{0} sikeresen azonosítva",
"Books": "Könyvek",
@@ -11,13 +11,13 @@
"Collections": "Gyűjtemények",
"DeviceOfflineWithName": "{0} kijelentkezett",
"DeviceOnlineWithName": "{0} belépett",
- "FailedLoginAttemptWithUserName": "Sikertelen bejelentkezési kísérlet {0}",
+ "FailedLoginAttemptWithUserName": "Sikertelen bejelentkezési kísérlet tőle: {0}",
"Favorites": "Kedvencek",
"Folders": "Könyvtárak",
"Genres": "Műfajok",
"HeaderAlbumArtists": "Album előadók",
"HeaderCameraUploads": "Kamera feltöltések",
- "HeaderContinueWatching": "Folyamatban lévő filmek",
+ "HeaderContinueWatching": "Megtekintés folytatása",
"HeaderFavoriteAlbums": "Kedvenc albumok",
"HeaderFavoriteArtists": "Kedvenc előadók",
"HeaderFavoriteEpisodes": "Kedvenc epizódok",
@@ -27,7 +27,7 @@
"HeaderNextUp": "Következik",
"HeaderRecordingGroups": "Felvételi csoportok",
"HomeVideos": "Házi videók",
- "Inherit": "Inherit",
+ "Inherit": "Örökölt",
"ItemAddedWithName": "{0} hozzáadva a könyvtárhoz",
"ItemRemovedWithName": "{0} eltávolítva a könyvtárból",
"LabelIpAddressValue": "IP cím: {0}",
@@ -42,7 +42,7 @@
"Music": "Zene",
"MusicVideos": "Zenei videók",
"NameInstallFailed": "{0} sikertelen telepítés",
- "NameSeasonNumber": "Évad {0}",
+ "NameSeasonNumber": "{0}. évad",
"NameSeasonUnknown": "Ismeretlen évad",
"NewVersionIsAvailable": "Letölthető a Jellyfin Szerver új verziója.",
"NotificationOptionApplicationUpdateAvailable": "Frissítés érhető el az alkalmazáshoz",
@@ -73,7 +73,7 @@
"ServerNameNeedsToBeRestarted": "{0}-t újra kell indítani",
"Shows": "Műsorok",
"Songs": "Dalok",
- "StartupEmbyServerIsLoading": "A Jellyfin Szerver betöltődik. Kérlek próbáld újra később.",
+ "StartupEmbyServerIsLoading": "A Jellyfin Szerver betöltődik. Kérlek, próbáld újra hamarosan.",
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
"SubtitleDownloadFailureFromForItem": "Nem sikerült a felirat letöltése innen: {0} ehhez: {1}",
"SubtitlesDownloadedForItem": "Letöltött feliratok a következőhöz: {0}",
@@ -86,11 +86,11 @@
"UserDownloadingItemWithValues": "{0} letölti {1}",
"UserLockedOutWithName": "{0} felhasználó zárolva van",
"UserOfflineFromDevice": "{0} kijelentkezett innen: {1}",
- "UserOnlineFromDevice": "{0} online itt: {1}",
+ "UserOnlineFromDevice": "{0} online innen: {1}",
"UserPasswordChangedWithName": "Jelszó megváltozott a következő felhasználó számára: {0}",
"UserPolicyUpdatedWithName": "A felhasználói házirend frissítve lett neki: {0}",
"UserStartedPlayingItemWithValues": "{0} elkezdte játszani a következőt: {1} itt: {2}",
- "UserStoppedPlayingItemWithValues": "{0} befejezte a következőt: {1} itt: {2}",
+ "UserStoppedPlayingItemWithValues": "{0} befejezte {1} lejátászását itt: {2}",
"ValueHasBeenAddedToLibrary": "{0} hozzáadva a médiatárhoz",
"ValueSpecialEpisodeName": "Special - {0}",
"VersionNumber": "Verzió: {0}"
diff --git a/Emby.Server.Implementations/Localization/Core/id.json b/Emby.Server.Implementations/Localization/Core/id.json
index 8d17ad38e..68fffbf0a 100644
--- a/Emby.Server.Implementations/Localization/Core/id.json
+++ b/Emby.Server.Implementations/Localization/Core/id.json
@@ -6,7 +6,7 @@
"MessageApplicationUpdatedTo": "Jellyfin Server sudah diperbarui ke {0}",
"MessageApplicationUpdated": "Jellyfin Server sudah diperbarui",
"Latest": "Terbaru",
- "LabelIpAddressValue": "IP address: {0}",
+ "LabelIpAddressValue": "Alamat IP: {0}",
"ItemRemovedWithName": "{0} sudah dikeluarkan dari perpustakaan",
"ItemAddedWithName": "{0} sudah dimasukkan ke dalam perpustakaan",
"Inherit": "Warisan",
@@ -28,5 +28,69 @@
"Collections": "Koleksi",
"Books": "Buku",
"Artists": "Artis",
- "Application": "Aplikasi"
+ "Application": "Aplikasi",
+ "ChapterNameValue": "Bagian {0}",
+ "Channels": "Saluran",
+ "TvShows": "Seri TV",
+ "SubtitleDownloadFailureFromForItem": "Talop gagal diunduh dari {0} untuk {1}",
+ "StartupEmbyServerIsLoading": "Peladen Jellyfin sedang dimuat. Silakan coba kembali beberapa saat lagi.",
+ "Songs": "Lagu",
+ "Playlists": "Daftar putar",
+ "NotificationOptionPluginUninstalled": "Plugin dilepas",
+ "MusicVideos": "Video musik",
+ "VersionNumber": "Versi {0}",
+ "ValueSpecialEpisodeName": "Spesial - {0}",
+ "ValueHasBeenAddedToLibrary": "{0} telah ditambahkan ke pustaka media Anda",
+ "UserStoppedPlayingItemWithValues": "{0} telah selesai memutar {1} pada {2}",
+ "UserStartedPlayingItemWithValues": "{0} sedang memutar {1} pada {2}",
+ "UserPolicyUpdatedWithName": "Kebijakan pengguna telah diperbarui untuk {0}",
+ "UserPasswordChangedWithName": "Kata sandi telah diubah untuk pengguna {0}",
+ "UserOnlineFromDevice": "{0} sedang daring dari {1}",
+ "UserOfflineFromDevice": "{0} telah terputus dari {1}",
+ "UserLockedOutWithName": "Pengguna {0} telah dikunci",
+ "UserDownloadingItemWithValues": "{0} sedang mengunduh {1}",
+ "UserDeletedWithName": "Pengguna {0} telah dihapus",
+ "UserCreatedWithName": "Pengguna {0} telah dibuat",
+ "User": "Pengguna",
+ "System": "Sistem",
+ "Sync": "Sinkron",
+ "SubtitlesDownloadedForItem": "Talop telah diunduh untuk {0}",
+ "Shows": "Tayangan",
+ "ServerNameNeedsToBeRestarted": "{0} perlu dimuat ulang",
+ "ScheduledTaskStartedWithName": "{0} dimulai",
+ "ScheduledTaskFailedWithName": "{0} gagal",
+ "ProviderValue": "Penyedia: {0}",
+ "PluginUpdatedWithName": "{0} telah diperbarui",
+ "PluginInstalledWithName": "{0} telah dipasang",
+ "Plugin": "Plugin",
+ "Photos": "Foto",
+ "NotificationOptionUserLockedOut": "Pengguna terkunci",
+ "NotificationOptionTaskFailed": "Kegagalan tugas terjadwal",
+ "NotificationOptionServerRestartRequired": "Restart peladen dibutuhkan",
+ "NotificationOptionPluginUpdateInstalled": "Pembaruan plugin terpasang",
+ "NotificationOptionPluginInstalled": "Plugin terpasang",
+ "NotificationOptionPluginError": "Kegagalan plugin",
+ "NotificationOptionNewLibraryContent": "Konten baru ditambahkan",
+ "NotificationOptionInstallationFailed": "Kegagalan pemasangan",
+ "NotificationOptionCameraImageUploaded": "Gambar kamera terunggah",
+ "NotificationOptionApplicationUpdateInstalled": "Pembaruan aplikasi terpasang",
+ "NotificationOptionApplicationUpdateAvailable": "Pembaruan aplikasi tersedia",
+ "NewVersionIsAvailable": "Sebuah versi baru dari Peladen Jellyfin tersedia untuk diunduh.",
+ "NameSeasonUnknown": "Musim tak diketahui",
+ "NameSeasonNumber": "Musim {0}",
+ "NameInstallFailed": "{0} instalasi gagal",
+ "Music": "Musik",
+ "Movies": "Film",
+ "MessageServerConfigurationUpdated": "Konfigurasi peladen telah diperbarui",
+ "MessageNamedServerConfigurationUpdatedWithValue": "Konfigurasi peladen bagian {0} telah diperbarui",
+ "FailedLoginAttemptWithUserName": "Percobaan login gagal dari {0}",
+ "CameraImageUploadedFrom": "Sebuah gambar baru telah diunggah dari {0}",
+ "DeviceOfflineWithName": "{0} telah terputus",
+ "DeviceOnlineWithName": "{0} telah terhubung",
+ "NotificationOptionVideoPlaybackStopped": "Pemutaran video berhenti",
+ "NotificationOptionVideoPlayback": "Pemutaran video dimulai",
+ "NotificationOptionAudioPlaybackStopped": "Pemutaran audio berhenti",
+ "NotificationOptionAudioPlayback": "Pemutaran audio dimulai",
+ "MixedContent": "Konten campur",
+ "PluginUninstalledWithName": "{0} telah dihapus"
}
diff --git a/Emby.Server.Implementations/Localization/Core/is.json b/Emby.Server.Implementations/Localization/Core/is.json
index c3b5211b8..3490a7302 100644
--- a/Emby.Server.Implementations/Localization/Core/is.json
+++ b/Emby.Server.Implementations/Localization/Core/is.json
@@ -3,7 +3,7 @@
"ItemRemovedWithName": "{0} var fjarlægt úr safninu",
"ItemAddedWithName": "{0} var bætt í safnið",
"Inherit": "Erfa",
- "HomeVideos": "Myndbönd að heiman",
+ "HomeVideos": "Heimamyndbönd",
"HeaderRecordingGroups": "Upptökuhópar",
"HeaderNextUp": "Næst á dagskrá",
"HeaderLiveTV": "Sjónvarp í beinni útsendingu",
@@ -36,10 +36,10 @@
"NotificationOptionVideoPlaybackStopped": "Myndbandafspilun stöðvuð",
"NotificationOptionVideoPlayback": "Myndbandafspilun hafin",
"NotificationOptionUserLockedOut": "Notandi læstur úti",
- "NotificationOptionServerRestartRequired": "Endurræsing miðlara nauðsynileg",
+ "NotificationOptionServerRestartRequired": "Endurræsing þjóns er nauðsynileg",
"NotificationOptionPluginUpdateInstalled": "Viðbótar uppfærsla uppsett",
"NotificationOptionPluginUninstalled": "Viðbót fjarlægð",
- "NotificationOptionPluginInstalled": "Viðbót settur upp",
+ "NotificationOptionPluginInstalled": "Viðbót sett upp",
"NotificationOptionPluginError": "Bilun í viðbót",
"NotificationOptionInstallationFailed": "Uppsetning tókst ekki",
"NotificationOptionCameraImageUploaded": "Myndavélarmynd hlaðið upp",
@@ -50,15 +50,15 @@
"NameSeasonUnknown": "Sería óþekkt",
"NameSeasonNumber": "Sería {0}",
"MixedContent": "Blandað efni",
- "MessageServerConfigurationUpdated": "Stillingar miðlarans hefur verið uppfærð",
- "MessageApplicationUpdatedTo": "Jellyfin Server hefur verið uppfærður í {0}",
- "MessageApplicationUpdated": "Jellyfin Server hefur verið uppfærður",
+ "MessageServerConfigurationUpdated": "Stillingar þjóns hafa verið uppfærðar",
+ "MessageApplicationUpdatedTo": "Jellyfin þjónn hefur verið uppfærður í {0}",
+ "MessageApplicationUpdated": "Jellyfin þjónn hefur verið uppfærður",
"Latest": "Nýjasta",
- "LabelRunningTimeValue": "Keyrslutími kerfis: {0}",
+ "LabelRunningTimeValue": "spilunartími: {0}",
"User": "Notandi",
"System": "Kerfi",
"NotificationOptionNewLibraryContent": "Nýju efni bætt við",
- "NewVersionIsAvailable": "Ný útgáfa af Jellyfin Server er fáanleg til niðurhals.",
+ "NewVersionIsAvailable": "Ný útgáfa af Jellyfin þjón er fáanleg til niðurhals.",
"NameInstallFailed": "{0} uppsetning mistókst",
"MusicVideos": "Tónlistarmyndbönd",
"Music": "Tónlist",
@@ -74,5 +74,23 @@
"PluginUpdatedWithName": "{0} var uppfært",
"PluginUninstalledWithName": "{0} var fjarlægt",
"PluginInstalledWithName": "{0} var sett upp",
- "NotificationOptionTaskFailed": "Tímasett verkefni mistókst"
+ "NotificationOptionTaskFailed": "Tímasett verkefni mistókst",
+ "StartupEmbyServerIsLoading": "Jellyfin netþjónnin er að hlaðast. Vinsamlega prufaðu aftur fljótlega.",
+ "VersionNumber": "Útgáfa {0}",
+ "ValueHasBeenAddedToLibrary": "{0} hefur verið bætt við í gagnasafnið þitt",
+ "UserStoppedPlayingItemWithValues": "{0} hefur lokið spilunar af {1} á {2}",
+ "UserStartedPlayingItemWithValues": "{0} er að spila {1} á {2}",
+ "UserPolicyUpdatedWithName": "Notandaregla hefur verið uppfærð fyrir notanda {0}",
+ "UserPasswordChangedWithName": "Lykilorði fyrir notandann {0} hefur verið breytt",
+ "UserOnlineFromDevice": "{0} hefur verið virkur síðan {1}",
+ "UserOfflineFromDevice": "{0} hefur aftengst frá {1}",
+ "UserLockedOutWithName": "Notanda {0} hefur verið hindraður aðgangur",
+ "UserDownloadingItemWithValues": "{0} Hleður niður {1}",
+ "SubtitlesDownloadedForItem": "Skjátextum halað niður fyrir {0}",
+ "SubtitleDownloadFailureFromForItem": "Tókst ekki að hala niður skjátextum frá {0} til {1}",
+ "ProviderValue": "Veitandi: {0}",
+ "MessageNamedServerConfigurationUpdatedWithValue": "Stilling {0} hefur verið uppfærð á netþjón",
+ "ValueSpecialEpisodeName": "Sérstakt - {0}",
+ "Shows": "Þættir",
+ "Playlists": "Spilunarlisti"
}
diff --git a/Emby.Server.Implementations/Localization/Core/it.json b/Emby.Server.Implementations/Localization/Core/it.json
index 8f91effb9..395924af4 100644
--- a/Emby.Server.Implementations/Localization/Core/it.json
+++ b/Emby.Server.Implementations/Localization/Core/it.json
@@ -5,11 +5,11 @@
"Artists": "Artisti",
"AuthenticationSucceededWithUserName": "{0} autenticato con successo",
"Books": "Libri",
- "CameraImageUploadedFrom": "È stata caricata una nuova immagine della fotocamera {0}",
+ "CameraImageUploadedFrom": "È stata caricata una nuova immagine della fotocamera da {0}",
"Channels": "Canali",
"ChapterNameValue": "Capitolo {0}",
"Collections": "Collezioni",
- "DeviceOfflineWithName": "{0} ha disconnesso",
+ "DeviceOfflineWithName": "{0} si è disconnesso",
"DeviceOnlineWithName": "{0} è connesso",
"FailedLoginAttemptWithUserName": "Tentativo di accesso fallito da {0}",
"Favorites": "Preferiti",
@@ -18,7 +18,7 @@
"HeaderAlbumArtists": "Artisti dell' Album",
"HeaderCameraUploads": "Caricamenti Fotocamera",
"HeaderContinueWatching": "Continua a guardare",
- "HeaderFavoriteAlbums": "Album preferiti",
+ "HeaderFavoriteAlbums": "Album Preferiti",
"HeaderFavoriteArtists": "Artisti Preferiti",
"HeaderFavoriteEpisodes": "Episodi Preferiti",
"HeaderFavoriteShows": "Serie TV Preferite",
diff --git a/Emby.Server.Implementations/Localization/Core/lv.json b/Emby.Server.Implementations/Localization/Core/lv.json
new file mode 100644
index 000000000..8b8d46b2e
--- /dev/null
+++ b/Emby.Server.Implementations/Localization/Core/lv.json
@@ -0,0 +1,96 @@
+{
+ "ServerNameNeedsToBeRestarted": "{0} ir vajadzīgs restarts",
+ "NotificationOptionTaskFailed": "Plānota uzdevuma kļūme",
+ "HeaderRecordingGroups": "Ierakstu Grupas",
+ "UserPolicyUpdatedWithName": "Lietotāju politika atjaunota priekš {0}",
+ "SubtitleDownloadFailureFromForItem": "Subtitru lejupielāde no {0} priekš {1} neizdevās",
+ "NotificationOptionVideoPlaybackStopped": "Video atskaņošana apturēta",
+ "NotificationOptionVideoPlayback": "Video atskaņošana sākta",
+ "NotificationOptionInstallationFailed": "Instalācija neizdevās",
+ "AuthenticationSucceededWithUserName": "{0} veiksmīgi autentificējies",
+ "ValueSpecialEpisodeName": "Speciālais - {0}",
+ "ScheduledTaskStartedWithName": "{0} iesākts",
+ "ScheduledTaskFailedWithName": "{0} neizdevās",
+ "Photos": "Attēli",
+ "NotificationOptionUserLockedOut": "Lietotājs bloķēts",
+ "LabelRunningTimeValue": "Garums: {0}",
+ "Inherit": "Mantot",
+ "AppDeviceValues": "Lietotne:{0}, Ierīce:{1}",
+ "VersionNumber": "Versija {0}",
+ "ValueHasBeenAddedToLibrary": "{0} ir ticis pievienots tavai multvides bibliotēkai",
+ "UserStoppedPlayingItemWithValues": "{0} ir beidzis atskaņot {1} uz {2}",
+ "UserStartedPlayingItemWithValues": "{0} atskaņo {1} uz {2}",
+ "UserPasswordChangedWithName": "Parole nomainīta lietotājam {0}",
+ "UserOnlineFromDevice": "{0} ir tiešsaistē no {1}",
+ "UserOfflineFromDevice": "{0} ir atvienojies no {1}",
+ "UserLockedOutWithName": "Lietotājs {0} ir ticis bloķēts",
+ "UserDownloadingItemWithValues": "{0} lejupielādē {1}",
+ "UserDeletedWithName": "Lietotājs {0} ir izdzēsts",
+ "UserCreatedWithName": "Lietotājs {0} ir ticis izveidots",
+ "User": "Lietotājs",
+ "TvShows": "TV Raidījumi",
+ "Sync": "Sinhronizācija",
+ "System": "Sistēma",
+ "SubtitlesDownloadedForItem": "Subtitri lejupielādēti priekš {0}",
+ "StartupEmbyServerIsLoading": "Jellyfin Serveris lādējas. Lūdzu mēģiniet vēlreiz pēc brīža.",
+ "Songs": "Dziesmas",
+ "Shows": "Raidījumi",
+ "PluginUpdatedWithName": "{0} tika atjaunots",
+ "PluginUninstalledWithName": "{0} tika noņemts",
+ "PluginInstalledWithName": "{0} tika uzstādīts",
+ "Plugin": "Paplašinājums",
+ "Playlists": "Atskaņošanas Saraksti",
+ "MixedContent": "Jaukts saturs",
+ "HomeVideos": "Mājas Video",
+ "HeaderNextUp": "Nākamais",
+ "ChapterNameValue": "Nodaļa {0}",
+ "Application": "Lietotne",
+ "NotificationOptionServerRestartRequired": "Vajadzīgs servera restarts",
+ "NotificationOptionPluginUpdateInstalled": "Paplašinājuma atjauninājums uzstādīts",
+ "NotificationOptionPluginUninstalled": "Paplašinājums noņemts",
+ "NotificationOptionPluginInstalled": "Paplašinājums uzstādīts",
+ "NotificationOptionPluginError": "Paplašinājuma kļūda",
+ "NotificationOptionNewLibraryContent": "Jauns saturs pievienots",
+ "NotificationOptionCameraImageUploaded": "Kameras attēls augšupielādēts",
+ "NotificationOptionAudioPlaybackStopped": "Audio atskaņošana apturēta",
+ "NotificationOptionAudioPlayback": "Audio atskaņošana sākta",
+ "NotificationOptionApplicationUpdateInstalled": "Lietotnes atjauninājums uzstādīts",
+ "NotificationOptionApplicationUpdateAvailable": "Lietotnes atjauninājums pieejams",
+ "NewVersionIsAvailable": "Lejupielādei ir pieejama jauna Jellyfin Server versija.",
+ "NameSeasonUnknown": "Nezināma Sezona",
+ "NameSeasonNumber": "Sezona {0}",
+ "NameInstallFailed": "{0} instalācija neizdevās",
+ "MusicVideos": "Mūzikas video",
+ "Music": "Mūzika",
+ "Movies": "Filmas",
+ "MessageServerConfigurationUpdated": "Servera konfigurācija ir tikusi atjaunota",
+ "MessageNamedServerConfigurationUpdatedWithValue": "Servera konfigurācijas sadaļa {0} ir tikusi atjaunota",
+ "MessageApplicationUpdatedTo": "Jellyfin Server ir ticis atjaunots uz {0}",
+ "MessageApplicationUpdated": "Jellyfin Server ir ticis atjaunots",
+ "Latest": "Jaunākais",
+ "LabelIpAddressValue": "IP adrese: {0}",
+ "ItemRemovedWithName": "{0} tika noņemts no bibliotēkas",
+ "ItemAddedWithName": "{0} tika pievienots bibliotēkai",
+ "HeaderLiveTV": "Tiešraides TV",
+ "HeaderContinueWatching": "Turpināt Skatīšanos",
+ "HeaderCameraUploads": "Kameras augšupielādes",
+ "HeaderAlbumArtists": "Albumu Izpildītāji",
+ "Genres": "Žanri",
+ "Folders": "Mapes",
+ "Favorites": "Favorīti",
+ "FailedLoginAttemptWithUserName": "Neizdevies pieslēgšanās mēģinājums no {0}",
+ "DeviceOnlineWithName": "{0} ir pievienojies",
+ "DeviceOfflineWithName": "{0} ir atvienojies",
+ "Collections": "Kolekcijas",
+ "Channels": "Kanāli",
+ "CameraImageUploadedFrom": "Jauns kameras attēls ir ticis augšupielādēts no {0}",
+ "Books": "Grāmatas",
+ "Artists": "Izpildītāji",
+ "Albums": "Albumi",
+ "ProviderValue": "Provider: {0}",
+ "HeaderFavoriteSongs": "Dziesmu Favorīti",
+ "HeaderFavoriteShows": "Raidījumu Favorīti",
+ "HeaderFavoriteEpisodes": "Episožu Favorīti",
+ "HeaderFavoriteArtists": "Izpildītāju Favorīti",
+ "HeaderFavoriteAlbums": "Albumu Favorīti"
+}
diff --git a/Emby.Server.Implementations/Localization/Core/mk.json b/Emby.Server.Implementations/Localization/Core/mk.json
new file mode 100644
index 000000000..684a97aad
--- /dev/null
+++ b/Emby.Server.Implementations/Localization/Core/mk.json
@@ -0,0 +1,96 @@
+{
+ "ScheduledTaskFailedWithName": "{0} неуспешно",
+ "ProviderValue": "Провајдер: {0}",
+ "PluginUpdatedWithName": "{0} беше надоградено",
+ "PluginUninstalledWithName": "{0} беше успешно деинсталирано",
+ "PluginInstalledWithName": "{0} беше успешно инсталирано",
+ "Plugin": "Додатоци",
+ "Playlists": "Листи",
+ "Photos": "Слики",
+ "NotificationOptionVideoPlaybackStopped": "Видео стопирано",
+ "NotificationOptionVideoPlayback": "Видео пуштено",
+ "NotificationOptionUserLockedOut": "Корисникот е ослободен",
+ "NotificationOptionTaskFailed": "Закажани задачи неуспешно",
+ "NotificationOptionServerRestartRequired": "Задолжително рестартирање на серверот",
+ "NotificationOptionPluginUpdateInstalled": "Надоградба на Додаток успешна",
+ "NotificationOptionPluginUninstalled": "Додаток успешно деинсталиран",
+ "NotificationOptionPluginInstalled": "Додаток успешно инсталиран",
+ "NotificationOptionPluginError": "Грешка на додаток",
+ "NotificationOptionNewLibraryContent": "Додадена нова содржина",
+ "NotificationOptionInstallationFailed": "Неуспешна Инсталација",
+ "NotificationOptionCameraImageUploaded": "Слика од камера поставена",
+ "NotificationOptionAudioPlaybackStopped": "Аудио стопирано",
+ "NotificationOptionAudioPlayback": "Аудио стартувано",
+ "NotificationOptionApplicationUpdateInstalled": "Надоградбата на Апликацијата е иснталирана",
+ "NotificationOptionApplicationUpdateAvailable": "Возможна надоградба на Апликацијата",
+ "NewVersionIsAvailable": "Нова верзија од Jellyfin е возможна за спуштање.",
+ "NameSeasonUnknown": "Непозната Сезона",
+ "NameSeasonNumber": "Сезона {0}",
+ "NameInstallFailed": "{0} неуспешна инсталација",
+ "MusicVideos": "Музички видеа",
+ "Music": "Музика",
+ "Movies": "Филмови",
+ "MixedContent": "Мешана содржина",
+ "MessageServerConfigurationUpdated": "Серверската конфигурација беше надградена",
+ "MessageNamedServerConfigurationUpdatedWithValue": "Секцијата на конфигурација на сервер {0} беше надоградена",
+ "MessageApplicationUpdatedTo": "Jellyfin беше надограден до {0}",
+ "MessageApplicationUpdated": "Jellyfin Серверот беше надограден",
+ "Latest": "Последно",
+ "LabelRunningTimeValue": "Време на работа: {0}",
+ "LabelIpAddressValue": "ИП Адреса: {0}",
+ "ItemRemovedWithName": "{0} е избришано до Библиотеката",
+ "ItemAddedWithName": "{0} беше додадено во Библиотеката",
+ "Inherit": "Следно",
+ "HomeVideos": "Домашни Видеа",
+ "HeaderRecordingGroups": "Групи на снимање",
+ "HeaderNextUp": "Следно",
+ "HeaderLiveTV": "ТВ",
+ "HeaderFavoriteSongs": "Омилени Песни",
+ "HeaderFavoriteShows": "Омилени Серии",
+ "HeaderFavoriteEpisodes": "Омилени Епизоди",
+ "HeaderFavoriteArtists": "Омилени Изведувачи",
+ "HeaderFavoriteAlbums": "Омилени Албуми",
+ "HeaderContinueWatching": "Продолжи со гледање",
+ "HeaderCameraUploads": "Поставувања од камера",
+ "HeaderAlbumArtists": "Изведувачи од Албуми",
+ "Genres": "Жанрови",
+ "Folders": "Папки",
+ "Favorites": "Омилени",
+ "FailedLoginAttemptWithUserName": "Неуспешно поврзување од {0}",
+ "DeviceOnlineWithName": "{0} е приклучен",
+ "DeviceOfflineWithName": "{0} се исклучи",
+ "Collections": "Колекции",
+ "ChapterNameValue": "Дел {0}",
+ "Channels": "Канали",
+ "CameraImageUploadedFrom": "Нова слика од камера беше поставена од {0}",
+ "Books": "Книги",
+ "AuthenticationSucceededWithUserName": "{0} успешно поврзан",
+ "Artists": "Изведувач",
+ "Application": "Апликација",
+ "AppDeviceValues": "Аплиакција: {0}, Уред: {1}",
+ "Albums": "Албуми",
+ "VersionNumber": "Верзија {0}",
+ "ValueSpecialEpisodeName": "Специјално - {0}",
+ "ValueHasBeenAddedToLibrary": "{0} е додадено во твојата библиотека",
+ "UserStoppedPlayingItemWithValues": "{0} заврши со репродукција {1} во {2}",
+ "UserStartedPlayingItemWithValues": "{0} пушти {1} на {2}",
+ "UserPolicyUpdatedWithName": "Полисата на користење беше надоградена за {0}",
+ "UserPasswordChangedWithName": "Лозинката е сменета за корисникот {0}",
+ "UserOnlineFromDevice": "{0} е приклучен од {1}",
+ "UserOfflineFromDevice": "{0} е дисконектиран од {1}",
+ "UserLockedOutWithName": "Корисникот {0} е заклучен",
+ "UserDownloadingItemWithValues": "{0} се спушта {1}",
+ "UserDeletedWithName": "Корисникот {0} е избришан",
+ "UserCreatedWithName": "Корисникот {0} е креиран",
+ "User": "Корисник",
+ "TvShows": "ТВ Серии",
+ "System": "Систем",
+ "Sync": "Синхронизација",
+ "SubtitlesDownloadedForItem": "Спуштање превод за {0}",
+ "SubtitleDownloadFailureFromForItem": "Преводот неуспешно се спушти од {0} за {1}",
+ "StartupEmbyServerIsLoading": "Jellyfin Server се пушта. Ве молиме причекајте.",
+ "Songs": "Песни",
+ "Shows": "Серии",
+ "ServerNameNeedsToBeRestarted": "{0} треба да се рестартира",
+ "ScheduledTaskStartedWithName": "{0} започна"
+}
diff --git a/Emby.Server.Implementations/Localization/Core/ms.json b/Emby.Server.Implementations/Localization/Core/ms.json
index c10fbe58f..1d86257f8 100644
--- a/Emby.Server.Implementations/Localization/Core/ms.json
+++ b/Emby.Server.Implementations/Localization/Core/ms.json
@@ -1,23 +1,23 @@
{
"Albums": "Album-album",
- "AppDeviceValues": "App: {0}, Device: {1}",
- "Application": "Application",
- "Artists": "Artis-artis",
- "AuthenticationSucceededWithUserName": "{0} successfully authenticated",
+ "AppDeviceValues": "Apl: {0}, Peranti: {1}",
+ "Application": "Aplikasi",
+ "Artists": "Artis",
+ "AuthenticationSucceededWithUserName": "{0} berjaya disahkan",
"Books": "Buku-buku",
"CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
- "Channels": "Channels",
+ "Channels": "Saluran",
"ChapterNameValue": "Chapter {0}",
- "Collections": "Collections",
+ "Collections": "Koleksi",
"DeviceOfflineWithName": "{0} has disconnected",
"DeviceOnlineWithName": "{0} is connected",
- "FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
+ "FailedLoginAttemptWithUserName": "Cubaan log masuk gagal dari {0}",
"Favorites": "Favorites",
"Folders": "Folders",
- "Genres": "Genres",
+ "Genres": "Genre-genre",
"HeaderAlbumArtists": "Album Artists",
- "HeaderCameraUploads": "Camera Uploads",
- "HeaderContinueWatching": "Continue Watching",
+ "HeaderCameraUploads": "Muatnaik Kamera",
+ "HeaderContinueWatching": "Terus Menonton",
"HeaderFavoriteAlbums": "Favorite Albums",
"HeaderFavoriteArtists": "Favorite Artists",
"HeaderFavoriteEpisodes": "Favorite Episodes",
@@ -30,7 +30,7 @@
"Inherit": "Inherit",
"ItemAddedWithName": "{0} was added to the library",
"ItemRemovedWithName": "{0} was removed from the library",
- "LabelIpAddressValue": "Ip address: {0}",
+ "LabelIpAddressValue": "Alamat IP: {0}",
"LabelRunningTimeValue": "Running time: {0}",
"Latest": "Latest",
"MessageApplicationUpdated": "Jellyfin Server has been updated",
@@ -39,8 +39,8 @@
"MessageServerConfigurationUpdated": "Server configuration has been updated",
"MixedContent": "Mixed content",
"Movies": "Movies",
- "Music": "Music",
- "MusicVideos": "Music videos",
+ "Music": "Muzik",
+ "MusicVideos": "Video muzik",
"NameInstallFailed": "{0} installation failed",
"NameSeasonNumber": "Season {0}",
"NameSeasonUnknown": "Season Unknown",
@@ -50,7 +50,7 @@
"NotificationOptionAudioPlayback": "Audio playback started",
"NotificationOptionAudioPlaybackStopped": "Audio playback stopped",
"NotificationOptionCameraImageUploaded": "Camera image uploaded",
- "NotificationOptionInstallationFailed": "Installation failure",
+ "NotificationOptionInstallationFailed": "Pemasangan gagal",
"NotificationOptionNewLibraryContent": "New content added",
"NotificationOptionPluginError": "Plugin failure",
"NotificationOptionPluginInstalled": "Plugin installed",
@@ -68,8 +68,8 @@
"PluginUninstalledWithName": "{0} was uninstalled",
"PluginUpdatedWithName": "{0} was updated",
"ProviderValue": "Provider: {0}",
- "ScheduledTaskFailedWithName": "{0} failed",
- "ScheduledTaskStartedWithName": "{0} started",
+ "ScheduledTaskFailedWithName": "{0} gagal",
+ "ScheduledTaskStartedWithName": "{0} bermula",
"ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
"Shows": "Series",
"Songs": "Songs",
@@ -78,7 +78,7 @@
"SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
"SubtitlesDownloadedForItem": "Subtitles downloaded for {0}",
"Sync": "Sync",
- "System": "System",
+ "System": "Sistem",
"TvShows": "TV Shows",
"User": "User",
"UserCreatedWithName": "User {0} has been created",
@@ -92,6 +92,6 @@
"UserStartedPlayingItemWithValues": "{0} is playing {1} on {2}",
"UserStoppedPlayingItemWithValues": "{0} has finished playing {1} on {2}",
"ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
- "ValueSpecialEpisodeName": "Special - {0}",
- "VersionNumber": "Version {0}"
+ "ValueSpecialEpisodeName": "Khas - {0}",
+ "VersionNumber": "Versi {0}"
}
diff --git a/Emby.Server.Implementations/Localization/Core/nb.json b/Emby.Server.Implementations/Localization/Core/nb.json
index 7d4b2bdac..f9fa1b68c 100644
--- a/Emby.Server.Implementations/Localization/Core/nb.json
+++ b/Emby.Server.Implementations/Localization/Core/nb.json
@@ -62,7 +62,7 @@
"NotificationOptionVideoPlayback": "Videoavspilling startet",
"NotificationOptionVideoPlaybackStopped": "Videoavspilling stoppet",
"Photos": "Bilder",
- "Playlists": "Spliielister",
+ "Playlists": "Spillelister",
"Plugin": "Plugin",
"PluginInstalledWithName": "{0} ble installert",
"PluginUninstalledWithName": "{0} ble avinstallert",
diff --git a/Emby.Server.Implementations/Localization/Core/nl.json b/Emby.Server.Implementations/Localization/Core/nl.json
index 4423b7f98..e22f95ab4 100644
--- a/Emby.Server.Implementations/Localization/Core/nl.json
+++ b/Emby.Server.Implementations/Localization/Core/nl.json
@@ -3,13 +3,13 @@
"AppDeviceValues": "App: {0}, Apparaat: {1}",
"Application": "Applicatie",
"Artists": "Artiesten",
- "AuthenticationSucceededWithUserName": "{0} is succesvol geverifieerd",
+ "AuthenticationSucceededWithUserName": "{0} succesvol geauthenticeerd",
"Books": "Boeken",
"CameraImageUploadedFrom": "Er is een nieuwe foto toegevoegd van {0}",
"Channels": "Kanalen",
"ChapterNameValue": "Hoofdstuk {0}",
"Collections": "Verzamelingen",
- "DeviceOfflineWithName": "{0} heeft de verbinding verbroken",
+ "DeviceOfflineWithName": "Verbinding met {0} is verbroken",
"DeviceOnlineWithName": "{0} is verbonden",
"FailedLoginAttemptWithUserName": "Mislukte aanmeld poging van {0}",
"Favorites": "Favorieten",
diff --git a/Emby.Server.Implementations/Localization/Core/nn.json b/Emby.Server.Implementations/Localization/Core/nn.json
new file mode 100644
index 000000000..ec6da213f
--- /dev/null
+++ b/Emby.Server.Implementations/Localization/Core/nn.json
@@ -0,0 +1,40 @@
+{
+ "MessageServerConfigurationUpdated": "Tenar konfigurasjonen har blitt oppdatert",
+ "MessageNamedServerConfigurationUpdatedWithValue": "Tenar konfigurasjon seksjon {0} har blitt oppdatert",
+ "MessageApplicationUpdatedTo": "Jellyfin Tenaren har blitt oppdatert til {0}",
+ "MessageApplicationUpdated": "Jellyfin Tenaren har blitt oppdatert",
+ "Latest": "Nyaste",
+ "LabelRunningTimeValue": "Speletid: {0}",
+ "LabelIpAddressValue": "IP adresse: {0}",
+ "ItemRemovedWithName": "{0} vart fjerna frå biblioteket",
+ "ItemAddedWithName": "{0} vart lagt til i biblioteket",
+ "Inherit": "Arv",
+ "HomeVideos": "Heime Videoar",
+ "HeaderRecordingGroups": "Innspelingsgrupper",
+ "HeaderNextUp": "Neste",
+ "HeaderLiveTV": "Direkte TV",
+ "HeaderFavoriteSongs": "Favoritt Songar",
+ "HeaderFavoriteShows": "Favoritt Seriar",
+ "HeaderFavoriteEpisodes": "Favoritt Episodar",
+ "HeaderFavoriteArtists": "Favoritt Artistar",
+ "HeaderFavoriteAlbums": "Favoritt Album",
+ "HeaderContinueWatching": "Fortsett å sjå",
+ "HeaderCameraUploads": "Kamera Opplastingar",
+ "HeaderAlbumArtists": "Album Artist",
+ "Genres": "Sjangrar",
+ "Folders": "Mapper",
+ "Favorites": "Favorittar",
+ "FailedLoginAttemptWithUserName": "Mislukka påloggingsforsøk frå {0}",
+ "DeviceOnlineWithName": "{0} er tilkopla",
+ "DeviceOfflineWithName": "{0} har kopla frå",
+ "Collections": "Samlingar",
+ "ChapterNameValue": "Kapittel {0}",
+ "Channels": "Kanalar",
+ "CameraImageUploadedFrom": "Eit nytt kamera bilete har blitt lasta opp frå {0}",
+ "Books": "Bøker",
+ "AuthenticationSucceededWithUserName": "{0} Har logga inn",
+ "Artists": "Artistar",
+ "Application": "Program",
+ "AppDeviceValues": "App: {0}, Einheit: {1}",
+ "Albums": "Album"
+}
diff --git a/Emby.Server.Implementations/Localization/Core/pt.json b/Emby.Server.Implementations/Localization/Core/pt.json
index ef8d988c8..9ee3c37a8 100644
--- a/Emby.Server.Implementations/Localization/Core/pt.json
+++ b/Emby.Server.Implementations/Localization/Core/pt.json
@@ -1,5 +1,5 @@
{
- "HeaderLiveTV": "TV ao Vivo",
+ "HeaderLiveTV": "TV em Directo",
"Collections": "Colecções",
"Books": "Livros",
"Artists": "Artistas",
@@ -10,13 +10,13 @@
"HeaderFavoriteAlbums": "Álbuns Favoritos",
"HeaderFavoriteEpisodes": "Episódios Favoritos",
"HeaderFavoriteShows": "Séries Favoritas",
- "HeaderContinueWatching": "Continuar a Ver",
+ "HeaderContinueWatching": "Continuar a Assistir",
"HeaderAlbumArtists": "Artistas do Álbum",
"Genres": "Géneros",
- "Folders": "Pastas",
+ "Folders": "Directórios",
"Favorites": "Favoritos",
"Channels": "Canais",
- "UserDownloadingItemWithValues": "{0} está a transferir {1}",
+ "UserDownloadingItemWithValues": "{0} está a ser transferido {1}",
"VersionNumber": "Versão {0}",
"ValueHasBeenAddedToLibrary": "{0} foi adicionado à sua biblioteca multimédia",
"UserStoppedPlayingItemWithValues": "{0} terminou a reprodução de {1} em {2}",
@@ -24,12 +24,12 @@
"UserPolicyUpdatedWithName": "A política do utilizador {0} foi alterada",
"UserPasswordChangedWithName": "A palavra-passe do utilizador {0} foi alterada",
"UserOnlineFromDevice": "{0} ligou-se a partir de {1}",
- "UserOfflineFromDevice": "{0} desligou-se a partir de {1}",
- "UserLockedOutWithName": "Utilizador {0} bloqueado",
- "UserDeletedWithName": "Utilizador {0} removido",
- "UserCreatedWithName": "Utilizador {0} criado",
+ "UserOfflineFromDevice": "{0} desconectou-se a partir de {1}",
+ "UserLockedOutWithName": "O utilizador {0} foi bloqueado",
+ "UserDeletedWithName": "O utilizador {0} foi removido",
+ "UserCreatedWithName": "O utilizador {0} foi criado",
"User": "Utilizador",
- "TvShows": "Programas",
+ "TvShows": "Séries",
"System": "Sistema",
"SubtitlesDownloadedForItem": "Legendas transferidas para {0}",
"SubtitleDownloadFailureFromForItem": "Falha na transferência de legendas de {0} para {1}",
@@ -38,22 +38,22 @@
"ScheduledTaskStartedWithName": "{0} iniciou",
"ScheduledTaskFailedWithName": "{0} falhou",
"ProviderValue": "Fornecedor: {0}",
- "PluginUpdatedWithName": "{0} foi actualizado",
+ "PluginUpdatedWithName": "{0} foi atualizado",
"PluginUninstalledWithName": "{0} foi desinstalado",
"PluginInstalledWithName": "{0} foi instalado",
- "Plugin": "Extensão",
+ "Plugin": "Plugin",
"NotificationOptionVideoPlaybackStopped": "Reprodução de vídeo parada",
"NotificationOptionVideoPlayback": "Reprodução de vídeo iniciada",
"NotificationOptionUserLockedOut": "Utilizador bloqueado",
"NotificationOptionTaskFailed": "Falha em tarefa agendada",
"NotificationOptionServerRestartRequired": "É necessário reiniciar o servidor",
- "NotificationOptionPluginUpdateInstalled": "Extensão actualizada",
- "NotificationOptionPluginUninstalled": "Extensão desinstalada",
- "NotificationOptionPluginInstalled": "Extensão instalada",
- "NotificationOptionPluginError": "Falha na extensão",
+ "NotificationOptionPluginUpdateInstalled": "Plugin actualizado",
+ "NotificationOptionPluginUninstalled": "Plugin desinstalado",
+ "NotificationOptionPluginInstalled": "Plugin instalado",
+ "NotificationOptionPluginError": "Falha no plugin",
"NotificationOptionNewLibraryContent": "Novo conteúdo adicionado",
"NotificationOptionInstallationFailed": "Falha de instalação",
- "NotificationOptionCameraImageUploaded": "Imagem da câmara enviada",
+ "NotificationOptionCameraImageUploaded": "Imagem de câmara enviada",
"NotificationOptionAudioPlaybackStopped": "Reprodução Parada",
"NotificationOptionAudioPlayback": "Reprodução Iniciada",
"NotificationOptionApplicationUpdateInstalled": "A actualização da aplicação foi instalada",
@@ -66,30 +66,30 @@
"Music": "Música",
"MixedContent": "Conteúdo Misto",
"MessageServerConfigurationUpdated": "A configuração do servidor foi actualizada",
- "MessageNamedServerConfigurationUpdatedWithValue": "Configurações do servidor na secção {0} foram atualizadas",
- "MessageApplicationUpdatedTo": "O servidor Jellyfin foi actualizado para a versão {0}",
+ "MessageNamedServerConfigurationUpdatedWithValue": "As configurações do servidor na secção {0} foram atualizadas",
+ "MessageApplicationUpdatedTo": "O servidor Jellyfin foi atualizado para a versão {0}",
"MessageApplicationUpdated": "O servidor Jellyfin foi actualizado",
"Latest": "Mais Recente",
"LabelRunningTimeValue": "Duração: {0}",
- "LabelIpAddressValue": "Endereço IP: {0}",
+ "LabelIpAddressValue": "Endereço de IP: {0}",
"ItemRemovedWithName": "{0} foi removido da biblioteca",
"ItemAddedWithName": "{0} foi adicionado à biblioteca",
"Inherit": "Herdar",
"HomeVideos": "Vídeos Caseiros",
"HeaderRecordingGroups": "Grupos de Gravação",
- "ValueSpecialEpisodeName": "Especial - {0}",
+ "ValueSpecialEpisodeName": "Episódio Especial - {0}",
"Sync": "Sincronização",
"Songs": "Músicas",
"Shows": "Séries",
"Playlists": "Listas de Reprodução",
"Photos": "Fotografias",
"Movies": "Filmes",
- "HeaderCameraUploads": "Envios a partir da câmara",
- "FailedLoginAttemptWithUserName": "Tentativa de ligação a partir de {0} falhou",
- "DeviceOnlineWithName": "{0} ligou-se",
- "DeviceOfflineWithName": "{0} desligou-se",
+ "HeaderCameraUploads": "Carregamentos a partir da câmara",
+ "FailedLoginAttemptWithUserName": "Tentativa de ligação falhada a partir de {0}",
+ "DeviceOnlineWithName": "{0} está connectado",
+ "DeviceOfflineWithName": "{0} desconectou-se",
"ChapterNameValue": "Capítulo {0}",
- "CameraImageUploadedFrom": "Uma nova imagem de câmara foi enviada a partir de {0}",
+ "CameraImageUploadedFrom": "Uma nova imagem da câmara foi enviada a partir de {0}",
"AuthenticationSucceededWithUserName": "{0} autenticado com sucesso",
"Application": "Aplicação",
"AppDeviceValues": "Aplicação {0}, Dispositivo: {1}"
diff --git a/Emby.Server.Implementations/Localization/Core/ro.json b/Emby.Server.Implementations/Localization/Core/ro.json
index b871626f0..71bffffc6 100644
--- a/Emby.Server.Implementations/Localization/Core/ro.json
+++ b/Emby.Server.Implementations/Localization/Core/ro.json
@@ -65,8 +65,8 @@
"LabelIpAddressValue": "Adresa IP: {0}",
"ItemRemovedWithName": "{0} a fost eliminat din bibliotecă",
"ItemAddedWithName": "{0} a fost adăugat în bibliotecă",
- "Inherit": "moștenit",
- "HomeVideos": "Videoclipuri personale",
+ "Inherit": "Moștenit",
+ "HomeVideos": "Filme personale",
"HeaderRecordingGroups": "Grupuri de înregistrare",
"HeaderLiveTV": "TV în Direct",
"HeaderFavoriteSongs": "Melodii Favorite",
diff --git a/Emby.Server.Implementations/Localization/Core/sk.json b/Emby.Server.Implementations/Localization/Core/sk.json
index 9bac305a2..1988bda52 100644
--- a/Emby.Server.Implementations/Localization/Core/sk.json
+++ b/Emby.Server.Implementations/Localization/Core/sk.json
@@ -15,7 +15,7 @@
"Favorites": "Obľúbené",
"Folders": "Priečinky",
"Genres": "Žánre",
- "HeaderAlbumArtists": "Albumy umelcov",
+ "HeaderAlbumArtists": "Umelci albumu",
"HeaderCameraUploads": "Nahrané fotografie",
"HeaderContinueWatching": "Pokračovať v pozeraní",
"HeaderFavoriteAlbums": "Obľúbené albumy",
@@ -45,25 +45,25 @@
"NameSeasonNumber": "Séria {0}",
"NameSeasonUnknown": "Neznáma séria",
"NewVersionIsAvailable": "Nová verzia Jellyfin Serveru je dostupná na stiahnutie.",
- "NotificationOptionApplicationUpdateAvailable": "Aktualizácia aplikácie je dostupná",
- "NotificationOptionApplicationUpdateInstalled": "Aktualizácia aplikácie nainštalovaná",
- "NotificationOptionAudioPlayback": "Prehrávanie audia bolo spustené",
- "NotificationOptionAudioPlaybackStopped": "Prehrávanie audia bolo zastavené",
+ "NotificationOptionApplicationUpdateAvailable": "Je dostupná aktualizácia aplikácie",
+ "NotificationOptionApplicationUpdateInstalled": "Aktualizácia aplikácie bola nainštalovaná",
+ "NotificationOptionAudioPlayback": "Prehrávanie zvuku bolo spustené",
+ "NotificationOptionAudioPlaybackStopped": "Prehrávanie zvuku bolo zastavené",
"NotificationOptionCameraImageUploaded": "Obrázok z fotoaparátu bol nahraný",
"NotificationOptionInstallationFailed": "Chyba inštalácie",
- "NotificationOptionNewLibraryContent": "Nový obsah bol pridaný",
- "NotificationOptionPluginError": "Chyba rozšírenia",
- "NotificationOptionPluginInstalled": "Rozšírenie nainštalované",
- "NotificationOptionPluginUninstalled": "Rozšírenie odinštalované",
- "NotificationOptionPluginUpdateInstalled": "Aktualizácia rozšírenia nainštalovaná",
+ "NotificationOptionNewLibraryContent": "Bol pridaný nový obsah",
+ "NotificationOptionPluginError": "Chyba zásuvného modulu",
+ "NotificationOptionPluginInstalled": "Bol nainštalovaný zásuvný modul",
+ "NotificationOptionPluginUninstalled": "Zásuvný modul bol odinštalovaný",
+ "NotificationOptionPluginUpdateInstalled": "Bola nainštalovaná aktualizácia zásuvného modulu",
"NotificationOptionServerRestartRequired": "Vyžaduje sa reštart servera",
"NotificationOptionTaskFailed": "Naplánovaná úloha zlyhala",
"NotificationOptionUserLockedOut": "Používateľ je uzamknutý",
"NotificationOptionVideoPlayback": "Spustené prehrávanie videa",
"NotificationOptionVideoPlaybackStopped": "Zastavené prehrávanie videa",
"Photos": "Fotky",
- "Playlists": "Zoznamy skladieb",
- "Plugin": "Rozšírenie",
+ "Playlists": "Playlisty",
+ "Plugin": "Zásuvný modul",
"PluginInstalledWithName": "{0} bol nainštalovaný",
"PluginUninstalledWithName": "{0} bol odinštalovaný",
"PluginUpdatedWithName": "{0} bol aktualizovaný",
@@ -72,8 +72,8 @@
"ScheduledTaskStartedWithName": "{0} zahájených",
"ServerNameNeedsToBeRestarted": "{0} vyžaduje reštart",
"Shows": "Seriály",
- "Songs": "Skladby",
- "StartupEmbyServerIsLoading": "Jellyfin Server sa spúšťa. Skúste to prosím o chvíľu znova.",
+ "Songs": "Piesne",
+ "StartupEmbyServerIsLoading": "Jellyfin Server sa spúšťa. Prosím, skúste to o chvíľu znova.",
"SubtitleDownloadFailureForItem": "Sťahovanie titulkov pre {0} zlyhalo",
"SubtitleDownloadFailureFromForItem": "Sťahovanie titulkov z {0} pre {1} zlyhalo",
"SubtitlesDownloadedForItem": "Titulky pre {0} stiahnuté",
@@ -87,11 +87,11 @@
"UserLockedOutWithName": "Používateľ {0} bol vymknutý",
"UserOfflineFromDevice": "{0} sa odpojil od {1}",
"UserOnlineFromDevice": "{0} je online z {1}",
- "UserPasswordChangedWithName": "Heslo používateľa {0} zmenené",
+ "UserPasswordChangedWithName": "Heslo používateľa {0} bolo zmenené",
"UserPolicyUpdatedWithName": "Používateľské zásady pre {0} boli aktualizované",
- "UserStartedPlayingItemWithValues": "{0} spustil prehrávanie {1}",
- "UserStoppedPlayingItemWithValues": "{0} zastavil prehrávanie {1}",
- "ValueHasBeenAddedToLibrary": "{0} bolo pridané do vašej knižnice médií",
+ "UserStartedPlayingItemWithValues": "{0} spustil prehrávanie {1} na {2}",
+ "UserStoppedPlayingItemWithValues": "{0} ukončil prehrávanie {1} na {2}",
+ "ValueHasBeenAddedToLibrary": "{0} bol pridané do vašej knižnice médií",
"ValueSpecialEpisodeName": "Špeciál - {0}",
"VersionNumber": "Verzia {0}"
}
diff --git a/Emby.Server.Implementations/Localization/Core/sl-SI.json b/Emby.Server.Implementations/Localization/Core/sl-SI.json
index c141a40f6..0fc8379de 100644
--- a/Emby.Server.Implementations/Localization/Core/sl-SI.json
+++ b/Emby.Server.Implementations/Localization/Core/sl-SI.json
@@ -12,7 +12,7 @@
"DeviceOfflineWithName": "{0} je prekinil povezavo",
"DeviceOnlineWithName": "{0} je povezan",
"FailedLoginAttemptWithUserName": "Neuspešen poskus prijave z {0}",
- "Favorites": "Priljubljeni",
+ "Favorites": "Priljubljeno",
"Folders": "Mape",
"Genres": "Zvrsti",
"HeaderAlbumArtists": "Izvajalci albuma",
@@ -40,7 +40,7 @@
"MixedContent": "Razne vsebine",
"Movies": "Filmi",
"Music": "Glasba",
- "MusicVideos": "Glasbeni posnetki",
+ "MusicVideos": "Glasbeni videi",
"NameInstallFailed": "{0} namestitev neuspešna",
"NameSeasonNumber": "Sezona {0}",
"NameSeasonUnknown": "Season neznana",
diff --git a/Emby.Server.Implementations/Localization/Core/sr.json b/Emby.Server.Implementations/Localization/Core/sr.json
new file mode 100644
index 000000000..da0088991
--- /dev/null
+++ b/Emby.Server.Implementations/Localization/Core/sr.json
@@ -0,0 +1,96 @@
+{
+ "UserPolicyUpdatedWithName": "Корисничке смернице ажуриране за {0}",
+ "NotificationOptionUserLockedOut": "Корисник закључан",
+ "VersionNumber": "Верзија {0}",
+ "ValueSpecialEpisodeName": "Специјал - {0}",
+ "ValueHasBeenAddedToLibrary": "{0} је додато у вашу медијску библиотеку",
+ "UserStoppedPlayingItemWithValues": "{0} заврши пуштање {1} на {2}",
+ "UserStartedPlayingItemWithValues": "{0} пушта {1} на {2}",
+ "UserPasswordChangedWithName": "Лозинка је промењена за корисника {0}",
+ "UserOnlineFromDevice": "{0} је на вези од {1}",
+ "UserOfflineFromDevice": "{0} се одвезао са {1}",
+ "UserLockedOutWithName": "Корисник {0} је закључан",
+ "UserDownloadingItemWithValues": "{0} преузима {1}",
+ "UserDeletedWithName": "Корисник {0} је обрисан",
+ "UserCreatedWithName": "Корисник {0} је направљен",
+ "User": "Корисник",
+ "TvShows": "ТВ серије",
+ "System": "Систем",
+ "Sync": "Усклади",
+ "SubtitlesDownloadedForItem": "Титлови преузети за {0}",
+ "SubtitleDownloadFailureFromForItem": "Неуспело преузимање титлова за {1} са {0}",
+ "StartupEmbyServerIsLoading": "Џелифин сервер се подиже. Покушајте поново убрзо.",
+ "Songs": "Песме",
+ "Shows": "Серије",
+ "ServerNameNeedsToBeRestarted": "{0} треба поново покренути",
+ "ScheduledTaskStartedWithName": "{0} покренуто",
+ "ScheduledTaskFailedWithName": "{0} неуспело",
+ "ProviderValue": "Пружалац: {0}",
+ "PluginUpdatedWithName": "{0} ажуриран",
+ "PluginUninstalledWithName": "{0} деинсталиран",
+ "PluginInstalledWithName": "{0} инсталиран",
+ "Plugin": "Прикључак",
+ "Playlists": "Листе",
+ "Photos": "Фотографије",
+ "NotificationOptionVideoPlaybackStopped": "Заустављено пуштање видеа",
+ "NotificationOptionVideoPlayback": "Покренуто пуштање видеа",
+ "NotificationOptionTaskFailed": "Неуспешан заказани посао",
+ "NotificationOptionServerRestartRequired": "Потребан је рестарт сервера",
+ "NotificationOptionPluginUpdateInstalled": "Ажурирање прикључка инсталирано",
+ "NotificationOptionPluginUninstalled": "Прикључак уклоњен",
+ "NotificationOptionPluginInstalled": "Прикључак инсталиран",
+ "NotificationOptionPluginError": "Грешка прикључка",
+ "NotificationOptionNewLibraryContent": "Додат нови садржај",
+ "NotificationOptionInstallationFailed": "Неуспела инсталација",
+ "NotificationOptionCameraImageUploaded": "Слика са камере послата",
+ "NotificationOptionAudioPlaybackStopped": "Заустављено пуштање звука",
+ "NotificationOptionAudioPlayback": "Покренуто пуштање звука",
+ "NotificationOptionApplicationUpdateInstalled": "Ажурирање инсталирано",
+ "NotificationOptionApplicationUpdateAvailable": "Доступно је ажурирање за апликацију",
+ "NewVersionIsAvailable": "Нова верзија Џелифин сервера је доступна за преузимање.",
+ "NameSeasonUnknown": "Непозната сезона",
+ "NameSeasonNumber": "Сезона {0}",
+ "NameInstallFailed": "Инсталација {0} није успела",
+ "MusicVideos": "Музички спотови",
+ "Music": "Музика",
+ "Movies": "Филмови",
+ "MixedContent": "Мешовит садржај",
+ "MessageServerConfigurationUpdated": "Серверска поставка је ажурирана",
+ "MessageNamedServerConfigurationUpdatedWithValue": "Одељак серверске поставке {0} је ажуриран",
+ "MessageApplicationUpdatedTo": "Џелифин сервер је ажуриран на {0}",
+ "MessageApplicationUpdated": "Џелифин сервер је ажуриран",
+ "Latest": "Последње",
+ "LabelRunningTimeValue": "Време рада: {0}",
+ "LabelIpAddressValue": "ИП адреса: {0}",
+ "ItemRemovedWithName": "{0} уклоњено из библиотеке",
+ "ItemAddedWithName": "{0} додато у библиотеку",
+ "Inherit": "Наследи",
+ "HomeVideos": "Кућни видео",
+ "HeaderRecordingGroups": "Групе снимања",
+ "HeaderNextUp": "Следеће горе",
+ "HeaderLiveTV": "ТВ уживо",
+ "HeaderFavoriteSongs": "Омиљене песме",
+ "HeaderFavoriteShows": "Омиљене серије",
+ "HeaderFavoriteEpisodes": "Омиљене епизоде",
+ "HeaderFavoriteArtists": "Омиљени извођачи",
+ "HeaderFavoriteAlbums": "Омиљени албуми",
+ "HeaderContinueWatching": "Настави гледање",
+ "HeaderCameraUploads": "Слања са камере",
+ "HeaderAlbumArtists": "Извођачи албума",
+ "Genres": "Жанрови",
+ "Folders": "Фасцикле",
+ "Favorites": "Омиљено",
+ "FailedLoginAttemptWithUserName": "Неуспела пријава са {0}",
+ "DeviceOnlineWithName": "{0} се повезао",
+ "DeviceOfflineWithName": "{0} се одвезао",
+ "Collections": "Колекције",
+ "ChapterNameValue": "Поглавље {0}",
+ "Channels": "Канали",
+ "CameraImageUploadedFrom": "Нова фотографија је послата са {0}",
+ "Books": "Књиге",
+ "AuthenticationSucceededWithUserName": "{0} успешно проверено",
+ "Artists": "Извођач",
+ "Application": "Апликација",
+ "AppDeviceValues": "Апл: {0}, уређај: {1}",
+ "Albums": "Албуми"
+}
diff --git a/Emby.Server.Implementations/Localization/Core/sv.json b/Emby.Server.Implementations/Localization/Core/sv.json
index 744b0e2d3..b2934545d 100644
--- a/Emby.Server.Implementations/Localization/Core/sv.json
+++ b/Emby.Server.Implementations/Localization/Core/sv.json
@@ -1,7 +1,7 @@
{
"Albums": "Album",
- "AppDeviceValues": "App: {0}, Enhet: {1}",
- "Application": "App",
+ "AppDeviceValues": "Applikation: {0}, Enhet: {1}",
+ "Application": "Applikation",
"Artists": "Artister",
"AuthenticationSucceededWithUserName": "{0} har autentiserats",
"Books": "Böcker",
@@ -16,15 +16,15 @@
"Folders": "Mappar",
"Genres": "Genrer",
"HeaderAlbumArtists": "Albumartister",
- "HeaderCameraUploads": "Kamera Uppladdningar",
- "HeaderContinueWatching": "Fortsätt kolla på",
+ "HeaderCameraUploads": "Kamerauppladdningar",
+ "HeaderContinueWatching": "Fortsätt kolla",
"HeaderFavoriteAlbums": "Favoritalbum",
"HeaderFavoriteArtists": "Favoritartister",
"HeaderFavoriteEpisodes": "Favoritavsnitt",
"HeaderFavoriteShows": "Favoritserier",
"HeaderFavoriteSongs": "Favoritlåtar",
"HeaderLiveTV": "Live-TV",
- "HeaderNextUp": "Nästa på tur",
+ "HeaderNextUp": "Nästa",
"HeaderRecordingGroups": "Inspelningsgrupper",
"HomeVideos": "Hemvideor",
"Inherit": "Ärv",
@@ -34,9 +34,9 @@
"LabelRunningTimeValue": "Speltid: {0}",
"Latest": "Senaste",
"MessageApplicationUpdated": "Jellyfin Server har uppdaterats",
- "MessageApplicationUpdatedTo": "Jellyfin Server har uppgraderats till {0}",
+ "MessageApplicationUpdatedTo": "Jellyfin Server har uppdaterats till {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "Serverinställningarna {0} har uppdaterats",
- "MessageServerConfigurationUpdated": "Server konfigurationen har uppdaterats",
+ "MessageServerConfigurationUpdated": "Serverkonfigurationen har uppdaterats",
"MixedContent": "Blandat innehåll",
"Movies": "Filmer",
"Music": "Musik",
@@ -44,11 +44,11 @@
"NameInstallFailed": "{0} installationen misslyckades",
"NameSeasonNumber": "Säsong {0}",
"NameSeasonUnknown": "Okänd säsong",
- "NewVersionIsAvailable": "En ny version av Jellyfin Server är klar för nedladdning.",
+ "NewVersionIsAvailable": "En ny version av Jellyfin Server är tillgänglig att hämta.",
"NotificationOptionApplicationUpdateAvailable": "Ny programversion tillgänglig",
"NotificationOptionApplicationUpdateInstalled": "Programuppdatering installerad",
"NotificationOptionAudioPlayback": "Ljuduppspelning har påbörjats",
- "NotificationOptionAudioPlaybackStopped": "Ljuduppspelning stoppad",
+ "NotificationOptionAudioPlaybackStopped": "Ljuduppspelning stoppades",
"NotificationOptionCameraImageUploaded": "Kamerabild har laddats upp",
"NotificationOptionInstallationFailed": "Fel vid installation",
"NotificationOptionNewLibraryContent": "Nytt innehåll har lagts till",
@@ -60,7 +60,7 @@
"NotificationOptionTaskFailed": "Schemalagd aktivitet har misslyckats",
"NotificationOptionUserLockedOut": "Användare har låsts ut",
"NotificationOptionVideoPlayback": "Videouppspelning har påbörjats",
- "NotificationOptionVideoPlaybackStopped": "Videouppspelning stoppad",
+ "NotificationOptionVideoPlaybackStopped": "Videouppspelning stoppades",
"Photos": "Bilder",
"Playlists": "Spellistor",
"Plugin": "Tillägg",
@@ -69,13 +69,13 @@
"PluginUpdatedWithName": "{0} uppdaterades",
"ProviderValue": "Källa: {0}",
"ScheduledTaskFailedWithName": "{0} misslyckades",
- "ScheduledTaskStartedWithName": "{0} startad",
+ "ScheduledTaskStartedWithName": "{0} startades",
"ServerNameNeedsToBeRestarted": "{0} behöver startas om",
"Shows": "Serier",
"Songs": "Låtar",
- "StartupEmbyServerIsLoading": "Jellyfin server arbetar. Pröva igen inom kort.",
+ "StartupEmbyServerIsLoading": "Jellyfin Server arbetar. Pröva igen snart.",
"SubtitleDownloadFailureForItem": "Nerladdning av undertexter för {0} misslyckades",
- "SubtitleDownloadFailureFromForItem": "Undertexter misslyckades att ladda ner {0} för {1}",
+ "SubtitleDownloadFailureFromForItem": "Undertexter kunde inte laddas ner från {0} för {1}",
"SubtitlesDownloadedForItem": "Undertexter har laddats ner till {0}",
"Sync": "Synk",
"System": "System",
@@ -89,9 +89,9 @@
"UserOnlineFromDevice": "{0} är uppkopplad från {1}",
"UserPasswordChangedWithName": "Lösenordet för {0} har ändrats",
"UserPolicyUpdatedWithName": "Användarpolicyn har uppdaterats för {0}",
- "UserStartedPlayingItemWithValues": "{0} har börjat spela upp {1}",
- "UserStoppedPlayingItemWithValues": "{0} har avslutat uppspelningen av {1}",
- "ValueHasBeenAddedToLibrary": "{0} har blivit tillagd till ditt mediabibliotek",
+ "UserStartedPlayingItemWithValues": "{0} spelar upp {1} på {2}",
+ "UserStoppedPlayingItemWithValues": "{0} har avslutat uppspelningen av {1} på {2}",
+ "ValueHasBeenAddedToLibrary": "{0} har lagts till i ditt mediebibliotek",
"ValueSpecialEpisodeName": "Specialavsnitt - {0}",
"VersionNumber": "Version {0}"
}
diff --git a/Emby.Server.Implementations/Localization/Core/tr.json b/Emby.Server.Implementations/Localization/Core/tr.json
index eb1c2623f..d3552225b 100644
--- a/Emby.Server.Implementations/Localization/Core/tr.json
+++ b/Emby.Server.Implementations/Localization/Core/tr.json
@@ -34,8 +34,8 @@
"LabelRunningTimeValue": "Çalışma süresi: {0}",
"Latest": "En son",
"MessageApplicationUpdated": "Jellyfin Sunucusu güncellendi",
- "MessageApplicationUpdatedTo": "Jellyfin Sunucusu {0} olarak güncellendi",
- "MessageNamedServerConfigurationUpdatedWithValue": "Sunucu ayarları kısım {0} güncellendi",
+ "MessageApplicationUpdatedTo": "Jellyfin Sunucusu {0} sürümüne güncellendi",
+ "MessageNamedServerConfigurationUpdatedWithValue": "Sunucu ayar kısmı {0} güncellendi",
"MessageServerConfigurationUpdated": "Sunucu ayarları güncellendi",
"MixedContent": "Karışık içerik",
"Movies": "Filmler",
diff --git a/Emby.Server.Implementations/Localization/Core/zh-CN.json b/Emby.Server.Implementations/Localization/Core/zh-CN.json
index a4d53c57e..85ebce0f5 100644
--- a/Emby.Server.Implementations/Localization/Core/zh-CN.json
+++ b/Emby.Server.Implementations/Localization/Core/zh-CN.json
@@ -3,11 +3,11 @@
"AppDeviceValues": "应用: {0}, 设备: {1}",
"Application": "应用程序",
"Artists": "艺术家",
- "AuthenticationSucceededWithUserName": "{0} 认证成功",
+ "AuthenticationSucceededWithUserName": "{0} 验证成功",
"Books": "书籍",
- "CameraImageUploadedFrom": "已从 {0} 上传了一张新的相片",
+ "CameraImageUploadedFrom": "新的相机图像已从 {0} 上传",
"Channels": "频道",
- "ChapterNameValue": "章节 {0}",
+ "ChapterNameValue": "第 {0} 章",
"Collections": "合集",
"DeviceOfflineWithName": "{0} 已断开",
"DeviceOnlineWithName": "{0} 已连接",
@@ -17,14 +17,14 @@
"Genres": "风格",
"HeaderAlbumArtists": "专辑作家",
"HeaderCameraUploads": "相机上传",
- "HeaderContinueWatching": "继续观看",
- "HeaderFavoriteAlbums": "最爱的专辑",
+ "HeaderContinueWatching": "继续观影",
+ "HeaderFavoriteAlbums": "收藏的专辑",
"HeaderFavoriteArtists": "最爱的艺术家",
"HeaderFavoriteEpisodes": "最爱的剧集",
"HeaderFavoriteShows": "最爱的节目",
"HeaderFavoriteSongs": "最爱的歌曲",
"HeaderLiveTV": "电视直播",
- "HeaderNextUp": "下一步",
+ "HeaderNextUp": "接下来",
"HeaderRecordingGroups": "录制组",
"HomeVideos": "家庭视频",
"Inherit": "继承",
@@ -79,7 +79,7 @@
"SubtitlesDownloadedForItem": "已为 {0} 下载了字幕",
"Sync": "同步",
"System": "系统",
- "TvShows": "电视节目",
+ "TvShows": "电视剧",
"User": "用户",
"UserCreatedWithName": "用户 {0} 已创建",
"UserDeletedWithName": "用户 {0} 已删除",
@@ -89,8 +89,8 @@
"UserOnlineFromDevice": "{0} 在线,来自 {1}",
"UserPasswordChangedWithName": "已为用户 {0} 更改密码",
"UserPolicyUpdatedWithName": "用户协议已经被更新为 {0}",
- "UserStartedPlayingItemWithValues": "{0} 已开始播放 {1}",
- "UserStoppedPlayingItemWithValues": "{0} 已停止播放 {1}",
+ "UserStartedPlayingItemWithValues": "{0} 已在 {2} 上开始播放 {1}",
+ "UserStoppedPlayingItemWithValues": "{0} 已在 {2} 上停止播放 {1}",
"ValueHasBeenAddedToLibrary": "{0} 已添加至您的媒体库中",
"ValueSpecialEpisodeName": "特典 - {0}",
"VersionNumber": "版本 {0}"
diff --git a/Emby.Server.Implementations/Localization/Core/zh-HK.json b/Emby.Server.Implementations/Localization/Core/zh-HK.json
index 33fcb2d37..f3d9e5fce 100644
--- a/Emby.Server.Implementations/Localization/Core/zh-HK.json
+++ b/Emby.Server.Implementations/Localization/Core/zh-HK.json
@@ -1,97 +1,97 @@
{
- "Albums": "Albums",
- "AppDeviceValues": "App: {0}, Device: {1}",
- "Application": "Application",
+ "Albums": "專輯",
+ "AppDeviceValues": "軟體: {0}, 設備: {1}",
+ "Application": "應用程式",
"Artists": "藝人",
- "AuthenticationSucceededWithUserName": "{0} successfully authenticated",
- "Books": "Books",
- "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
- "Channels": "Channels",
- "ChapterNameValue": "Chapter {0}",
- "Collections": "Collections",
- "DeviceOfflineWithName": "{0} has disconnected",
- "DeviceOnlineWithName": "{0} is connected",
- "FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
- "Favorites": "Favorites",
- "Folders": "Folders",
- "Genres": "Genres",
- "HeaderAlbumArtists": "Album Artists",
- "HeaderCameraUploads": "Camera Uploads",
- "HeaderContinueWatching": "Continue Watching",
- "HeaderFavoriteAlbums": "Favorite Albums",
- "HeaderFavoriteArtists": "Favorite Artists",
- "HeaderFavoriteEpisodes": "Favorite Episodes",
- "HeaderFavoriteShows": "Favorite Shows",
- "HeaderFavoriteSongs": "Favorite Songs",
- "HeaderLiveTV": "Live TV",
- "HeaderNextUp": "Next Up",
- "HeaderRecordingGroups": "Recording Groups",
- "HomeVideos": "Home videos",
- "Inherit": "Inherit",
- "ItemAddedWithName": "{0} was added to the library",
- "ItemRemovedWithName": "{0} was removed from the library",
- "LabelIpAddressValue": "Ip address: {0}",
- "LabelRunningTimeValue": "Running time: {0}",
- "Latest": "Latest",
- "MessageApplicationUpdated": "Jellyfin Server has been updated",
- "MessageApplicationUpdatedTo": "Jellyfin Server has been updated to {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated",
- "MessageServerConfigurationUpdated": "Server configuration has been updated",
+ "AuthenticationSucceededWithUserName": "{0} 授權成功",
+ "Books": "圖書",
+ "CameraImageUploadedFrom": "{0} 成功上傳一張新相片",
+ "Channels": "頻道",
+ "ChapterNameValue": "章節 {0}",
+ "Collections": "合輯",
+ "DeviceOfflineWithName": "{0} 已經斷開連結",
+ "DeviceOnlineWithName": "{0} 已經連接",
+ "FailedLoginAttemptWithUserName": "來自 {0} 的失敗登入嘗試",
+ "Favorites": "我的最愛",
+ "Folders": "檔案夾",
+ "Genres": "風格",
+ "HeaderAlbumArtists": "專輯藝術家",
+ "HeaderCameraUploads": "相機上載",
+ "HeaderContinueWatching": "繼續觀看",
+ "HeaderFavoriteAlbums": "最愛專輯",
+ "HeaderFavoriteArtists": "最愛藝術家",
+ "HeaderFavoriteEpisodes": "最愛的劇集",
+ "HeaderFavoriteShows": "最愛的節目",
+ "HeaderFavoriteSongs": "最愛的歌曲",
+ "HeaderLiveTV": "電視直播",
+ "HeaderNextUp": "接下來",
+ "HeaderRecordingGroups": "錄製組",
+ "HomeVideos": "家庭影片",
+ "Inherit": "繼承",
+ "ItemAddedWithName": "{0} 已添加至媒體庫",
+ "ItemRemovedWithName": "{0} 已從媒體庫移除",
+ "LabelIpAddressValue": "IP 地址: {0}",
+ "LabelRunningTimeValue": "運行時間: {0}",
+ "Latest": "最新",
+ "MessageApplicationUpdated": "Jellyfin Server 已更新",
+ "MessageApplicationUpdatedTo": "Jellyfin 伺服器已更新至 {0}",
+ "MessageNamedServerConfigurationUpdatedWithValue": "伺服器設定 {0} 部分已更新",
+ "MessageServerConfigurationUpdated": "伺服器設定已經更新",
"MixedContent": "Mixed content",
- "Movies": "Movies",
- "Music": "Music",
- "MusicVideos": "Music videos",
- "NameInstallFailed": "{0} installation failed",
- "NameSeasonNumber": "Season {0}",
- "NameSeasonUnknown": "Season Unknown",
- "NewVersionIsAvailable": "A new version of Jellyfin Server is available for download.",
- "NotificationOptionApplicationUpdateAvailable": "Application update available",
- "NotificationOptionApplicationUpdateInstalled": "Application update installed",
- "NotificationOptionAudioPlayback": "Audio playback started",
- "NotificationOptionAudioPlaybackStopped": "Audio playback stopped",
- "NotificationOptionCameraImageUploaded": "Camera image uploaded",
- "NotificationOptionInstallationFailed": "Installation failure",
- "NotificationOptionNewLibraryContent": "New content added",
- "NotificationOptionPluginError": "Plugin failure",
- "NotificationOptionPluginInstalled": "Plugin installed",
- "NotificationOptionPluginUninstalled": "Plugin uninstalled",
- "NotificationOptionPluginUpdateInstalled": "Plugin update installed",
- "NotificationOptionServerRestartRequired": "Server restart required",
- "NotificationOptionTaskFailed": "Scheduled task failure",
- "NotificationOptionUserLockedOut": "User locked out",
- "NotificationOptionVideoPlayback": "Video playback started",
- "NotificationOptionVideoPlaybackStopped": "Video playback stopped",
- "Photos": "Photos",
- "Playlists": "Playlists",
+ "Movies": "電影",
+ "Music": "音樂",
+ "MusicVideos": "音樂MV",
+ "NameInstallFailed": "{0} 安裝失敗",
+ "NameSeasonNumber": "第 {0} 季",
+ "NameSeasonUnknown": "未知季數",
+ "NewVersionIsAvailable": "新版本的 Jellyfin 伺服器可供下載。",
+ "NotificationOptionApplicationUpdateAvailable": "有可用的應用程式更新",
+ "NotificationOptionApplicationUpdateInstalled": "應用程式已更新",
+ "NotificationOptionAudioPlayback": "開始播放音頻",
+ "NotificationOptionAudioPlaybackStopped": "已停止播放音頻",
+ "NotificationOptionCameraImageUploaded": "相機相片已上傳",
+ "NotificationOptionInstallationFailed": "安裝失敗",
+ "NotificationOptionNewLibraryContent": "已添加新内容",
+ "NotificationOptionPluginError": "擴充元件錯誤",
+ "NotificationOptionPluginInstalled": "擴充元件已安裝",
+ "NotificationOptionPluginUninstalled": "擴充元件已移除",
+ "NotificationOptionPluginUpdateInstalled": "擴充元件更新已安裝",
+ "NotificationOptionServerRestartRequired": "伺服器需要重啓",
+ "NotificationOptionTaskFailed": "計劃任務失敗",
+ "NotificationOptionUserLockedOut": "用家已鎖定",
+ "NotificationOptionVideoPlayback": "開始播放視頻",
+ "NotificationOptionVideoPlaybackStopped": "已停止播放視頻",
+ "Photos": "相片",
+ "Playlists": "播放清單",
"Plugin": "Plugin",
- "PluginInstalledWithName": "{0} was installed",
- "PluginUninstalledWithName": "{0} was uninstalled",
- "PluginUpdatedWithName": "{0} was updated",
+ "PluginInstalledWithName": "已安裝 {0}",
+ "PluginUninstalledWithName": "已移除 {0}",
+ "PluginUpdatedWithName": "已更新 {0}",
"ProviderValue": "Provider: {0}",
- "ScheduledTaskFailedWithName": "{0} failed",
- "ScheduledTaskStartedWithName": "{0} started",
- "ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
- "Shows": "Shows",
- "Songs": "Songs",
- "StartupEmbyServerIsLoading": "Jellyfin Server is loading. Please try again shortly.",
+ "ScheduledTaskFailedWithName": "{0} 任務失敗",
+ "ScheduledTaskStartedWithName": "{0} 任務開始",
+ "ServerNameNeedsToBeRestarted": "{0} 需要重啓",
+ "Shows": "節目",
+ "Songs": "歌曲",
+ "StartupEmbyServerIsLoading": "Jellyfin 伺服器載入中,請稍後再試。",
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
- "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
- "SubtitlesDownloadedForItem": "Subtitles downloaded for {0}",
- "Sync": "Sync",
+ "SubtitleDownloadFailureFromForItem": "無法從 {0} 下載 {1} 的字幕",
+ "SubtitlesDownloadedForItem": "已為 {0} 下載了字幕",
+ "Sync": "同步",
"System": "System",
- "TvShows": "TV Shows",
+ "TvShows": "電視節目",
"User": "User",
- "UserCreatedWithName": "User {0} has been created",
- "UserDeletedWithName": "User {0} has been deleted",
- "UserDownloadingItemWithValues": "{0} is downloading {1}",
- "UserLockedOutWithName": "User {0} has been locked out",
- "UserOfflineFromDevice": "{0} has disconnected from {1}",
- "UserOnlineFromDevice": "{0} is online from {1}",
- "UserPasswordChangedWithName": "Password has been changed for user {0}",
- "UserPolicyUpdatedWithName": "User policy has been updated for {0}",
- "UserStartedPlayingItemWithValues": "{0} is playing {1} on {2}",
- "UserStoppedPlayingItemWithValues": "{0} has finished playing {1} on {2}",
- "ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
- "ValueSpecialEpisodeName": "Special - {0}",
+ "UserCreatedWithName": "用家 {0} 已創建",
+ "UserDeletedWithName": "用家 {0} 已移除",
+ "UserDownloadingItemWithValues": "{0} 正在下載 {1}",
+ "UserLockedOutWithName": "用家 {0} 已被鎖定",
+ "UserOfflineFromDevice": "{0} 已從 {1} 斷開",
+ "UserOnlineFromDevice": "{0} 已連綫,來自 {1}",
+ "UserPasswordChangedWithName": "用家 {0} 的密碼已變更",
+ "UserPolicyUpdatedWithName": "用戶協議已被更新為 {0}",
+ "UserStartedPlayingItemWithValues": "{0} 正在 {2} 上播放 {1}",
+ "UserStoppedPlayingItemWithValues": "{0} 已在 {2} 上停止播放 {1}",
+ "ValueHasBeenAddedToLibrary": "{0} 已添加到你的媒體庫",
+ "ValueSpecialEpisodeName": "特典 - {0}",
"VersionNumber": "版本{0}"
}
diff --git a/Emby.Server.Implementations/Localization/Core/zh-TW.json b/Emby.Server.Implementations/Localization/Core/zh-TW.json
index 33bdbfb98..acd211f22 100644
--- a/Emby.Server.Implementations/Localization/Core/zh-TW.json
+++ b/Emby.Server.Implementations/Localization/Core/zh-TW.json
@@ -50,10 +50,10 @@
"NotificationOptionCameraImageUploaded": "相機相片已上傳",
"NotificationOptionInstallationFailed": "安裝失敗",
"NotificationOptionNewLibraryContent": "已新增新內容",
- "NotificationOptionPluginError": "外掛失敗",
- "NotificationOptionPluginInstalled": "外掛已安裝",
- "NotificationOptionPluginUninstalled": "外掛已移除",
- "NotificationOptionPluginUpdateInstalled": "已更新外掛",
+ "NotificationOptionPluginError": "擴充元件錯誤",
+ "NotificationOptionPluginInstalled": "擴充元件已安裝",
+ "NotificationOptionPluginUninstalled": "擴充元件已移除",
+ "NotificationOptionPluginUpdateInstalled": "已更新擴充元件",
"NotificationOptionServerRestartRequired": "伺服器需要重新啟動",
"NotificationOptionTaskFailed": "排程任務失敗",
"NotificationOptionUserLockedOut": "使用者已鎖定",
diff --git a/Emby.Server.Implementations/Net/SocketFactory.cs b/Emby.Server.Implementations/Net/SocketFactory.cs
index 0870db003..e42ff8496 100644
--- a/Emby.Server.Implementations/Net/SocketFactory.cs
+++ b/Emby.Server.Implementations/Net/SocketFactory.cs
@@ -1,5 +1,4 @@
using System;
-using System.IO;
using System.Net;
using System.Net.Sockets;
using MediaBrowser.Model.Net;
@@ -8,32 +7,6 @@ namespace Emby.Server.Implementations.Net
{
public class SocketFactory : ISocketFactory
{
- /// <summary>
- /// Creates a new UDP acceptSocket and binds it to the specified local port.
- /// </summary>
- /// <param name="localPort">An integer specifying the local port to bind the acceptSocket to.</param>
- public ISocket CreateUdpSocket(int localPort)
- {
- if (localPort < 0)
- {
- throw new ArgumentException("localPort cannot be less than zero.", nameof(localPort));
- }
-
- var retVal = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
-
- try
- {
- retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
- return new UdpSocket(retVal, localPort, IPAddress.Any);
- }
- catch
- {
- retVal?.Dispose();
-
- throw;
- }
- }
-
public ISocket CreateUdpBroadcastSocket(int localPort)
{
if (localPort < 0)
@@ -156,8 +129,5 @@ namespace Emby.Server.Implementations.Net
throw;
}
}
-
- public Stream CreateNetworkStream(ISocket socket, bool ownsSocket)
- => new NetworkStream(((UdpSocket)socket).Socket, ownsSocket);
}
}
diff --git a/Emby.Server.Implementations/Net/UdpSocket.cs b/Emby.Server.Implementations/Net/UdpSocket.cs
index dde4a2a34..211ca6784 100644
--- a/Emby.Server.Implementations/Net/UdpSocket.cs
+++ b/Emby.Server.Implementations/Net/UdpSocket.cs
@@ -181,15 +181,6 @@ namespace Emby.Server.Implementations.Net
return taskCompletion.Task;
}
- public Task<SocketReceiveResult> ReceiveAsync(CancellationToken cancellationToken)
- {
- ThrowIfDisposed();
-
- var buffer = new byte[8192];
-
- return ReceiveAsync(buffer, 0, buffer.Length, cancellationToken);
- }
-
public Task SendToAsync(byte[] buffer, int offset, int size, IPEndPoint endPoint, CancellationToken cancellationToken)
{
ThrowIfDisposed();
diff --git a/Emby.Server.Implementations/Net/WebSocketConnectEventArgs.cs b/Emby.Server.Implementations/Net/WebSocketConnectEventArgs.cs
index e3047d392..6880766f9 100644
--- a/Emby.Server.Implementations/Net/WebSocketConnectEventArgs.cs
+++ b/Emby.Server.Implementations/Net/WebSocketConnectEventArgs.cs
@@ -1,6 +1,4 @@
using System;
-using System.Net.WebSockets;
-using MediaBrowser.Model.Services;
using Microsoft.AspNetCore.Http;
namespace Emby.Server.Implementations.Net
diff --git a/Emby.Server.Implementations/Playlists/PlaylistImageProvider.cs b/Emby.Server.Implementations/Playlists/PlaylistImageProvider.cs
index 2dfe59088..bb56d9771 100644
--- a/Emby.Server.Implementations/Playlists/PlaylistImageProvider.cs
+++ b/Emby.Server.Implementations/Playlists/PlaylistImageProvider.cs
@@ -1,4 +1,3 @@
-using System;
using System.Collections.Generic;
using System.Linq;
using Emby.Server.Implementations.Images;
diff --git a/Emby.Server.Implementations/Playlists/PlaylistManager.cs b/Emby.Server.Implementations/Playlists/PlaylistManager.cs
index b26f4026c..9b1510ac9 100644
--- a/Emby.Server.Implementations/Playlists/PlaylistManager.cs
+++ b/Emby.Server.Implementations/Playlists/PlaylistManager.cs
@@ -8,12 +8,14 @@ using System.Threading.Tasks;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.Extensions;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Playlists;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Playlists;
+using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using PlaylistsNET.Content;
using PlaylistsNET.Models;
@@ -28,21 +30,24 @@ namespace Emby.Server.Implementations.Playlists
private readonly ILogger _logger;
private readonly IUserManager _userManager;
private readonly IProviderManager _providerManager;
+ private readonly IConfiguration _appConfig;
public PlaylistManager(
ILibraryManager libraryManager,
IFileSystem fileSystem,
ILibraryMonitor iLibraryMonitor,
- ILoggerFactory loggerFactory,
+ ILogger<PlaylistManager> logger,
IUserManager userManager,
- IProviderManager providerManager)
+ IProviderManager providerManager,
+ IConfiguration appConfig)
{
_libraryManager = libraryManager;
_fileSystem = fileSystem;
_iLibraryMonitor = iLibraryMonitor;
- _logger = loggerFactory.CreateLogger(nameof(PlaylistManager));
+ _logger = logger;
_userManager = userManager;
_providerManager = providerManager;
+ _appConfig = appConfig;
}
public IEnumerable<Playlist> GetPlaylists(Guid userId)
@@ -177,7 +182,7 @@ namespace Emby.Server.Implementations.Playlists
return Playlist.GetPlaylistItems(playlistMediaType, items, user, options);
}
- public void AddToPlaylist(string playlistId, IEnumerable<Guid> itemIds, Guid userId)
+ public void AddToPlaylist(string playlistId, ICollection<Guid> itemIds, Guid userId)
{
var user = userId.Equals(Guid.Empty) ? null : _userManager.GetUserById(userId);
@@ -187,37 +192,59 @@ namespace Emby.Server.Implementations.Playlists
});
}
- private void AddToPlaylistInternal(string playlistId, IEnumerable<Guid> itemIds, User user, DtoOptions options)
+ private void AddToPlaylistInternal(string playlistId, ICollection<Guid> newItemIds, User user, DtoOptions options)
{
- var playlist = _libraryManager.GetItemById(playlistId) as Playlist;
+ // Retrieve the existing playlist
+ var playlist = _libraryManager.GetItemById(playlistId) as Playlist
+ ?? throw new ArgumentException("No Playlist exists with Id " + playlistId);
- if (playlist == null)
+ // Retrieve all the items to be added to the playlist
+ var newItems = GetPlaylistItems(newItemIds, playlist.MediaType, user, options)
+ .Where(i => i.SupportsAddingToPlaylist);
+
+ // Filter out duplicate items, if necessary
+ if (!_appConfig.DoPlaylistsAllowDuplicates())
{
- throw new ArgumentException("No Playlist exists with the supplied Id");
+ var existingIds = playlist.LinkedChildren.Select(c => c.ItemId).ToHashSet();
+ newItems = newItems
+ .Where(i => !existingIds.Contains(i.Id))
+ .Distinct();
}
- var list = new List<LinkedChild>();
-
- var items = GetPlaylistItems(itemIds, playlist.MediaType, user, options)
- .Where(i => i.SupportsAddingToPlaylist)
+ // Create a list of the new linked children to add to the playlist
+ var childrenToAdd = newItems
+ .Select(i => LinkedChild.Create(i))
.ToList();
- foreach (var item in items)
+ // Log duplicates that have been ignored, if any
+ int numDuplicates = newItemIds.Count - childrenToAdd.Count;
+ if (numDuplicates > 0)
{
- list.Add(LinkedChild.Create(item));
+ _logger.LogWarning("Ignored adding {DuplicateCount} duplicate items to playlist {PlaylistName}.", numDuplicates, playlist.Name);
}
- var newList = playlist.LinkedChildren.ToList();
- newList.AddRange(list);
- playlist.LinkedChildren = newList.ToArray();
+ // Do nothing else if there are no items to add to the playlist
+ if (childrenToAdd.Count == 0)
+ {
+ return;
+ }
+
+ // Create a new array with the updated playlist items
+ var newLinkedChildren = new LinkedChild[playlist.LinkedChildren.Length + childrenToAdd.Count];
+ playlist.LinkedChildren.CopyTo(newLinkedChildren, 0);
+ childrenToAdd.CopyTo(newLinkedChildren, playlist.LinkedChildren.Length);
+ // Update the playlist in the repository
+ playlist.LinkedChildren = newLinkedChildren;
playlist.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
+ // Update the playlist on disk
if (playlist.IsFile)
{
SavePlaylistFile(playlist);
}
+ // Refresh playlist metadata
_providerManager.QueueRefresh(
playlist.Id,
new MetadataRefreshOptions(new DirectoryService(_fileSystem))
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs
index ecd526251..5822c467b 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs
@@ -70,7 +70,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
}
/// <summary>
- /// Returns the task to be executed
+ /// Returns the task to be executed.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="progress">The progress.</param>
@@ -89,7 +89,6 @@ namespace Emby.Server.Implementations.ScheduledTasks
SourceTypes = new SourceType[] { SourceType.Library },
HasChapterImages = false,
IsVirtualItem = false
-
})
.OfType<Video>()
.ToList();
@@ -160,7 +159,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
}
}
- public string Name => "Chapter image extraction";
+ public string Name => "Extract Chapter Images";
public string Description => "Creates thumbnails for videos that have chapters.";
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs
index 6ec83b5c0..b7668c872 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs
@@ -29,7 +29,10 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
/// <summary>
/// Initializes a new instance of the <see cref="DeleteCacheFileTask" /> class.
/// </summary>
- public DeleteCacheFileTask(IApplicationPaths appPaths, ILogger logger, IFileSystem fileSystem)
+ public DeleteCacheFileTask(
+ IApplicationPaths appPaths,
+ ILogger<DeleteCacheFileTask> logger,
+ IFileSystem fileSystem)
{
ApplicationPaths = appPaths;
_logger = logger;
@@ -158,9 +161,9 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
}
}
- public string Name => "Cache file cleanup";
+ public string Name => "Clean Cache Directory";
- public string Description => "Deletes cache files no longer needed by the system";
+ public string Description => "Deletes cache files no longer needed by the system.";
public string Category => "Maintenance";
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs
index e468c301a..9f9c6353a 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs
@@ -10,7 +10,7 @@ using MediaBrowser.Model.Tasks;
namespace Emby.Server.Implementations.ScheduledTasks.Tasks
{
/// <summary>
- /// Deletes old log files
+ /// Deletes old log files.
/// </summary>
public class DeleteLogFileTask : IScheduledTask, IConfigurableScheduledTask
{
@@ -33,20 +33,18 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
}
/// <summary>
- /// Creates the triggers that define when the task will run
+ /// Creates the triggers that define when the task will run.
/// </summary>
/// <returns>IEnumerable{BaseTaskTrigger}.</returns>
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
return new[] {
-
- // Every so often
new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks}
};
}
/// <summary>
- /// Returns the task to be executed
+ /// Returns the task to be executed.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="progress">The progress.</param>
@@ -81,7 +79,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
return Task.CompletedTask;
}
- public string Name => "Log file cleanup";
+ public string Name => "Clean Log Directory";
public string Description => string.Format("Deletes log files that are more than {0} days old.", ConfigurationManager.CommonConfiguration.LogFileRetentionDays);
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs
index f197734d4..37136f438 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs
@@ -23,7 +23,10 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
/// <summary>
/// Initializes a new instance of the <see cref="DeleteTranscodeFileTask" /> class.
/// </summary>
- public DeleteTranscodeFileTask(ILogger logger, IFileSystem fileSystem, IConfigurationManager configurationManager)
+ public DeleteTranscodeFileTask(
+ ILogger<DeleteTranscodeFileTask> logger,
+ IFileSystem fileSystem,
+ IConfigurationManager configurationManager)
{
_logger = logger;
_fileSystem = fileSystem;
@@ -125,9 +128,9 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
}
}
- public string Name => "Transcode file cleanup";
+ public string Name => "Clean Transcode Directory";
- public string Description => "Deletes transcode files more than 24 hours old.";
+ public string Description => "Deletes transcode files more than one day old.";
public string Category => "Maintenance";
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs
index d70799c47..eaf17aace 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs
@@ -9,12 +9,12 @@ using MediaBrowser.Model.Tasks;
namespace Emby.Server.Implementations.ScheduledTasks
{
/// <summary>
- /// Class PeopleValidationTask
+ /// Class PeopleValidationTask.
/// </summary>
public class PeopleValidationTask : IScheduledTask
{
/// <summary>
- /// The _library manager
+ /// The library manager.
/// </summary>
private readonly ILibraryManager _libraryManager;
@@ -32,13 +32,12 @@ namespace Emby.Server.Implementations.ScheduledTasks
}
/// <summary>
- /// Creates the triggers that define when the task will run
+ /// Creates the triggers that define when the task will run.
/// </summary>
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
return new[]
{
- // Every so often
new TaskTriggerInfo
{
Type = TaskTriggerInfo.TriggerInterval,
@@ -48,7 +47,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
}
/// <summary>
- /// Returns the task to be executed
+ /// Returns the task to be executed.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="progress">The progress.</param>
@@ -58,7 +57,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
return _libraryManager.ValidatePeople(cancellationToken, progress);
}
- public string Name => "Refresh people";
+ public string Name => "Refresh People";
public string Description => "Updates metadata for actors and directors in your media library.";
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs
index 909fffb1f..9d87316e4 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs
@@ -23,7 +23,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
private readonly IInstallationManager _installationManager;
- public PluginUpdateTask(ILogger logger, IInstallationManager installationManager)
+ public PluginUpdateTask(ILogger<PluginUpdateTask> logger, IInstallationManager installationManager)
{
_logger = logger;
_installationManager = installationManager;
@@ -96,7 +96,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
}
/// <inheritdoc />
- public string Name => "Check for plugin updates";
+ public string Name => "Update Plugins";
/// <inheritdoc />
public string Description => "Downloads and installs updates for plugins that are configured to update automatically.";
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs
index 3e6d251c9..073678019 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs
@@ -57,7 +57,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
return ((LibraryManager)_libraryManager).ValidateMediaLibraryInternal(progress, cancellationToken);
}
- public string Name => "Scan media library";
+ public string Name => "Scan Media Library";
public string Description => "Scans your media library for new files and refreshes metadata.";
diff --git a/Emby.Server.Implementations/Serialization/JsonSerializer.cs b/Emby.Server.Implementations/Serialization/JsonSerializer.cs
index 36196ee36..bcc814daf 100644
--- a/Emby.Server.Implementations/Serialization/JsonSerializer.cs
+++ b/Emby.Server.Implementations/Serialization/JsonSerializer.cs
@@ -2,7 +2,6 @@ using System;
using System.Globalization;
using System.IO;
using System.Threading.Tasks;
-using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
namespace Emby.Server.Implementations.Serialization
@@ -12,13 +11,15 @@ namespace Emby.Server.Implementations.Serialization
/// </summary>
public class JsonSerializer : IJsonSerializer
{
- private readonly IFileSystem _fileSystem;
-
- public JsonSerializer(
- IFileSystem fileSystem)
+ public JsonSerializer()
{
- _fileSystem = fileSystem;
- Configure();
+ ServiceStack.Text.JsConfig.DateHandler = ServiceStack.Text.DateHandler.ISO8601;
+ ServiceStack.Text.JsConfig.ExcludeTypeInfo = true;
+ ServiceStack.Text.JsConfig.IncludeNullValues = false;
+ ServiceStack.Text.JsConfig.AlwaysUseUtc = true;
+ ServiceStack.Text.JsConfig.AssumeUtc = true;
+
+ ServiceStack.Text.JsConfig<Guid>.SerializeFn = SerializeGuid;
}
/// <summary>
@@ -81,7 +82,7 @@ namespace Emby.Server.Implementations.Serialization
throw new ArgumentNullException(nameof(file));
}
- using (var stream = _fileSystem.GetFileStream(file, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
+ using (var stream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read))
{
SerializeToStream(obj, stream);
}
@@ -162,7 +163,6 @@ namespace Emby.Server.Implementations.Serialization
throw new ArgumentNullException(nameof(stream));
}
-
return ServiceStack.Text.JsonSerializer.DeserializeFromStreamAsync<T>(stream);
}
@@ -225,20 +225,6 @@ namespace Emby.Server.Implementations.Serialization
}
}
- /// <summary>
- /// Configures this instance.
- /// </summary>
- private void Configure()
- {
- ServiceStack.Text.JsConfig.DateHandler = ServiceStack.Text.DateHandler.ISO8601;
- ServiceStack.Text.JsConfig.ExcludeTypeInfo = true;
- ServiceStack.Text.JsConfig.IncludeNullValues = false;
- ServiceStack.Text.JsConfig.AlwaysUseUtc = true;
- ServiceStack.Text.JsConfig.AssumeUtc = true;
-
- ServiceStack.Text.JsConfig<Guid>.SerializeFn = SerializeGuid;
- }
-
private static string SerializeGuid(Guid guid)
{
if (guid.Equals(Guid.Empty))
diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs
index b1d513dd4..dfcd3843c 100644
--- a/Emby.Server.Implementations/Session/SessionManager.cs
+++ b/Emby.Server.Implementations/Session/SessionManager.cs
@@ -30,17 +30,17 @@ using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Session
{
/// <summary>
- /// Class SessionManager
+ /// Class SessionManager.
/// </summary>
public class SessionManager : ISessionManager, IDisposable
{
/// <summary>
- /// The _user data repository
+ /// The user data repository.
/// </summary>
private readonly IUserDataManager _userDataManager;
/// <summary>
- /// The _logger
+ /// The logger.
/// </summary>
private readonly ILogger _logger;
@@ -57,36 +57,19 @@ namespace Emby.Server.Implementations.Session
private readonly IDeviceManager _deviceManager;
/// <summary>
- /// The _active connections
+ /// The active connections.
/// </summary>
private readonly ConcurrentDictionary<string, SessionInfo> _activeConnections =
new ConcurrentDictionary<string, SessionInfo>(StringComparer.OrdinalIgnoreCase);
- public event EventHandler<GenericEventArgs<AuthenticationRequest>> AuthenticationFailed;
-
- public event EventHandler<GenericEventArgs<AuthenticationResult>> AuthenticationSucceeded;
-
- /// <summary>
- /// Occurs when [playback start].
- /// </summary>
- public event EventHandler<PlaybackProgressEventArgs> PlaybackStart;
- /// <summary>
- /// Occurs when [playback progress].
- /// </summary>
- public event EventHandler<PlaybackProgressEventArgs> PlaybackProgress;
- /// <summary>
- /// Occurs when [playback stopped].
- /// </summary>
- public event EventHandler<PlaybackStopEventArgs> PlaybackStopped;
+ private Timer _idleTimer;
- public event EventHandler<SessionEventArgs> SessionStarted;
- public event EventHandler<SessionEventArgs> CapabilitiesChanged;
- public event EventHandler<SessionEventArgs> SessionEnded;
- public event EventHandler<SessionEventArgs> SessionActivity;
+ private DtoOptions _itemInfoDtoOptions;
+ private bool _disposed = false;
public SessionManager(
+ ILogger<SessionManager> logger,
IUserDataManager userDataManager,
- ILoggerFactory loggerFactory,
ILibraryManager libraryManager,
IUserManager userManager,
IMusicManager musicManager,
@@ -97,8 +80,8 @@ namespace Emby.Server.Implementations.Session
IDeviceManager deviceManager,
IMediaSourceManager mediaSourceManager)
{
+ _logger = logger;
_userDataManager = userDataManager;
- _logger = loggerFactory.CreateLogger(nameof(SessionManager));
_libraryManager = libraryManager;
_userManager = userManager;
_musicManager = musicManager;
@@ -108,9 +91,49 @@ namespace Emby.Server.Implementations.Session
_authRepo = authRepo;
_deviceManager = deviceManager;
_mediaSourceManager = mediaSourceManager;
+
_deviceManager.DeviceOptionsUpdated += OnDeviceManagerDeviceOptionsUpdated;
}
+ /// <inheritdoc />
+ public event EventHandler<GenericEventArgs<AuthenticationRequest>> AuthenticationFailed;
+
+ /// <inheritdoc />
+ public event EventHandler<GenericEventArgs<AuthenticationResult>> AuthenticationSucceeded;
+
+ /// <summary>
+ /// Occurs when playback has started.
+ /// </summary>
+ public event EventHandler<PlaybackProgressEventArgs> PlaybackStart;
+
+ /// <summary>
+ /// Occurs when playback has progressed.
+ /// </summary>
+ public event EventHandler<PlaybackProgressEventArgs> PlaybackProgress;
+
+ /// <summary>
+ /// Occurs when playback has stopped.
+ /// </summary>
+ public event EventHandler<PlaybackStopEventArgs> PlaybackStopped;
+
+ /// <inheritdoc />
+ public event EventHandler<SessionEventArgs> SessionStarted;
+
+ /// <inheritdoc />
+ public event EventHandler<SessionEventArgs> CapabilitiesChanged;
+
+ /// <inheritdoc />
+ public event EventHandler<SessionEventArgs> SessionEnded;
+
+ /// <inheritdoc />
+ public event EventHandler<SessionEventArgs> SessionActivity;
+
+ /// <summary>
+ /// Gets all connections.
+ /// </summary>
+ /// <value>All connections.</value>
+ public IEnumerable<SessionInfo> Sessions => _activeConnections.Values.OrderByDescending(c => c.LastActivityDate);
+
private void OnDeviceManagerDeviceOptionsUpdated(object sender, GenericEventArgs<Tuple<string, DeviceOptions>> e)
{
foreach (var session in Sessions)
@@ -130,14 +153,17 @@ namespace Emby.Server.Implementations.Session
}
}
- private bool _disposed = false;
-
+ /// <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)
@@ -147,15 +173,17 @@ namespace Emby.Server.Implementations.Session
if (disposing)
{
- // TODO: dispose stuff
+ _idleTimer?.Dispose();
}
+ _idleTimer = null;
+
_deviceManager.DeviceOptionsUpdated -= OnDeviceManagerDeviceOptionsUpdated;
_disposed = true;
}
- public void CheckDisposed()
+ private void CheckDisposed()
{
if (_disposed)
{
@@ -163,12 +191,6 @@ namespace Emby.Server.Implementations.Session
}
}
- /// <summary>
- /// Gets all connections.
- /// </summary>
- /// <value>All connections.</value>
- public IEnumerable<SessionInfo> Sessions => _activeConnections.Values.OrderByDescending(c => c.LastActivityDate);
-
private void OnSessionStarted(SessionInfo info)
{
if (!string.IsNullOrEmpty(info.DeviceId))
@@ -199,13 +221,13 @@ namespace Emby.Server.Implementations.Session
new SessionEventArgs
{
SessionInfo = info
-
},
_logger);
info.Dispose();
}
+ /// <inheritdoc />
public void UpdateDeviceName(string sessionId, string deviceName)
{
var session = GetSession(sessionId);
@@ -225,7 +247,6 @@ namespace Emby.Server.Implementations.Session
/// <param name="remoteEndPoint">The remote end point.</param>
/// <param name="user">The user.</param>
/// <returns>SessionInfo.</returns>
- /// <exception cref="ArgumentNullException">user</exception>
public SessionInfo LogSessionActivity(
string appName,
string appVersion,
@@ -263,14 +284,7 @@ namespace Emby.Server.Implementations.Session
if ((activityDate - userLastActivityDate).TotalSeconds > 60)
{
- try
- {
- _userManager.UpdateUser(user);
- }
- catch (Exception ex)
- {
- _logger.LogError("Error updating user", ex);
- }
+ _userManager.UpdateUser(user);
}
}
@@ -287,18 +301,20 @@ namespace Emby.Server.Implementations.Session
return session;
}
+ /// <inheritdoc />
public void CloseIfNeeded(SessionInfo session)
{
if (!session.SessionControllers.Any(i => i.IsSessionActive))
{
var key = GetSessionKey(session.Client, session.DeviceId);
- _activeConnections.TryRemove(key, out var removed);
+ _activeConnections.TryRemove(key, out _);
OnSessionEnded(session);
}
}
+ /// <inheritdoc />
public void ReportSessionEnded(string sessionId)
{
CheckDisposed();
@@ -308,7 +324,7 @@ namespace Emby.Server.Implementations.Session
{
var key = GetSessionKey(session.Client, session.DeviceId);
- _activeConnections.TryRemove(key, out var removed);
+ _activeConnections.TryRemove(key, out _);
OnSessionEnded(session);
}
@@ -339,7 +355,7 @@ namespace Emby.Server.Implementations.Session
var runtimeTicks = libraryItem.RunTimeTicks;
MediaSourceInfo mediaSource = null;
- if (libraryItem is IHasMediaSources hasMediaSources)
+ if (libraryItem is IHasMediaSources)
{
mediaSource = await GetMediaSource(libraryItem, info.MediaSourceId, info.LiveStreamId).ConfigureAwait(false);
@@ -391,7 +407,6 @@ namespace Emby.Server.Implementations.Session
/// Removes the now playing item id.
/// </summary>
/// <param name="session">The session.</param>
- /// <exception cref="ArgumentNullException">item</exception>
private void RemoveNowPlayingItem(SessionInfo session)
{
session.NowPlayingItem = null;
@@ -404,9 +419,7 @@ namespace Emby.Server.Implementations.Session
}
private static string GetSessionKey(string appName, string deviceId)
- {
- return appName + deviceId;
- }
+ => appName + deviceId;
/// <summary>
/// Gets the connection.
@@ -426,6 +439,7 @@ namespace Emby.Server.Implementations.Session
{
throw new ArgumentNullException(nameof(deviceId));
}
+
var key = GetSessionKey(appName, deviceId);
CheckDisposed();
@@ -498,7 +512,7 @@ namespace Emby.Server.Implementations.Session
{
var users = new List<User>();
- if (!session.UserId.Equals(Guid.Empty))
+ if (session.UserId != Guid.Empty)
{
var user = _userManager.GetUserById(session.UserId);
@@ -517,8 +531,6 @@ namespace Emby.Server.Implementations.Session
return users;
}
- private Timer _idleTimer;
-
private void StartIdleCheckTimer()
{
if (_idleTimer == null)
@@ -594,11 +606,11 @@ namespace Emby.Server.Implementations.Session
}
/// <summary>
- /// Used to report that playback has started for an item
+ /// Used to report that playback has started for an item.
/// </summary>
/// <param name="info">The info.</param>
/// <returns>Task.</returns>
- /// <exception cref="ArgumentNullException">info</exception>
+ /// <exception cref="ArgumentNullException"><c>info</c> is <c>null</c>.</exception>
public async Task OnPlaybackStart(PlaybackStartInfo info)
{
CheckDisposed();
@@ -610,7 +622,7 @@ namespace Emby.Server.Implementations.Session
var session = GetSession(info.SessionId);
- var libraryItem = info.ItemId.Equals(Guid.Empty)
+ var libraryItem = info.ItemId == Guid.Empty
? null
: GetNowPlayingItem(session, info.ItemId);
@@ -648,7 +660,6 @@ namespace Emby.Server.Implementations.Session
ClientName = session.Client,
DeviceId = session.DeviceId,
Session = session
-
},
_logger);
@@ -679,13 +690,14 @@ namespace Emby.Server.Implementations.Session
_userDataManager.SaveUserData(user, item, data, UserDataSaveReason.PlaybackStart, CancellationToken.None);
}
+ /// <inheritdoc />
public Task OnPlaybackProgress(PlaybackProgressInfo info)
{
return OnPlaybackProgress(info, false);
}
/// <summary>
- /// Used to report playback progress for an item
+ /// Used to report playback progress for an item.
/// </summary>
/// <returns>Task.</returns>
public async Task OnPlaybackProgress(PlaybackProgressInfo info, bool isAutomated)
@@ -852,7 +864,7 @@ namespace Emby.Server.Implementations.Session
{
MediaSourceInfo mediaSource = null;
- if (libraryItem is IHasMediaSources hasMediaSources)
+ if (libraryItem is IHasMediaSources)
{
mediaSource = await GetMediaSource(libraryItem, info.MediaSourceId, info.LiveStreamId).ConfigureAwait(false);
}
@@ -924,7 +936,6 @@ namespace Emby.Server.Implementations.Session
ClientName = session.Client,
DeviceId = session.DeviceId,
Session = session
-
},
_logger);
}
@@ -962,13 +973,17 @@ namespace Emby.Server.Implementations.Session
/// <param name="sessionId">The session identifier.</param>
/// <param name="throwOnMissing">if set to <c>true</c> [throw on missing].</param>
/// <returns>SessionInfo.</returns>
- /// <exception cref="ResourceNotFoundException">sessionId</exception>
+ /// <exception cref="ResourceNotFoundException">
+ /// No session with an Id equal to <c>sessionId</c> was found
+ /// and <c>throwOnMissing</c> is <c>true</c>.
+ /// </exception>
private SessionInfo GetSession(string sessionId, bool throwOnMissing = true)
{
var session = Sessions.FirstOrDefault(i => string.Equals(i.Id, sessionId, StringComparison.Ordinal));
if (session == null && throwOnMissing)
{
- throw new ResourceNotFoundException(string.Format("Session {0} not found.", sessionId));
+ throw new ResourceNotFoundException(
+ string.Format(CultureInfo.InvariantCulture, "Session {0} not found.", sessionId));
}
return session;
@@ -981,12 +996,14 @@ namespace Emby.Server.Implementations.Session
if (session == null)
{
- throw new ResourceNotFoundException(string.Format("Session {0} not found.", sessionId));
+ throw new ResourceNotFoundException(
+ string.Format(CultureInfo.InvariantCulture, "Session {0} not found.", sessionId));
}
return session;
}
+ /// <inheritdoc />
public Task SendMessageCommand(string controllingSessionId, string sessionId, MessageCommand command, CancellationToken cancellationToken)
{
CheckDisposed();
@@ -1007,6 +1024,7 @@ namespace Emby.Server.Implementations.Session
return SendGeneralCommand(controllingSessionId, sessionId, generalCommand, cancellationToken);
}
+ /// <inheritdoc />
public Task SendGeneralCommand(string controllingSessionId, string sessionId, GeneralCommand command, CancellationToken cancellationToken)
{
CheckDisposed();
@@ -1051,6 +1069,7 @@ namespace Emby.Server.Implementations.Session
return Task.WhenAll(GetTasks());
}
+ /// <inheritdoc />
public async Task SendPlayCommand(string controllingSessionId, string sessionId, PlayRequest command, CancellationToken cancellationToken)
{
CheckDisposed();
@@ -1092,7 +1111,8 @@ namespace Emby.Server.Implementations.Session
{
if (items.Any(i => i.GetPlayAccess(user) != PlayAccess.Full))
{
- throw new ArgumentException(string.Format("{0} is not allowed to play media.", user.Name));
+ throw new ArgumentException(
+ string.Format(CultureInfo.InvariantCulture, "{0} is not allowed to play media.", user.Name));
}
}
@@ -1200,6 +1220,7 @@ namespace Emby.Server.Implementations.Session
return _musicManager.GetInstantMixFromItem(item, user, new DtoOptions(false) { EnableImages = false });
}
+ /// <inheritdoc />
public Task SendBrowseCommand(string controllingSessionId, string sessionId, BrowseRequest command, CancellationToken cancellationToken)
{
var generalCommand = new GeneralCommand
@@ -1216,6 +1237,7 @@ namespace Emby.Server.Implementations.Session
return SendGeneralCommand(controllingSessionId, sessionId, generalCommand, cancellationToken);
}
+ /// <inheritdoc />
public Task SendPlaystateCommand(string controllingSessionId, string sessionId, PlaystateRequest command, CancellationToken cancellationToken)
{
CheckDisposed();
@@ -1299,12 +1321,12 @@ namespace Emby.Server.Implementations.Session
var session = GetSession(sessionId);
- if (session.UserId.Equals(userId))
+ if (session.UserId == userId)
{
throw new ArgumentException("The requested user is already the primary user of the session.");
}
- if (session.AdditionalUsers.All(i => !i.UserId.Equals(userId)))
+ if (session.AdditionalUsers.All(i => i.UserId != userId))
{
var user = _userManager.GetUserById(userId);
@@ -1379,20 +1401,16 @@ namespace Emby.Server.Implementations.Session
user = _userManager.GetUserByName(request.Username);
}
- if (user != null)
+ if (user == null)
{
- // TODO: Move this to userManager?
- if (!string.IsNullOrEmpty(request.DeviceId)
- && !_deviceManager.CanAccessDevice(user, request.DeviceId))
- {
- throw new SecurityException("User is not allowed access from this device.");
- }
+ AuthenticationFailed?.Invoke(this, new GenericEventArgs<AuthenticationRequest>(request));
+ throw new SecurityException("Invalid username or password entered.");
}
- if (user == null)
+ if (!string.IsNullOrEmpty(request.DeviceId)
+ && !_deviceManager.CanAccessDevice(user, request.DeviceId))
{
- AuthenticationFailed?.Invoke(this, new GenericEventArgs<AuthenticationRequest>(request));
- throw new SecurityException("Invalid user or password entered.");
+ throw new SecurityException("User is not allowed access from this device.");
}
if (enforcePassword)
@@ -1430,19 +1448,19 @@ namespace Emby.Server.Implementations.Session
private string GetAuthorizationToken(User user, string deviceId, string app, string appVersion, string deviceName)
{
- var existing = _authRepo.Get(new AuthenticationInfoQuery
- {
- DeviceId = deviceId,
- UserId = user.Id,
- Limit = 1
-
- }).Items.FirstOrDefault();
-
- var allExistingForDevice = _authRepo.Get(new AuthenticationInfoQuery
- {
- DeviceId = deviceId
+ var existing = _authRepo.Get(
+ new AuthenticationInfoQuery
+ {
+ DeviceId = deviceId,
+ UserId = user.Id,
+ Limit = 1
+ }).Items.FirstOrDefault();
- }).Items;
+ var allExistingForDevice = _authRepo.Get(
+ new AuthenticationInfoQuery
+ {
+ DeviceId = deviceId
+ }).Items;
foreach (var auth in allExistingForDevice)
{
@@ -1461,7 +1479,7 @@ namespace Emby.Server.Implementations.Session
if (existing != null)
{
- _logger.LogInformation("Reissuing access token: " + existing.AccessToken);
+ _logger.LogInformation("Reissuing access token: {Token}", existing.AccessToken);
return existing.AccessToken;
}
@@ -1486,6 +1504,7 @@ namespace Emby.Server.Implementations.Session
return newToken.AccessToken;
}
+ /// <inheritdoc />
public void Logout(string accessToken)
{
CheckDisposed();
@@ -1495,19 +1514,20 @@ namespace Emby.Server.Implementations.Session
throw new ArgumentNullException(nameof(accessToken));
}
- var existing = _authRepo.Get(new AuthenticationInfoQuery
- {
- Limit = 1,
- AccessToken = accessToken
-
- }).Items.FirstOrDefault();
+ var existing = _authRepo.Get(
+ new AuthenticationInfoQuery
+ {
+ Limit = 1,
+ AccessToken = accessToken
+ }).Items;
- if (existing != null)
+ if (existing.Count > 0)
{
- Logout(existing);
+ Logout(existing[0]);
}
}
+ /// <inheritdoc />
public void Logout(AuthenticationInfo existing)
{
CheckDisposed();
@@ -1533,6 +1553,7 @@ namespace Emby.Server.Implementations.Session
}
}
+ /// <inheritdoc />
public void RevokeUserTokens(Guid userId, string currentAccessToken)
{
CheckDisposed();
@@ -1551,6 +1572,7 @@ namespace Emby.Server.Implementations.Session
}
}
+ /// <inheritdoc />
public void RevokeToken(string token)
{
Logout(token);
@@ -1607,10 +1629,8 @@ namespace Emby.Server.Implementations.Session
_deviceManager.SaveCapabilities(deviceId, capabilities);
}
- private DtoOptions _itemInfoDtoOptions;
-
/// <summary>
- /// Converts a BaseItem to a BaseItemInfo
+ /// Converts a BaseItem to a BaseItemInfo.
/// </summary>
private BaseItemDto GetItemInfo(BaseItem item, MediaSourceInfo mediaSource)
{
@@ -1680,11 +1700,12 @@ namespace Emby.Server.Implementations.Session
}
catch (Exception ex)
{
- _logger.LogError("Error getting {0} image info", ex, type);
+ _logger.LogError(ex, "Error getting image information for {Type}", type);
return null;
}
}
+ /// <inheritdoc />
public void ReportNowViewingItem(string sessionId, string itemId)
{
if (string.IsNullOrEmpty(itemId))
@@ -1692,23 +1713,26 @@ namespace Emby.Server.Implementations.Session
throw new ArgumentNullException(nameof(itemId));
}
- //var item = _libraryManager.GetItemById(new Guid(itemId));
+ var item = _libraryManager.GetItemById(new Guid(itemId));
- //var info = GetItemInfo(item, null, null);
+ var info = GetItemInfo(item, null);
- //ReportNowViewingItem(sessionId, info);
+ ReportNowViewingItem(sessionId, info);
}
+ /// <inheritdoc />
public void ReportNowViewingItem(string sessionId, BaseItemDto item)
{
- //var session = GetSession(sessionId);
+ var session = GetSession(sessionId);
- //session.NowViewingItem = item;
+ session.NowViewingItem = item;
}
+ /// <inheritdoc />
public void ReportTranscodingInfo(string deviceId, TranscodingInfo info)
{
- var session = Sessions.FirstOrDefault(i => string.Equals(i.DeviceId, deviceId));
+ var session = Sessions.FirstOrDefault(i =>
+ string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase));
if (session != null)
{
@@ -1716,17 +1740,21 @@ namespace Emby.Server.Implementations.Session
}
}
+ /// <inheritdoc />
public void ClearTranscodingInfo(string deviceId)
{
ReportTranscodingInfo(deviceId, null);
}
+ /// <inheritdoc />
public SessionInfo GetSession(string deviceId, string client, string version)
{
- return Sessions.FirstOrDefault(i => string.Equals(i.DeviceId, deviceId) &&
- string.Equals(i.Client, client));
+ return Sessions.FirstOrDefault(i =>
+ string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase)
+ && string.Equals(i.Client, client, StringComparison.OrdinalIgnoreCase));
}
+ /// <inheritdoc />
public SessionInfo GetSessionByAuthenticationToken(AuthenticationInfo info, string deviceId, string remoteEndpoint, string appVersion)
{
if (info == null)
@@ -1759,23 +1787,24 @@ namespace Emby.Server.Implementations.Session
return LogSessionActivity(appName, appVersion, deviceId, deviceName, remoteEndpoint, user);
}
+ /// <inheritdoc />
public SessionInfo GetSessionByAuthenticationToken(string token, string deviceId, string remoteEndpoint)
{
- var result = _authRepo.Get(new AuthenticationInfoQuery
+ var items = _authRepo.Get(new AuthenticationInfoQuery
{
- AccessToken = token
- });
-
- var info = result.Items.FirstOrDefault();
+ AccessToken = token,
+ Limit = 1
+ }).Items;
- if (info == null)
+ if (items.Count == 0)
{
return null;
}
- return GetSessionByAuthenticationToken(info, deviceId, remoteEndpoint, null);
+ return GetSessionByAuthenticationToken(items[0], deviceId, remoteEndpoint, null);
}
+ /// <inheritdoc />
public Task SendMessageToAdminSessions<T>(string name, T data, CancellationToken cancellationToken)
{
CheckDisposed();
@@ -1785,6 +1814,7 @@ namespace Emby.Server.Implementations.Session
return SendMessageToUserSessions(adminUserIds, name, data, cancellationToken);
}
+ /// <inheritdoc />
public Task SendMessageToUserSessions<T>(List<Guid> userIds, string name, Func<T> dataFn, CancellationToken cancellationToken)
{
CheckDisposed();
@@ -1796,11 +1826,10 @@ namespace Emby.Server.Implementations.Session
return Task.CompletedTask;
}
- var data = dataFn();
-
- return SendMessageToSessions(sessions, name, data, cancellationToken);
+ return SendMessageToSessions(sessions, name, dataFn(), cancellationToken);
}
+ /// <inheritdoc />
public Task SendMessageToUserSessions<T>(List<Guid> userIds, string name, T data, CancellationToken cancellationToken)
{
CheckDisposed();
@@ -1809,6 +1838,7 @@ namespace Emby.Server.Implementations.Session
return SendMessageToSessions(sessions, name, data, cancellationToken);
}
+ /// <inheritdoc />
public Task SendMessageToUserDeviceSessions<T>(string deviceId, string name, T data, CancellationToken cancellationToken)
{
CheckDisposed();
@@ -1817,22 +1847,5 @@ namespace Emby.Server.Implementations.Session
return SendMessageToSessions(sessions, name, data, cancellationToken);
}
-
- public Task SendMessageToUserDeviceAndAdminSessions<T>(string deviceId, string name, T data, CancellationToken cancellationToken)
- {
- CheckDisposed();
-
- var sessions = Sessions
- .Where(i => string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase) || IsAdminSession(i));
-
- return SendMessageToSessions(sessions, name, data, cancellationToken);
- }
-
- private bool IsAdminSession(SessionInfo s)
- {
- var user = _userManager.GetUserById(s.UserId);
-
- return user != null && user.Policy.IsAdministrator;
- }
}
}
diff --git a/Emby.Server.Implementations/SocketSharp/HttpPostedFile.cs b/Emby.Server.Implementations/SocketSharp/HttpPostedFile.cs
index 95b7912fb..7479d8104 100644
--- a/Emby.Server.Implementations/SocketSharp/HttpPostedFile.cs
+++ b/Emby.Server.Implementations/SocketSharp/HttpPostedFile.cs
@@ -1,11 +1,5 @@
using System;
-using System.Collections.Generic;
-using System.Globalization;
using System.IO;
-using System.Net;
-using System.Text;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Services;
public sealed class HttpPostedFile : IDisposable
{
diff --git a/Emby.Server.Implementations/SocketSharp/WebSocketSharpListener.cs b/Emby.Server.Implementations/SocketSharp/WebSocketSharpListener.cs
index ba5ba1904..b85750c9b 100644
--- a/Emby.Server.Implementations/SocketSharp/WebSocketSharpListener.cs
+++ b/Emby.Server.Implementations/SocketSharp/WebSocketSharpListener.cs
@@ -1,13 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Net;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
using Emby.Server.Implementations.HttpServer;
using Emby.Server.Implementations.Net;
-using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Services;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
@@ -23,15 +21,14 @@ namespace Emby.Server.Implementations.SocketSharp
private CancellationTokenSource _disposeCancellationTokenSource = new CancellationTokenSource();
private CancellationToken _disposeCancellationToken;
- public WebSocketSharpListener(
- ILogger logger)
+ public WebSocketSharpListener(ILogger<WebSocketSharpListener> logger)
{
_logger = logger;
-
_disposeCancellationToken = _disposeCancellationTokenSource.Token;
}
public Func<Exception, IRequest, bool, bool, Task> ErrorHandler { get; set; }
+
public Func<IHttpRequest, string, string, string, CancellationToken, Task> RequestHandler { get; set; }
public Action<WebSocketConnectEventArgs> WebSocketConnected { get; set; }
diff --git a/Emby.Server.Implementations/Sorting/AlphanumComparator.cs b/Emby.Server.Implementations/Sorting/AlphanumComparator.cs
deleted file mode 100644
index 2e00c24d7..000000000
--- a/Emby.Server.Implementations/Sorting/AlphanumComparator.cs
+++ /dev/null
@@ -1,99 +0,0 @@
-using System.Collections.Generic;
-using System.Text;
-using MediaBrowser.Controller.Sorting;
-
-namespace Emby.Server.Implementations.Sorting
-{
- public class AlphanumComparator : IComparer<string>
- {
- public static int CompareValues(string s1, string s2)
- {
- if (s1 == null || s2 == null)
- {
- return 0;
- }
-
- int thisMarker = 0, thisNumericChunk = 0;
- int thatMarker = 0, thatNumericChunk = 0;
-
- while ((thisMarker < s1.Length) || (thatMarker < s2.Length))
- {
- if (thisMarker >= s1.Length)
- {
- return -1;
- }
- else if (thatMarker >= s2.Length)
- {
- return 1;
- }
- char thisCh = s1[thisMarker];
- char thatCh = s2[thatMarker];
-
- var thisChunk = new StringBuilder();
- var thatChunk = new StringBuilder();
-
- while ((thisMarker < s1.Length) && (thisChunk.Length == 0 || SortHelper.InChunk(thisCh, thisChunk[0])))
- {
- thisChunk.Append(thisCh);
- thisMarker++;
-
- if (thisMarker < s1.Length)
- {
- thisCh = s1[thisMarker];
- }
- }
-
- while ((thatMarker < s2.Length) && (thatChunk.Length == 0 || SortHelper.InChunk(thatCh, thatChunk[0])))
- {
- thatChunk.Append(thatCh);
- thatMarker++;
-
- if (thatMarker < s2.Length)
- {
- thatCh = s2[thatMarker];
- }
- }
-
- int result = 0;
- // If both chunks contain numeric characters, sort them numerically
- if (char.IsDigit(thisChunk[0]) && char.IsDigit(thatChunk[0]))
- {
- if (!int.TryParse(thisChunk.ToString(), out thisNumericChunk))
- {
- return 0;
- }
- if (!int.TryParse(thatChunk.ToString(), out thatNumericChunk))
- {
- return 0;
- }
-
- if (thisNumericChunk < thatNumericChunk)
- {
- result = -1;
- }
-
- if (thisNumericChunk > thatNumericChunk)
- {
- result = 1;
- }
- }
- else
- {
- result = thisChunk.ToString().CompareTo(thatChunk.ToString());
- }
-
- if (result != 0)
- {
- return result;
- }
- }
-
- return 0;
- }
-
- public int Compare(string x, string y)
- {
- return CompareValues(x, y);
- }
- }
-}
diff --git a/Emby.Server.Implementations/Udp/UdpServer.cs b/Emby.Server.Implementations/Udp/UdpServer.cs
index 185a282ac..c91d137a7 100644
--- a/Emby.Server.Implementations/Udp/UdpServer.cs
+++ b/Emby.Server.Implementations/Udp/UdpServer.cs
@@ -1,112 +1,44 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
using System.Net;
+using System.Net.Sockets;
using System.Text;
+using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller;
using MediaBrowser.Model.ApiClient;
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.Net;
-using MediaBrowser.Model.Serialization;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Udp
{
/// <summary>
- /// Provides a Udp Server
+ /// Provides a Udp Server.
/// </summary>
- public class UdpServer : IDisposable
+ public sealed class UdpServer : IDisposable
{
/// <summary>
/// The _logger
/// </summary>
private readonly ILogger _logger;
+ private readonly IServerApplicationHost _appHost;
- private bool _isDisposed;
-
- private readonly List<Tuple<string, bool, Func<string, IPEndPoint, Encoding, CancellationToken, Task>>> _responders = new List<Tuple<string, bool, Func<string, IPEndPoint, Encoding, CancellationToken, Task>>>();
+ private Socket _udpSocket;
+ private IPEndPoint _endpoint;
+ private readonly byte[] _receiveBuffer = new byte[8192];
- private readonly IServerApplicationHost _appHost;
- private readonly IJsonSerializer _json;
+ private bool _disposed = false;
/// <summary>
/// Initializes a new instance of the <see cref="UdpServer" /> class.
/// </summary>
- public UdpServer(ILogger logger, IServerApplicationHost appHost, IJsonSerializer json, ISocketFactory socketFactory)
+ public UdpServer(ILogger logger, IServerApplicationHost appHost)
{
_logger = logger;
_appHost = appHost;
- _json = json;
- _socketFactory = socketFactory;
-
- AddMessageResponder("who is JellyfinServer?", true, RespondToV2Message);
- }
-
- private void AddMessageResponder(string message, bool isSubstring, Func<string, IPEndPoint, Encoding, CancellationToken, Task> responder)
- {
- _responders.Add(new Tuple<string, bool, Func<string, IPEndPoint, Encoding, CancellationToken, Task>>(message, isSubstring, responder));
- }
-
- /// <summary>
- /// Raises the <see cref="E:MessageReceived" /> event.
- /// </summary>
- private async void OnMessageReceived(GenericEventArgs<SocketReceiveResult> e)
- {
- var message = e.Argument;
-
- var encoding = Encoding.UTF8;
- var responder = GetResponder(message.Buffer, message.ReceivedBytes, encoding);
-
- if (responder == null)
- {
- encoding = Encoding.Unicode;
- responder = GetResponder(message.Buffer, message.ReceivedBytes, encoding);
- }
-
- if (responder != null)
- {
- var cancellationToken = CancellationToken.None;
-
- try
- {
- await responder.Item2.Item3(responder.Item1, message.RemoteEndPoint, encoding, cancellationToken).ConfigureAwait(false);
- }
- catch (OperationCanceledException)
- {
-
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error in OnMessageReceived");
- }
- }
}
- private Tuple<string, Tuple<string, bool, Func<string, IPEndPoint, Encoding, CancellationToken, Task>>> GetResponder(byte[] buffer, int bytesReceived, Encoding encoding)
+ private async Task RespondToV2Message(string messageText, EndPoint endpoint, CancellationToken cancellationToken)
{
- var text = encoding.GetString(buffer, 0, bytesReceived);
- var responder = _responders.FirstOrDefault(i =>
- {
- if (i.Item2)
- {
- return text.IndexOf(i.Item1, StringComparison.OrdinalIgnoreCase) != -1;
- }
- return string.Equals(i.Item1, text, StringComparison.OrdinalIgnoreCase);
- });
-
- if (responder == null)
- {
- return null;
- }
- return new Tuple<string, Tuple<string, bool, Func<string, IPEndPoint, Encoding, CancellationToken, Task>>>(text, responder);
- }
-
- private async Task RespondToV2Message(string messageText, IPEndPoint endpoint, Encoding encoding, CancellationToken cancellationToken)
- {
- var parts = messageText.Split('|');
-
var localUrl = await _appHost.GetLocalApiUrl(cancellationToken).ConfigureAwait(false);
if (!string.IsNullOrEmpty(localUrl))
@@ -118,8 +50,16 @@ namespace Emby.Server.Implementations.Udp
Name = _appHost.FriendlyName
};
- await SendAsync(encoding.GetBytes(_json.SerializeToString(response)), endpoint, cancellationToken).ConfigureAwait(false);
+ try
+ {
+ await _udpSocket.SendToAsync(JsonSerializer.SerializeToUtf8Bytes(response), SocketFlags.None, endpoint).ConfigureAwait(false);
+ }
+ catch (SocketException ex)
+ {
+ _logger.LogError(ex, "Error sending response message");
+ }
+ var parts = messageText.Split('|');
if (parts.Length > 1)
{
_appHost.EnableLoopback(parts[1]);
@@ -132,161 +72,59 @@ namespace Emby.Server.Implementations.Udp
}
/// <summary>
- /// The _udp client
- /// </summary>
- private ISocket _udpClient;
- private readonly ISocketFactory _socketFactory;
-
- /// <summary>
/// Starts the specified port.
/// </summary>
/// <param name="port">The port.</param>
- public void Start(int port)
+ /// <param name="cancellationToken"></param>
+ public void Start(int port, CancellationToken cancellationToken)
{
- _udpClient = _socketFactory.CreateUdpSocket(port);
+ _endpoint = new IPEndPoint(IPAddress.Any, port);
- Task.Run(() => BeginReceive());
- }
-
- private readonly byte[] _receiveBuffer = new byte[8192];
-
- private void BeginReceive()
- {
- if (_isDisposed)
- {
- return;
- }
+ _udpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
+ _udpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
+ _udpSocket.Bind(_endpoint);
- try
- {
- var result = _udpClient.BeginReceive(_receiveBuffer, 0, _receiveBuffer.Length, OnReceiveResult);
-
- if (result.CompletedSynchronously)
- {
- OnReceiveResult(result);
- }
- }
- catch (ObjectDisposedException)
- {
- //TODO Investigate and properly fix.
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error receiving udp message");
- }
+ _ = Task.Run(async () => await BeginReceiveAsync(cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false);
}
- private void OnReceiveResult(IAsyncResult result)
+ private async Task BeginReceiveAsync(CancellationToken cancellationToken)
{
- if (_isDisposed)
- {
- return;
- }
-
- try
- {
- var socketResult = _udpClient.EndReceive(result);
-
- OnMessageReceived(socketResult);
- }
- catch (ObjectDisposedException)
- {
- //TODO Investigate and properly fix.
- }
- catch (Exception ex)
+ while (!cancellationToken.IsCancellationRequested)
{
- _logger.LogError(ex, "Error receiving udp message");
- }
-
- BeginReceive();
- }
-
- /// <summary>
- /// Called when [message received].
- /// </summary>
- /// <param name="message">The message.</param>
- private void OnMessageReceived(SocketReceiveResult message)
- {
- if (_isDisposed)
- {
- return;
- }
-
- if (message.RemoteEndPoint.Port == 0)
- {
- return;
- }
-
- try
- {
- OnMessageReceived(new GenericEventArgs<SocketReceiveResult>
+ try
{
- Argument = message
- });
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error handling UDP message");
- }
- }
-
- /// <summary>
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
- /// </summary>
- public void Dispose()
- {
- Dispose(true);
- }
+ var result = await _udpSocket.ReceiveFromAsync(_receiveBuffer, SocketFlags.None, _endpoint).ConfigureAwait(false);
- /// <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)
- {
- _isDisposed = true;
+ cancellationToken.ThrowIfCancellationRequested();
- if (_udpClient != null)
+ var text = Encoding.UTF8.GetString(_receiveBuffer, 0, result.ReceivedBytes);
+ if (text.Contains("who is JellyfinServer?", StringComparison.OrdinalIgnoreCase))
+ {
+ await RespondToV2Message(text, result.RemoteEndPoint, cancellationToken).ConfigureAwait(false);
+ }
+ }
+ catch (SocketException ex)
{
- _udpClient.Dispose();
+ _logger.LogError(ex, "Failed to receive data drom socket");
+ }
+ catch (OperationCanceledException)
+ {
+ // Don't throw
}
}
}
- public async Task SendAsync(byte[] bytes, IPEndPoint remoteEndPoint, CancellationToken cancellationToken)
+ /// <inheritdoc />
+ public void Dispose()
{
- if (_isDisposed)
+ if (_disposed)
{
- throw new ObjectDisposedException(GetType().Name);
- }
-
- if (bytes == null)
- {
- throw new ArgumentNullException(nameof(bytes));
- }
-
- if (remoteEndPoint == null)
- {
- throw new ArgumentNullException(nameof(remoteEndPoint));
+ return;
}
- try
- {
- await _udpClient.SendToAsync(bytes, 0, bytes.Length, remoteEndPoint, cancellationToken).ConfigureAwait(false);
+ _udpSocket?.Dispose();
- _logger.LogInformation("Udp message sent to {remoteEndPoint}", remoteEndPoint);
- }
- catch (OperationCanceledException)
- {
-
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error sending message to {remoteEndPoint}", remoteEndPoint);
- }
+ GC.SuppressFinalize(this);
}
}
-
}
diff --git a/Emby.Server.Implementations/WebSockets/WebSocketManager.cs b/Emby.Server.Implementations/WebSockets/WebSocketManager.cs
index efd97e4ff..31a7468fb 100644
--- a/Emby.Server.Implementations/WebSockets/WebSocketManager.cs
+++ b/Emby.Server.Implementations/WebSockets/WebSocketManager.cs
@@ -1,12 +1,10 @@
using System;
-using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
-using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Serialization;
using Microsoft.Extensions.Logging;
diff --git a/Jellyfin.Api/Controllers/StartupController.cs b/Jellyfin.Api/Controllers/StartupController.cs
index 1014c8c56..afc9b8f3d 100644
--- a/Jellyfin.Api/Controllers/StartupController.cs
+++ b/Jellyfin.Api/Controllers/StartupController.cs
@@ -96,7 +96,6 @@ namespace Jellyfin.Api.Controllers
public StartupUserDto GetFirstUser()
{
var user = _userManager.Users.First();
-
return new StartupUserDto
{
Name = user.Name,
diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj
index 73ffaa53d..4241d9b95 100644
--- a/Jellyfin.Api/Jellyfin.Api.csproj
+++ b/Jellyfin.Api/Jellyfin.Api.csproj
@@ -8,9 +8,9 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication" Version="2.2.0" />
- <PackageReference Include="Microsoft.AspNetCore.Authorization" Version="3.0.0" />
+ <PackageReference Include="Microsoft.AspNetCore.Authorization" Version="3.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" />
- <PackageReference Include="Swashbuckle.AspNetCore" Version="5.0.0-rc4" />
+ <PackageReference Include="Swashbuckle.AspNetCore" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
@@ -19,7 +19,7 @@
<!-- Code Analyzers-->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
- <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.7" 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/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj
index febb1adab..f9ce0bbe1 100644
--- a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj
+++ b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj
@@ -12,8 +12,8 @@
</ItemGroup>
<ItemGroup>
- <PackageReference Include="SkiaSharp" Version="1.68.0" />
- <PackageReference Include="SkiaSharp.NativeAssets.Linux" Version="1.68.0" />
+ <PackageReference Include="SkiaSharp" Version="1.68.1" />
+ <PackageReference Include="SkiaSharp.NativeAssets.Linux" Version="1.68.1" />
<PackageReference Include="Jellyfin.SkiaSharp.NativeAssets.LinuxArm" Version="1.68.0" />
</ItemGroup>
@@ -25,7 +25,7 @@
<!-- Code analysers-->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
- <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.7" 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/Jellyfin.Drawing.Skia/SkiaCodecException.cs b/Jellyfin.Drawing.Skia/SkiaCodecException.cs
index 8158b846d..1d2db5515 100644
--- a/Jellyfin.Drawing.Skia/SkiaCodecException.cs
+++ b/Jellyfin.Drawing.Skia/SkiaCodecException.cs
@@ -1,4 +1,3 @@
-using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using SkiaSharp;
diff --git a/Jellyfin.Drawing.Skia/SkiaEncoder.cs b/Jellyfin.Drawing.Skia/SkiaEncoder.cs
index b080b3e6a..2ea690650 100644
--- a/Jellyfin.Drawing.Skia/SkiaEncoder.cs
+++ b/Jellyfin.Drawing.Skia/SkiaEncoder.cs
@@ -6,7 +6,6 @@ using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Extensions;
using MediaBrowser.Model.Drawing;
-using MediaBrowser.Model.Globalization;
using Microsoft.Extensions.Logging;
using SkiaSharp;
using static Jellyfin.Drawing.Skia.SkiaHelper;
@@ -18,27 +17,23 @@ namespace Jellyfin.Drawing.Skia
/// </summary>
public class SkiaEncoder : IImageEncoder
{
- private readonly ILogger _logger;
- private readonly IApplicationPaths _appPaths;
- private readonly ILocalizationManager _localizationManager;
-
private static readonly HashSet<string> _transparentImageTypes
= new HashSet<string>(StringComparer.OrdinalIgnoreCase) { ".png", ".gif", ".webp" };
+ private readonly ILogger _logger;
+ private readonly IApplicationPaths _appPaths;
+
/// <summary>
/// Initializes a new instance of the <see cref="SkiaEncoder"/> class.
/// </summary>
/// <param name="logger">The application logger.</param>
/// <param name="appPaths">The application paths.</param>
- /// <param name="localizationManager">The application localization manager.</param>
public SkiaEncoder(
ILogger<SkiaEncoder> logger,
- IApplicationPaths appPaths,
- ILocalizationManager localizationManager)
+ IApplicationPaths appPaths)
{
_logger = logger;
_appPaths = appPaths;
- _localizationManager = localizationManager;
}
/// <inheritdoc/>
@@ -235,9 +230,12 @@ namespace Jellyfin.Drawing.Skia
private bool RequiresSpecialCharacterHack(string path)
{
- if (_localizationManager.HasUnicodeCategory(path, UnicodeCategory.OtherLetter))
+ for (int i = 0; i < path.Length; i++)
{
- return true;
+ if (char.GetUnicodeCategory(path[i]) == UnicodeCategory.OtherLetter)
+ {
+ return true;
+ }
}
if (HasDiacritics(path))
diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs
index 8b4b61e29..ed5968ad6 100644
--- a/Jellyfin.Server/CoreAppHost.cs
+++ b/Jellyfin.Server/CoreAppHost.cs
@@ -23,23 +23,20 @@ namespace Jellyfin.Server
/// <param name="fileSystem">The <see cref="IFileSystem" /> to be used by the <see cref="CoreAppHost" />.</param>
/// <param name="imageEncoder">The <see cref="IImageEncoder" /> 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="configuration">The <see cref="IConfiguration" /> to be used by the <see cref="CoreAppHost" />.</param>
public CoreAppHost(
ServerApplicationPaths applicationPaths,
ILoggerFactory loggerFactory,
StartupOptions options,
IFileSystem fileSystem,
IImageEncoder imageEncoder,
- INetworkManager networkManager,
- IConfiguration configuration)
+ INetworkManager networkManager)
: base(
applicationPaths,
loggerFactory,
options,
fileSystem,
imageEncoder,
- networkManager,
- configuration)
+ networkManager)
{
}
diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj
index a41112191..bc18f11fd 100644
--- a/Jellyfin.Server/Jellyfin.Server.csproj
+++ b/Jellyfin.Server/Jellyfin.Server.csproj
@@ -6,9 +6,6 @@
<TargetFramework>netcoreapp3.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
- </PropertyGroup>
-
- <PropertyGroup>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Nullable>enable</Nullable>
</PropertyGroup>
@@ -27,10 +24,10 @@
<!-- Code Analyzers-->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
- <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.7" />
- <PackageReference Include="SerilogAnalyzer" Version="0.15.0" />
- <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" />
- <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" />
+ <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" />
</ItemGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
@@ -38,15 +35,16 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="CommandLineParser" Version="2.6.0" />
- <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.0.1" />
- <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.0.1" />
+ <PackageReference Include="CommandLineParser" Version="2.7.82" />
+ <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.1.1" />
+ <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.1" />
<PackageReference Include="Serilog.AspNetCore" Version="3.2.0" />
+ <PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="3.1.0" />
<PackageReference Include="Serilog.Sinks.Async" Version="1.4.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
<PackageReference Include="Serilog.Sinks.File" Version="4.1.0" />
- <PackageReference Include="Serilog.Sinks.Graylog" Version="2.1.1" />
+ <PackageReference Include="Serilog.Sinks.Graylog" Version="2.1.2" />
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.0.2" />
<PackageReference Include="SQLitePCLRaw.provider.sqlite3.netstandard11" Version="1.1.14" />
</ItemGroup>
diff --git a/Jellyfin.Server/Migrations/IMigrationRoutine.cs b/Jellyfin.Server/Migrations/IMigrationRoutine.cs
new file mode 100644
index 000000000..eab995d67
--- /dev/null
+++ b/Jellyfin.Server/Migrations/IMigrationRoutine.cs
@@ -0,0 +1,28 @@
+using System;
+using Microsoft.Extensions.Logging;
+
+namespace Jellyfin.Server.Migrations
+{
+ /// <summary>
+ /// Interface that describes a migration routine.
+ /// </summary>
+ internal interface IMigrationRoutine
+ {
+ /// <summary>
+ /// Gets the unique id for this migration. This should never be modified after the migration has been created.
+ /// </summary>
+ public Guid Id { get; }
+
+ /// <summary>
+ /// Gets the display name of the migration.
+ /// </summary>
+ public string Name { get; }
+
+ /// <summary>
+ /// Execute the migration routine.
+ /// </summary>
+ /// <param name="host">Host that hosts current version.</param>
+ /// <param name="logger">Host logger.</param>
+ public void Perform(CoreAppHost host, ILogger logger);
+ }
+}
diff --git a/Jellyfin.Server/Migrations/MigrationOptions.cs b/Jellyfin.Server/Migrations/MigrationOptions.cs
new file mode 100644
index 000000000..816dd9ee7
--- /dev/null
+++ b/Jellyfin.Server/Migrations/MigrationOptions.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+
+namespace Jellyfin.Server.Migrations
+{
+ /// <summary>
+ /// Configuration part that holds all migrations that were applied.
+ /// </summary>
+ public class MigrationOptions
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MigrationOptions"/> class.
+ /// </summary>
+ public MigrationOptions()
+ {
+ Applied = new List<(Guid Id, string Name)>();
+ }
+
+ /// <summary>
+ /// Gets the list of applied migration routine names.
+ /// </summary>
+ public List<(Guid Id, string Name)> Applied { get; }
+ }
+}
diff --git a/Jellyfin.Server/Migrations/MigrationRunner.cs b/Jellyfin.Server/Migrations/MigrationRunner.cs
new file mode 100644
index 000000000..b5ea04dca
--- /dev/null
+++ b/Jellyfin.Server/Migrations/MigrationRunner.cs
@@ -0,0 +1,73 @@
+using System;
+using System.Linq;
+using MediaBrowser.Common.Configuration;
+using Microsoft.Extensions.Logging;
+
+namespace Jellyfin.Server.Migrations
+{
+ /// <summary>
+ /// The class that knows which migrations to apply and how to apply them.
+ /// </summary>
+ public sealed class MigrationRunner
+ {
+ /// <summary>
+ /// The list of known migrations, in order of applicability.
+ /// </summary>
+ internal static readonly IMigrationRoutine[] Migrations =
+ {
+ new Routines.DisableTranscodingThrottling(),
+ new Routines.CreateUserLoggingConfigFile()
+ };
+
+ /// <summary>
+ /// Run all needed migrations.
+ /// </summary>
+ /// <param name="host">CoreAppHost that hosts current version.</param>
+ /// <param name="loggerFactory">Factory for making the logger.</param>
+ public static void Run(CoreAppHost host, ILoggerFactory loggerFactory)
+ {
+ var logger = loggerFactory.CreateLogger<MigrationRunner>();
+ var migrationOptions = ((IConfigurationManager)host.ServerConfigurationManager).GetConfiguration<MigrationOptions>(MigrationsListStore.StoreKey);
+
+ if (!host.ServerConfigurationManager.Configuration.IsStartupWizardCompleted && migrationOptions.Applied.Count == 0)
+ {
+ // If startup wizard is not finished, this is a fresh install.
+ // Don't run any migrations, just mark all of them as applied.
+ logger.LogInformation("Marking all known migrations as applied because this is a fresh install");
+ migrationOptions.Applied.AddRange(Migrations.Select(m => (m.Id, m.Name)));
+ host.ServerConfigurationManager.SaveConfiguration(MigrationsListStore.StoreKey, migrationOptions);
+ return;
+ }
+
+ var appliedMigrationIds = migrationOptions.Applied.Select(m => m.Id).ToHashSet();
+
+ for (var i = 0; i < Migrations.Length; i++)
+ {
+ var migrationRoutine = Migrations[i];
+ if (appliedMigrationIds.Contains(migrationRoutine.Id))
+ {
+ logger.LogDebug("Skipping migration '{Name}' since it is already applied", migrationRoutine.Name);
+ continue;
+ }
+
+ logger.LogInformation("Applying migration '{Name}'", migrationRoutine.Name);
+
+ try
+ {
+ migrationRoutine.Perform(host, logger);
+ }
+ catch (Exception ex)
+ {
+ logger.LogError(ex, "Could not apply migration '{Name}'", migrationRoutine.Name);
+ throw;
+ }
+
+ // Mark the migration as completed
+ logger.LogInformation("Migration '{Name}' applied successfully", migrationRoutine.Name);
+ migrationOptions.Applied.Add((migrationRoutine.Id, migrationRoutine.Name));
+ host.ServerConfigurationManager.SaveConfiguration(MigrationsListStore.StoreKey, migrationOptions);
+ logger.LogDebug("Migration '{Name}' marked as applied in configuration.", migrationRoutine.Name);
+ }
+ }
+ }
+}
diff --git a/Jellyfin.Server/Migrations/MigrationsFactory.cs b/Jellyfin.Server/Migrations/MigrationsFactory.cs
new file mode 100644
index 000000000..23c1b1ee6
--- /dev/null
+++ b/Jellyfin.Server/Migrations/MigrationsFactory.cs
@@ -0,0 +1,20 @@
+using System.Collections.Generic;
+using MediaBrowser.Common.Configuration;
+
+namespace Jellyfin.Server.Migrations
+{
+ /// <summary>
+ /// A factory that can find a persistent file of the migration configuration, which lists all applied migrations.
+ /// </summary>
+ public class MigrationsFactory : IConfigurationFactory
+ {
+ /// <inheritdoc/>
+ public IEnumerable<ConfigurationStore> GetConfigurations()
+ {
+ return new[]
+ {
+ new MigrationsListStore()
+ };
+ }
+ }
+}
diff --git a/Jellyfin.Server/Migrations/MigrationsListStore.cs b/Jellyfin.Server/Migrations/MigrationsListStore.cs
new file mode 100644
index 000000000..7a1ca6671
--- /dev/null
+++ b/Jellyfin.Server/Migrations/MigrationsListStore.cs
@@ -0,0 +1,24 @@
+using MediaBrowser.Common.Configuration;
+
+namespace Jellyfin.Server.Migrations
+{
+ /// <summary>
+ /// A configuration that lists all the migration routines that were applied.
+ /// </summary>
+ public class MigrationsListStore : ConfigurationStore
+ {
+ /// <summary>
+ /// The name of the configuration in the storage.
+ /// </summary>
+ public static readonly string StoreKey = "migrations";
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MigrationsListStore"/> class.
+ /// </summary>
+ public MigrationsListStore()
+ {
+ ConfigurationType = typeof(MigrationOptions);
+ Key = StoreKey;
+ }
+ }
+}
diff --git a/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs b/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs
new file mode 100644
index 000000000..3bc32c047
--- /dev/null
+++ b/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs
@@ -0,0 +1,73 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using MediaBrowser.Common.Configuration;
+using Microsoft.Extensions.Logging;
+using Newtonsoft.Json.Linq;
+
+namespace Jellyfin.Server.Migrations.Routines
+{
+ /// <summary>
+ /// Migration to initialize the user logging configuration file "logging.user.json".
+ /// If the deprecated logging.json file exists and has a custom config, it will be used as logging.user.json,
+ /// otherwise a blank file will be created.
+ /// </summary>
+ internal class CreateUserLoggingConfigFile : IMigrationRoutine
+ {
+ /// <summary>
+ /// File history for logging.json as existed during this migration creation. The contents for each has been minified.
+ /// </summary>
+ private readonly List<string> _defaultConfigHistory = new List<string>
+ {
+ // 9a6c27947353585391e211aa88b925f81e8cd7b9
+ @"{""Serilog"":{""MinimumLevel"":{""Default"":""Information"",""Override"":{""Microsoft"":""Warning"",""System"":""Warning""}},""WriteTo"":[{""Name"":""Console"",""Args"":{""outputTemplate"":""[{Timestamp:HH:mm:ss}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message:lj}{NewLine}{Exception}""}},{""Name"":""Async"",""Args"":{""configure"":[{""Name"":""File"",""Args"":{""path"":""%JELLYFIN_LOG_DIR%//log_.log"",""rollingInterval"":""Day"",""retainedFileCountLimit"":3,""rollOnFileSizeLimit"":true,""fileSizeLimitBytes"":100000000,""outputTemplate"":""[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message}{NewLine}{Exception}""}}]}}],""Enrich"":[""FromLogContext"",""WithThreadId""]}}",
+ // 71bdcd730705a714ee208eaad7290b7c68df3885
+ @"{""Serilog"":{""MinimumLevel"":""Information"",""WriteTo"":[{""Name"":""Console"",""Args"":{""outputTemplate"":""[{Timestamp:HH:mm:ss}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message:lj}{NewLine}{Exception}""}},{""Name"":""Async"",""Args"":{""configure"":[{""Name"":""File"",""Args"":{""path"":""%JELLYFIN_LOG_DIR%//log_.log"",""rollingInterval"":""Day"",""retainedFileCountLimit"":3,""rollOnFileSizeLimit"":true,""fileSizeLimitBytes"":100000000,""outputTemplate"":""[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message}{NewLine}{Exception}""}}]}}],""Enrich"":[""FromLogContext"",""WithThreadId""]}}",
+ // a44936f97f8afc2817d3491615a7cfe1e31c251c
+ @"{""Serilog"":{""MinimumLevel"":""Information"",""WriteTo"":[{""Name"":""Console"",""Args"":{""outputTemplate"":""[{Timestamp:HH:mm:ss}] [{Level:u3}] {Message:lj}{NewLine}{Exception}""}},{""Name"":""File"",""Args"":{""path"":""%JELLYFIN_LOG_DIR%//log_.log"",""rollingInterval"":""Day"",""outputTemplate"":""[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] {Message}{NewLine}{Exception}""}}]}}",
+ // 7af3754a11ad5a4284f107997fb5419a010ce6f3
+ @"{""Serilog"":{""MinimumLevel"":""Information"",""WriteTo"":[{""Name"":""Console"",""Args"":{""outputTemplate"":""[{Timestamp:HH:mm:ss}] [{Level:u3}] {Message:lj}{NewLine}{Exception}""}},{""Name"":""Async"",""Args"":{""configure"":[{""Name"":""File"",""Args"":{""path"":""%JELLYFIN_LOG_DIR%//log_.log"",""rollingInterval"":""Day"",""outputTemplate"":""[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] {Message}{NewLine}{Exception}""}}]}}]}}",
+ // 60691349a11f541958e0b2247c9abc13cb40c9fb
+ @"{""Serilog"":{""MinimumLevel"":""Information"",""WriteTo"":[{""Name"":""Console"",""Args"":{""outputTemplate"":""[{Timestamp:HH:mm:ss}] [{Level:u3}] {Message:lj}{NewLine}{Exception}""}},{""Name"":""Async"",""Args"":{""configure"":[{""Name"":""File"",""Args"":{""path"":""%JELLYFIN_LOG_DIR%//log_.log"",""rollingInterval"":""Day"",""retainedFileCountLimit"":3,""rollOnFileSizeLimit"":true,""fileSizeLimitBytes"":100000000,""outputTemplate"":""[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] {Message}{NewLine}{Exception}""}}]}}]}}",
+ // 65fe243afbcc4b596cf8726708c1965cd34b5f68
+ @"{""Serilog"":{""MinimumLevel"":""Information"",""WriteTo"":[{""Name"":""Console"",""Args"":{""outputTemplate"":""[{Timestamp:HH:mm:ss}] [{Level:u3}] {ThreadId} {SourceContext}: {Message:lj} {NewLine}{Exception}""}},{""Name"":""Async"",""Args"":{""configure"":[{""Name"":""File"",""Args"":{""path"":""%JELLYFIN_LOG_DIR%//log_.log"",""rollingInterval"":""Day"",""retainedFileCountLimit"":3,""rollOnFileSizeLimit"":true,""fileSizeLimitBytes"":100000000,""outputTemplate"":""[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] {ThreadId} {SourceContext}:{Message} {NewLine}{Exception}""}}]}}],""Enrich"":[""FromLogContext"",""WithThreadId""]}}",
+ // 96c9af590494aa8137d5a061aaf1e68feee60b67
+ @"{""Serilog"":{""MinimumLevel"":""Information"",""WriteTo"":[{""Name"":""Console"",""Args"":{""outputTemplate"":""[{Timestamp:HH:mm:ss}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message:lj}{NewLine}{Exception}""}},{""Name"":""Async"",""Args"":{""configure"":[{""Name"":""File"",""Args"":{""path"":""%JELLYFIN_LOG_DIR%//log_.log"",""rollingInterval"":""Day"",""retainedFileCountLimit"":3,""rollOnFileSizeLimit"":true,""fileSizeLimitBytes"":100000000,""outputTemplate"":""[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] [{ThreadId}] {SourceContext}:{Message}{NewLine}{Exception}""}}]}}],""Enrich"":[""FromLogContext"",""WithThreadId""]}}",
+ };
+
+ /// <inheritdoc/>
+ public Guid Id => Guid.Parse("{EF103419-8451-40D8-9F34-D1A8E93A1679}");
+
+ /// <inheritdoc/>
+ public string Name => "CreateLoggingConfigHeirarchy";
+
+ /// <inheritdoc/>
+ public void Perform(CoreAppHost host, ILogger logger)
+ {
+ var logDirectory = host.Resolve<IApplicationPaths>().ConfigurationDirectoryPath;
+ var existingConfigPath = Path.Combine(logDirectory, "logging.json");
+
+ // If the existing logging.json config file is unmodified, then 'reset' it by moving it to 'logging.old.json'
+ // NOTE: This config file has 'reloadOnChange: true', so this change will take effect immediately even though it has already been loaded
+ if (File.Exists(existingConfigPath) && ExistingConfigUnmodified(existingConfigPath))
+ {
+ File.Move(existingConfigPath, Path.Combine(logDirectory, "logging.old.json"));
+ }
+ }
+
+ /// <summary>
+ /// Check if the existing logging.json file has not been modified by the user by comparing it to all the
+ /// versions in our git history. Until now, the file has never been migrated after first creation so users
+ /// could have any version from the git history.
+ /// </summary>
+ /// <exception cref="IOException"><paramref name="oldConfigPath"/> does not exist or could not be read.</exception>
+ private bool ExistingConfigUnmodified(string oldConfigPath)
+ {
+ var existingConfigJson = JToken.Parse(File.ReadAllText(oldConfigPath));
+ return _defaultConfigHistory
+ .Select(historicalConfigText => JToken.Parse(historicalConfigText))
+ .Any(historicalConfigJson => JToken.DeepEquals(existingConfigJson, historicalConfigJson));
+ }
+ }
+}
diff --git a/Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs b/Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs
new file mode 100644
index 000000000..673f0e415
--- /dev/null
+++ b/Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs
@@ -0,0 +1,35 @@
+using System;
+using System.IO;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Model.Configuration;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Logging;
+
+namespace Jellyfin.Server.Migrations.Routines
+{
+ /// <summary>
+ /// Disable transcode throttling for all installations since it is currently broken for certain video formats.
+ /// </summary>
+ internal class DisableTranscodingThrottling : IMigrationRoutine
+ {
+ /// <inheritdoc/>
+ public Guid Id => Guid.Parse("{4124C2CD-E939-4FFB-9BE9-9B311C413638}");
+
+ /// <inheritdoc/>
+ public string Name => "DisableTranscodingThrottling";
+
+ /// <inheritdoc/>
+ public void Perform(CoreAppHost host, ILogger logger)
+ {
+ // Set EnableThrottling to false since it wasn't used before and may introduce issues
+ var encoding = ((IConfigurationManager)host.ServerConfigurationManager).GetConfiguration<EncodingOptions>("encoding");
+ if (encoding.EnableThrottling)
+ {
+ logger.LogInformation("Disabling transcoding throttling during migration");
+ encoding.EnableThrottling = false;
+
+ host.ServerConfigurationManager.SaveConfiguration("encoding", encoding);
+ }
+ }
+ }
+}
diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs
index 712990a1e..e9e852349 100644
--- a/Jellyfin.Server/Program.cs
+++ b/Jellyfin.Server/Program.cs
@@ -4,7 +4,6 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
-using System.Net.Security;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
@@ -27,6 +26,7 @@ using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Serilog;
+using Serilog.Events;
using Serilog.Extensions.Logging;
using SQLitePCL;
using ILogger = Microsoft.Extensions.Logging.ILogger;
@@ -38,6 +38,16 @@ namespace Jellyfin.Server
/// </summary>
public static class Program
{
+ /// <summary>
+ /// The name of logging configuration file containing application defaults.
+ /// </summary>
+ public static readonly string LoggingConfigFileDefault = "logging.default.json";
+
+ /// <summary>
+ /// The name of the logging configuration file containing the system-specific override settings.
+ /// </summary>
+ public static readonly string LoggingConfigFileSystem = "logging.json";
+
private static readonly CancellationTokenSource _tokenSource = new CancellationTokenSource();
private static readonly ILoggerFactory _loggerFactory = new SerilogLoggerFactory();
private static ILogger _logger = NullLogger.Instance;
@@ -102,10 +112,12 @@ namespace Jellyfin.Server
// $JELLYFIN_LOG_DIR needs to be set for the logger configuration manager
Environment.SetEnvironmentVariable("JELLYFIN_LOG_DIR", appPaths.LogDirectoryPath);
- IConfiguration appConfig = await CreateConfiguration(appPaths).ConfigureAwait(false);
-
- CreateLogger(appConfig, appPaths);
+ // Create an instance of the application configuration to use for application startup
+ await InitLoggingConfigFile(appPaths).ConfigureAwait(false);
+ IConfiguration startupConfig = CreateAppConfiguration(appPaths);
+ // Initialize logging framework
+ InitializeLoggingFramework(startupConfig, appPaths);
_logger = _loggerFactory.CreateLogger("Main");
// Log uncaught exceptions to the logging instead of std error
@@ -169,23 +181,23 @@ namespace Jellyfin.Server
_loggerFactory,
options,
new ManagedFileSystem(_loggerFactory.CreateLogger<ManagedFileSystem>(), appPaths),
- new NullImageEncoder(),
- new NetworkManager(_loggerFactory.CreateLogger<NetworkManager>()),
- appConfig);
+ GetImageEncoder(appPaths),
+ new NetworkManager(_loggerFactory.CreateLogger<NetworkManager>()));
try
{
ServiceCollection serviceCollection = new ServiceCollection();
- await appHost.InitAsync(serviceCollection).ConfigureAwait(false);
+ await appHost.InitAsync(serviceCollection, startupConfig).ConfigureAwait(false);
- var host = CreateWebHostBuilder(appHost, serviceCollection).Build();
+ var webHost = CreateWebHostBuilder(appHost, serviceCollection, appPaths).Build();
// A bit hacky to re-use service provider since ASP.NET doesn't allow a custom service collection.
- appHost.ServiceProvider = host.Services;
+ appHost.ServiceProvider = webHost.Services;
appHost.FindParts();
+ Migrations.MigrationRunner.Run(appHost, _loggerFactory);
try
{
- await host.StartAsync().ConfigureAwait(false);
+ await webHost.StartAsync().ConfigureAwait(false);
}
catch
{
@@ -193,8 +205,6 @@ namespace Jellyfin.Server
throw;
}
- appHost.ImageProcessor.ImageEncoder = GetImageEncoder(appPaths, appHost.LocalizationManager);
-
await appHost.RunStartupTasksAsync().ConfigureAwait(false);
stopWatch.Stop();
@@ -223,7 +233,7 @@ namespace Jellyfin.Server
}
}
- private static IWebHostBuilder CreateWebHostBuilder(ApplicationHost appHost, IServiceCollection serviceCollection)
+ private static IWebHostBuilder CreateWebHostBuilder(ApplicationHost appHost, IServiceCollection serviceCollection, IApplicationPaths appPaths)
{
return new WebHostBuilder()
.UseKestrel(options =>
@@ -238,7 +248,7 @@ namespace Jellyfin.Server
{
foreach (var address in addresses)
{
- _logger.LogInformation("Kestrel listening on {ipaddr}", address);
+ _logger.LogInformation("Kestrel listening on {IpAddress}", address);
options.Listen(address, appHost.HttpPort);
if (appHost.EnableHttps && appHost.Certificate != null)
@@ -263,6 +273,8 @@ namespace Jellyfin.Server
}
}
})
+ .ConfigureAppConfiguration(config => config.ConfigureAppConfiguration(appPaths))
+ .UseSerilog()
.UseContentRoot(appHost.ContentRoot)
.ConfigureServices(services =>
{
@@ -435,39 +447,51 @@ namespace Jellyfin.Server
return new ServerApplicationPaths(dataDir, logDir, configDir, cacheDir, webDir);
}
- private static async Task<IConfiguration> CreateConfiguration(IApplicationPaths appPaths)
+ /// <summary>
+ /// Initialize the logging configuration file using the bundled resource file as a default if it doesn't exist
+ /// already.
+ /// </summary>
+ private static async Task InitLoggingConfigFile(IApplicationPaths appPaths)
{
- const string ResourcePath = "Jellyfin.Server.Resources.Configuration.logging.json";
- string configPath = Path.Combine(appPaths.ConfigurationDirectoryPath, "logging.json");
-
- if (!File.Exists(configPath))
+ // Do nothing if the config file already exists
+ string configPath = Path.Combine(appPaths.ConfigurationDirectoryPath, LoggingConfigFileDefault);
+ if (File.Exists(configPath))
{
- // For some reason the csproj name is used instead of the assembly name
- using (Stream? resource = typeof(Program).Assembly.GetManifestResourceStream(ResourcePath))
- {
- if (resource == null)
- {
- throw new InvalidOperationException(
- string.Format(
- CultureInfo.InvariantCulture,
- "Invalid resource path: '{0}'",
- ResourcePath));
- }
-
- using Stream dst = File.Open(configPath, FileMode.CreateNew);
- await resource.CopyToAsync(dst).ConfigureAwait(false);
- }
+ return;
}
+ // Get a stream of the resource contents
+ // NOTE: The .csproj name is used instead of the assembly name in the resource path
+ const string ResourcePath = "Jellyfin.Server.Resources.Configuration.logging.json";
+ await using Stream? resource = typeof(Program).Assembly.GetManifestResourceStream(ResourcePath)
+ ?? throw new InvalidOperationException($"Invalid resource path: '{ResourcePath}'");
+
+ // Copy the resource contents to the expected file path for the config file
+ await using Stream dst = File.Open(configPath, FileMode.CreateNew);
+ await resource.CopyToAsync(dst).ConfigureAwait(false);
+ }
+
+ private static IConfiguration CreateAppConfiguration(IApplicationPaths appPaths)
+ {
return new ConfigurationBuilder()
+ .ConfigureAppConfiguration(appPaths)
+ .Build();
+ }
+
+ private static IConfigurationBuilder ConfigureAppConfiguration(this IConfigurationBuilder config, IApplicationPaths appPaths)
+ {
+ return config
.SetBasePath(appPaths.ConfigurationDirectoryPath)
.AddInMemoryCollection(ConfigurationOptions.Configuration)
- .AddJsonFile("logging.json", false, true)
- .AddEnvironmentVariables("JELLYFIN_")
- .Build();
+ .AddJsonFile(LoggingConfigFileDefault, optional: false, reloadOnChange: true)
+ .AddJsonFile(LoggingConfigFileSystem, optional: true, reloadOnChange: true)
+ .AddEnvironmentVariables("JELLYFIN_");
}
- private static void CreateLogger(IConfiguration configuration, IApplicationPaths appPaths)
+ /// <summary>
+ /// Initialize Serilog using configuration and fall back to defaults on failure.
+ /// </summary>
+ private static void InitializeLoggingFramework(IConfiguration configuration, IApplicationPaths appPaths)
{
try
{
@@ -475,26 +499,26 @@ namespace Jellyfin.Server
Serilog.Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.Enrich.FromLogContext()
+ .Enrich.WithThreadId()
.CreateLogger();
}
catch (Exception ex)
{
Serilog.Log.Logger = new LoggerConfiguration()
- .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss}] [{Level:u3}] {Message:lj}{NewLine}{Exception}")
+ .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message:lj}{NewLine}{Exception}")
.WriteTo.Async(x => x.File(
Path.Combine(appPaths.LogDirectoryPath, "log_.log"),
rollingInterval: RollingInterval.Day,
- outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] {Message}{NewLine}{Exception}"))
+ outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message}{NewLine}{Exception}"))
.Enrich.FromLogContext()
+ .Enrich.WithThreadId()
.CreateLogger();
Serilog.Log.Logger.Fatal(ex, "Failed to create/read logger configuration");
}
}
- private static IImageEncoder GetImageEncoder(
- IApplicationPaths appPaths,
- ILocalizationManager localizationManager)
+ private static IImageEncoder GetImageEncoder(IApplicationPaths appPaths)
{
try
{
@@ -503,8 +527,7 @@ namespace Jellyfin.Server
return new SkiaEncoder(
_loggerFactory.CreateLogger<SkiaEncoder>(),
- appPaths,
- localizationManager);
+ appPaths);
}
catch (Exception ex)
{
diff --git a/Jellyfin.Server/Resources/Configuration/logging.json b/Jellyfin.Server/Resources/Configuration/logging.json
index e85ef05af..f64a85219 100644
--- a/Jellyfin.Server/Resources/Configuration/logging.json
+++ b/Jellyfin.Server/Resources/Configuration/logging.json
@@ -1,11 +1,17 @@
{
"Serilog": {
- "MinimumLevel": "Information",
+ "MinimumLevel": {
+ "Default": "Information",
+ "Override": {
+ "Microsoft": "Warning",
+ "System": "Warning"
+ }
+ },
"WriteTo": [
{
"Name": "Console",
"Args": {
- "outputTemplate": "[{Timestamp:HH:mm:ss}] [{Level:u3}] {Message:lj}{NewLine}{Exception}"
+ "outputTemplate": "[{Timestamp:HH:mm:ss}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message:lj}{NewLine}{Exception}"
}
},
{
@@ -20,12 +26,13 @@
"retainedFileCountLimit": 3,
"rollOnFileSizeLimit": true,
"fileSizeLimitBytes": 100000000,
- "outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] {Message}{NewLine}{Exception}"
+ "outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message}{NewLine}{Exception}"
}
}
]
}
}
- ]
+ ],
+ "Enrich": [ "FromLogContext", "WithThreadId" ]
}
}
diff --git a/Jellyfin.Server/Startup.cs b/Jellyfin.Server/Startup.cs
index 3ee5fb8b5..4d7d56e9d 100644
--- a/Jellyfin.Server/Startup.cs
+++ b/Jellyfin.Server/Startup.cs
@@ -3,7 +3,6 @@ using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
-using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs
index 1a3657c92..4bd13df00 100644
--- a/MediaBrowser.Api/ApiEntryPoint.cs
+++ b/MediaBrowser.Api/ApiEntryPoint.cs
@@ -12,7 +12,6 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Session;
-using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Session;
using Microsoft.Extensions.Logging;
@@ -62,7 +61,7 @@ namespace MediaBrowser.Api
/// <param name="fileSystem">The file system.</param>
/// <param name="mediaSourceManager">The media source manager.</param>
public ApiEntryPoint(
- ILogger logger,
+ ILogger<ApiEntryPoint> logger,
ISessionManager sessionManager,
IServerConfigurationManager config,
IFileSystem fileSystem,
diff --git a/MediaBrowser.Api/Attachments/AttachmentService.cs b/MediaBrowser.Api/Attachments/AttachmentService.cs
new file mode 100644
index 000000000..1632ca1b0
--- /dev/null
+++ b/MediaBrowser.Api/Attachments/AttachmentService.cs
@@ -0,0 +1,63 @@
+using System;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.MediaEncoding;
+using MediaBrowser.Controller.Net;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Services;
+using Microsoft.Extensions.Logging;
+
+namespace MediaBrowser.Api.Attachments
+{
+ [Route("/Videos/{Id}/{MediaSourceId}/Attachments/{Index}", "GET", Summary = "Gets specified attachment.")]
+ public class GetAttachment
+ {
+ [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
+ public Guid Id { get; set; }
+
+ [ApiMember(Name = "MediaSourceId", Description = "MediaSourceId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
+ public string MediaSourceId { get; set; }
+
+ [ApiMember(Name = "Index", Description = "The attachment stream index", IsRequired = true, DataType = "int", ParameterType = "path", Verb = "GET")]
+ public int Index { get; set; }
+ }
+
+ public class AttachmentService : BaseApiService
+ {
+ private readonly ILibraryManager _libraryManager;
+ private readonly IAttachmentExtractor _attachmentExtractor;
+
+ public AttachmentService(
+ ILogger<AttachmentService> logger,
+ IServerConfigurationManager serverConfigurationManager,
+ IHttpResultFactory httpResultFactory,
+ ILibraryManager libraryManager,
+ IAttachmentExtractor attachmentExtractor)
+ : base(logger, serverConfigurationManager, httpResultFactory)
+ {
+ _libraryManager = libraryManager;
+ _attachmentExtractor = attachmentExtractor;
+ }
+
+ public async Task<object> Get(GetAttachment request)
+ {
+ var (attachment, attachmentStream) = await GetAttachment(request).ConfigureAwait(false);
+ var mime = string.IsNullOrWhiteSpace(attachment.MimeType) ? "application/octet-stream" : attachment.MimeType;
+
+ return ResultFactory.GetResult(Request, attachmentStream, mime);
+ }
+
+ private Task<(MediaAttachment, Stream)> GetAttachment(GetAttachment request)
+ {
+ var item = _libraryManager.GetItemById(request.Id);
+
+ return _attachmentExtractor.GetAttachment(item,
+ request.MediaSourceId,
+ request.Index,
+ CancellationToken.None);
+ }
+ }
+}
diff --git a/MediaBrowser.Api/EnvironmentService.cs b/MediaBrowser.Api/EnvironmentService.cs
index c6dbfb938..322b9805b 100644
--- a/MediaBrowser.Api/EnvironmentService.cs
+++ b/MediaBrowser.Api/EnvironmentService.cs
@@ -6,7 +6,6 @@ using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Net;
using MediaBrowser.Model.Services;
using Microsoft.Extensions.Logging;
diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs
index e94c1321f..af455987b 100644
--- a/MediaBrowser.Api/Images/ImageService.cs
+++ b/MediaBrowser.Api/Images/ImageService.cs
@@ -24,7 +24,7 @@ using Microsoft.Net.Http.Headers;
namespace MediaBrowser.Api.Images
{
/// <summary>
- /// Class GetItemImage
+ /// Class GetItemImage.
/// </summary>
[Route("/Items/{Id}/Images", "GET", Summary = "Gets information about an item's images")]
[Authenticated]
@@ -558,21 +558,6 @@ namespace MediaBrowser.Api.Images
throw new ResourceNotFoundException(string.Format("{0} does not have an image of type {1}", displayText, request.Type));
}
- IImageEnhancer[] supportedImageEnhancers;
- if (_imageProcessor.ImageEnhancers.Count > 0)
- {
- if (item == null)
- {
- item = _libraryManager.GetItemById(itemId);
- }
-
- supportedImageEnhancers = request.EnableImageEnhancers ? _imageProcessor.GetSupportedEnhancers(item, request.Type).ToArray() : Array.Empty<IImageEnhancer>();
- }
- else
- {
- supportedImageEnhancers = Array.Empty<IImageEnhancer>();
- }
-
bool cropwhitespace;
if (request.CropWhitespace.HasValue)
{
@@ -598,25 +583,25 @@ namespace MediaBrowser.Api.Images
{"realTimeInfo.dlna.org", "DLNA.ORG_TLAG=*"}
};
- return GetImageResult(item,
+ return GetImageResult(
+ item,
itemId,
request,
imageInfo,
cropwhitespace,
outputFormats,
- supportedImageEnhancers,
cacheDuration,
responseHeaders,
isHeadRequest);
}
- private async Task<object> GetImageResult(BaseItem item,
+ private async Task<object> GetImageResult(
+ BaseItem item,
Guid itemId,
ImageRequest request,
ItemImageInfo image,
bool cropwhitespace,
IReadOnlyCollection<ImageFormat> supportedFormats,
- IReadOnlyCollection<IImageEnhancer> enhancers,
TimeSpan? cacheDuration,
IDictionary<string, string> headers,
bool isHeadRequest)
@@ -624,7 +609,6 @@ namespace MediaBrowser.Api.Images
var options = new ImageProcessingOptions
{
CropWhiteSpace = cropwhitespace,
- Enhancers = enhancers,
Height = request.Height,
ImageIndex = request.Index ?? 0,
Image = image,
@@ -656,7 +640,7 @@ namespace MediaBrowser.Api.Images
IsHeadRequest = isHeadRequest,
Path = imageResult.Item1,
- FileShare = FileShareMode.Read
+ FileShare = FileShare.Read
}).ConfigureAwait(false);
}
diff --git a/MediaBrowser.Api/Images/RemoteImageService.cs b/MediaBrowser.Api/Images/RemoteImageService.cs
index 5a37d3730..f03f5efd8 100644
--- a/MediaBrowser.Api/Images/RemoteImageService.cs
+++ b/MediaBrowser.Api/Images/RemoteImageService.cs
@@ -274,11 +274,9 @@ namespace MediaBrowser.Api.Images
Directory.CreateDirectory(Path.GetDirectoryName(fullCachePath));
using (var stream = result.Content)
+ using (var filestream = new FileStream(fullCachePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true))
{
- using (var filestream = _fileSystem.GetFileStream(fullCachePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
- {
- await stream.CopyToAsync(filestream).ConfigureAwait(false);
- }
+ await stream.CopyToAsync(filestream).ConfigureAwait(false);
}
Directory.CreateDirectory(Path.GetDirectoryName(pointerCachePath));
diff --git a/MediaBrowser.Api/ItemLookupService.cs b/MediaBrowser.Api/ItemLookupService.cs
index ea5a99892..a76369a15 100644
--- a/MediaBrowser.Api/ItemLookupService.cs
+++ b/MediaBrowser.Api/ItemLookupService.cs
@@ -305,7 +305,7 @@ namespace MediaBrowser.Api
Directory.CreateDirectory(Path.GetDirectoryName(fullCachePath));
using (var stream = result.Content)
- using (var filestream = _fileSystem.GetFileStream(fullCachePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
+ using (var filestream = new FileStream(fullCachePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true))
{
await stream.CopyToAsync(filestream).ConfigureAwait(false);
}
diff --git a/MediaBrowser.Api/ItemUpdateService.cs b/MediaBrowser.Api/ItemUpdateService.cs
index 1847f7fde..c81e89ca3 100644
--- a/MediaBrowser.Api/ItemUpdateService.cs
+++ b/MediaBrowser.Api/ItemUpdateService.cs
@@ -2,7 +2,6 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
-using System.Threading.Tasks;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs
index b1ea3e262..15284958d 100644
--- a/MediaBrowser.Api/Library/LibraryService.cs
+++ b/MediaBrowser.Api/Library/LibraryService.cs
@@ -16,17 +16,13 @@ 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;
-using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
-using MediaBrowser.Controller.TV;
using MediaBrowser.Model.Activity;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Globalization;
-using MediaBrowser.Model.IO;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Services;
using Microsoft.Extensions.Logging;
@@ -819,7 +815,7 @@ namespace MediaBrowser.Api.Library
if (!string.IsNullOrWhiteSpace(filename))
{
// Kestrel doesn't support non-ASCII characters in headers
- if (Regex.IsMatch(filename, "[^[:ascii:]]"))
+ if (Regex.IsMatch(filename, @"[^\p{IsBasicLatin}]"))
{
// Manually encoding non-ASCII characters, following https://tools.ietf.org/html/rfc5987#section-3.2.2
headers[HeaderNames.ContentDisposition] = "attachment; filename*=UTF-8''" + WebUtility.UrlEncode(filename);
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index 5881e22a7..5029ce0bb 100644
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs
@@ -250,18 +250,18 @@ namespace MediaBrowser.Api.Playback
{
if (string.Equals(state.OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase))
{
- logFilePrefix = "ffmpeg-directstream";
+ logFilePrefix = "ffmpeg-remux";
}
else
{
- logFilePrefix = "ffmpeg-remux";
+ logFilePrefix = "ffmpeg-directstream";
}
}
var logFilePath = Path.Combine(ServerConfigurationManager.ApplicationPaths.LogDirectoryPath, logFilePrefix + "-" + Guid.NewGuid() + ".txt");
// FFMpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory.
- Stream logStream = FileSystem.GetFileStream(logFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true);
+ Stream logStream = new FileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true);
var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(Request.AbsoluteUri + Environment.NewLine + Environment.NewLine + JsonSerializer.SerializeToString(state.MediaSource) + Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine);
await logStream.WriteAsync(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length, cancellationTokenSource.Token).ConfigureAwait(false);
@@ -327,15 +327,19 @@ namespace MediaBrowser.Api.Playback
private bool EnableThrottling(StreamState state)
{
+ var encodingOptions = ServerConfigurationManager.GetEncodingOptions();
+
+ // enable throttling when NOT using hardware acceleration
+ if (encodingOptions.HardwareAccelerationType == string.Empty)
+ {
+ return state.InputProtocol == MediaProtocol.File &&
+ state.RunTimeTicks.HasValue &&
+ state.RunTimeTicks.Value >= TimeSpan.FromMinutes(5).Ticks &&
+ state.IsInputVideo &&
+ state.VideoType == VideoType.VideoFile &&
+ !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase);
+ }
return false;
- //// do not use throttling with hardware encoders
- //return state.InputProtocol == MediaProtocol.File &&
- // state.RunTimeTicks.HasValue &&
- // state.RunTimeTicks.Value >= TimeSpan.FromMinutes(5).Ticks &&
- // state.IsInputVideo &&
- // state.VideoType == VideoType.VideoFile &&
- // !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase) &&
- // string.Equals(GetVideoEncoder(state), "libx264", StringComparison.OrdinalIgnoreCase);
}
/// <summary>
@@ -759,7 +763,7 @@ namespace MediaBrowser.Api.Playback
if (mediaSource == null)
{
- var mediaSources = await MediaSourceManager.GetPlayackMediaSources(LibraryManager.GetItemById(request.Id), null, false, false, cancellationToken).ConfigureAwait(false);
+ var mediaSources = await MediaSourceManager.GetPlaybackMediaSources(LibraryManager.GetItemById(request.Id), null, false, false, cancellationToken).ConfigureAwait(false);
mediaSource = string.IsNullOrEmpty(request.MediaSourceId)
? mediaSources[0]
diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
index 5d0dc98dd..0cbfe4bdf 100644
--- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
@@ -168,7 +168,7 @@ namespace MediaBrowser.Api.Playback.Hls
private string GetLivePlaylistText(string path, int segmentLength)
{
- using (var stream = FileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite))
+ using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
using (var reader = new StreamReader(stream))
{
@@ -211,7 +211,7 @@ namespace MediaBrowser.Api.Playback.Hls
{
try
{
- // Need to use FileShareMode.ReadWrite because we're reading the file at the same time it's being written
+ // Need to use FileShare.ReadWrite because we're reading the file at the same time it's being written
using (var fileStream = GetPlaylistFileStream(playlist))
{
using (var reader = new StreamReader(fileStream))
@@ -252,11 +252,11 @@ namespace MediaBrowser.Api.Playback.Hls
try
{
- return FileSystem.GetFileStream(tmpPath, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, FileOpenOptions.SequentialScan);
+ return new FileStream(tmpPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, IODefaults.FileStreamBufferSize, FileOptions.SequentialScan);
}
catch (IOException)
{
- return FileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, FileOpenOptions.SequentialScan);
+ return new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, IODefaults.FileStreamBufferSize, FileOptions.SequentialScan);
}
}
diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
index 0178f53af..f03e481df 100644
--- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
@@ -16,7 +16,6 @@ using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Services;
@@ -537,7 +536,7 @@ namespace MediaBrowser.Api.Playback.Hls
return ResultFactory.GetStaticFileResult(Request, new StaticFileResultOptions
{
Path = segmentPath,
- FileShare = FileShareMode.ReadWrite,
+ FileShare = FileShare.ReadWrite,
OnComplete = () =>
{
Logger.LogDebug("finished serving {0}", segmentPath);
@@ -954,12 +953,12 @@ namespace MediaBrowser.Api.Playback.Hls
// Unable to force key frames to h264_qsv transcode
if (string.Equals(codec, "h264_qsv", StringComparison.OrdinalIgnoreCase))
{
- Logger.LogInformation("Bug Workaround: Disabling force_key_frames for h264_qsv");
- }
+ Logger.LogInformation("Bug Workaround: Disabling force_key_frames for h264_qsv");
+ }
else
{
args += " " + keyFrameArg;
- }
+ }
//args += " -mixed-refs 0 -refs 3 -x264opts b_pyramid=0:weightb=0:weightp=0";
@@ -1008,6 +1007,7 @@ namespace MediaBrowser.Api.Playback.Hls
Logger.LogInformation("Current HLS implementation doesn't support non-keyframe breaks but one is requested, ignoring that request");
state.BaseRequest.BreakOnNonKeyFrames = false;
}
+
var inputModifier = EncodingHelper.GetInputModifier(state, encodingOptions);
// If isEncoding is true we're actually starting ffmpeg
diff --git a/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs b/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs
index bb12ab1f0..87ccde2e0 100644
--- a/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs
+++ b/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs
@@ -140,7 +140,7 @@ namespace MediaBrowser.Api.Playback.Hls
var file = request.SegmentId + Path.GetExtension(Request.PathInfo);
file = Path.Combine(ServerConfigurationManager.GetTranscodePath(), file);
- return ResultFactory.GetStaticFileResult(Request, file, FileShareMode.ReadWrite);
+ return ResultFactory.GetStaticFileResult(Request, file, FileShare.ReadWrite);
}
private Task<object> GetFileResult(string path, string playlistPath)
@@ -150,7 +150,7 @@ namespace MediaBrowser.Api.Playback.Hls
return ResultFactory.GetStaticFileResult(Request, new StaticFileResultOptions
{
Path = path,
- FileShare = FileShareMode.ReadWrite,
+ FileShare = FileShare.ReadWrite,
OnComplete = () =>
{
if (transcodingJob != null)
diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs
index c3032416b..2aa5e2df1 100644
--- a/MediaBrowser.Api/Playback/MediaInfoService.cs
+++ b/MediaBrowser.Api/Playback/MediaInfoService.cs
@@ -1,6 +1,11 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1402
+#pragma warning disable SA1649
+
using System;
-using System.Collections.Generic;
+using System.Buffers;
using System.Globalization;
+using System.Text.Json;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -16,7 +21,6 @@ using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.MediaInfo;
-using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Services;
using MediaBrowser.Model.Session;
using Microsoft.Extensions.Logging;
@@ -54,7 +58,7 @@ namespace MediaBrowser.Api.Playback
public class GetBitrateTestBytes
{
[ApiMember(Name = "Size", Description = "Size", IsRequired = true, DataType = "int", ParameterType = "query", Verb = "GET")]
- public long Size { get; set; }
+ public int Size { get; set; }
public GetBitrateTestBytes()
{
@@ -72,7 +76,6 @@ namespace MediaBrowser.Api.Playback
private readonly INetworkManager _networkManager;
private readonly IMediaEncoder _mediaEncoder;
private readonly IUserManager _userManager;
- private readonly IJsonSerializer _json;
private readonly IAuthorizationContext _authContext;
public MediaInfoService(
@@ -85,7 +88,6 @@ namespace MediaBrowser.Api.Playback
INetworkManager networkManager,
IMediaEncoder mediaEncoder,
IUserManager userManager,
- IJsonSerializer json,
IAuthorizationContext authContext)
: base(logger, serverConfigurationManager, httpResultFactory)
{
@@ -95,20 +97,35 @@ namespace MediaBrowser.Api.Playback
_networkManager = networkManager;
_mediaEncoder = mediaEncoder;
_userManager = userManager;
- _json = json;
_authContext = authContext;
}
public object Get(GetBitrateTestBytes request)
{
- var bytes = new byte[request.Size];
+ const int MaxSize = 10_000_000;
+
+ var size = request.Size;
+
+ if (size <= 0)
+ {
+ throw new ArgumentException($"The requested size ({size}) is equal to or smaller than 0.", nameof(request));
+ }
- for (var i = 0; i < bytes.Length; i++)
+ if (size > MaxSize)
{
- bytes[i] = 0;
+ throw new ArgumentException($"The requested size ({size}) is larger than the max allowed value ({MaxSize}).", nameof(request));
}
- return ResultFactory.GetResult(null, bytes, "application/octet-stream");
+ byte[] buffer = ArrayPool<byte>.Shared.Rent(size);
+ try
+ {
+ new Random().NextBytes(buffer);
+ return ResultFactory.GetResult(null, buffer, "application/octet-stream");
+ }
+ finally
+ {
+ ArrayPool<byte>.Shared.Return(buffer);
+ }
}
public async Task<object> Get(GetPlaybackInfo request)
@@ -166,8 +183,7 @@ namespace MediaBrowser.Api.Playback
public void Post(CloseMediaSource request)
{
- var task = _mediaSourceManager.CloseLiveStream(request.LiveStreamId);
- Task.WaitAll(task);
+ _mediaSourceManager.CloseLiveStream(request.LiveStreamId).GetAwaiter().GetResult();
}
public async Task<PlaybackInfoResponse> GetPlaybackInfo(GetPostedPlaybackInfo request)
@@ -176,7 +192,7 @@ namespace MediaBrowser.Api.Playback
var profile = request.DeviceProfile;
- //Logger.LogInformation("GetPostedPlaybackInfo profile: {profile}", _json.SerializeToString(profile));
+ Logger.LogInformation("GetPostedPlaybackInfo profile: {@Profile}", profile);
if (profile == null)
{
@@ -215,9 +231,7 @@ namespace MediaBrowser.Api.Playback
StartTimeTicks = request.StartTimeTicks,
SubtitleStreamIndex = request.SubtitleStreamIndex,
UserId = request.UserId,
- OpenToken = mediaSource.OpenToken,
- //EnableMediaProbe = request.EnableMediaProbe
-
+ OpenToken = mediaSource.OpenToken
}).ConfigureAwait(false);
info.MediaSources = new MediaSourceInfo[] { openStreamResult.MediaSource };
@@ -247,42 +261,26 @@ namespace MediaBrowser.Api.Playback
return ToOptimizedResult(result);
}
- private T Clone<T>(T obj)
- {
- // 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?
-
- var json = _json.SerializeToString(obj);
- return _json.DeserializeFromString<T>(json);
- }
-
private async Task<PlaybackInfoResponse> GetPlaybackInfo(Guid id, Guid userId, string[] supportedLiveMediaTypes, string mediaSourceId = null, string liveStreamId = null)
{
var user = _userManager.GetUserById(userId);
var item = _libraryManager.GetItemById(id);
var result = new PlaybackInfoResponse();
+ MediaSourceInfo[] mediaSources;
if (string.IsNullOrWhiteSpace(liveStreamId))
{
- IEnumerable<MediaSourceInfo> mediaSources;
- try
- {
- // TODO handle supportedLiveMediaTypes ?
- mediaSources = await _mediaSourceManager.GetPlayackMediaSources(item, user, true, false, CancellationToken.None).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- mediaSources = new List<MediaSourceInfo>();
- Logger.LogError(ex, "Could not find media sources for item id {id}", id);
- // TODO PlaybackException ??
- //result.ErrorCode = ex.ErrorCode;
- }
- result.MediaSources = mediaSources.ToArray();
+ // TODO handle supportedLiveMediaTypes?
+ var mediaSourcesList = await _mediaSourceManager.GetPlaybackMediaSources(item, user, true, true, CancellationToken.None).ConfigureAwait(false);
- if (!string.IsNullOrWhiteSpace(mediaSourceId))
+ if (string.IsNullOrWhiteSpace(mediaSourceId))
+ {
+ mediaSources = mediaSourcesList.ToArray();
+ }
+ else
{
- result.MediaSources = result.MediaSources
+ mediaSources = mediaSourcesList
.Where(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase))
.ToArray();
}
@@ -291,11 +289,13 @@ namespace MediaBrowser.Api.Playback
{
var mediaSource = await _mediaSourceManager.GetLiveStream(liveStreamId, CancellationToken.None).ConfigureAwait(false);
- result.MediaSources = new MediaSourceInfo[] { mediaSource };
+ mediaSources = new MediaSourceInfo[] { mediaSource };
}
- if (result.MediaSources.Length == 0)
+ if (mediaSources.Length == 0)
{
+ result.MediaSources = Array.Empty<MediaSourceInfo>();
+
if (!result.ErrorCode.HasValue)
{
result.ErrorCode = PlaybackErrorCode.NoCompatibleStream;
@@ -303,7 +303,9 @@ namespace MediaBrowser.Api.Playback
}
else
{
- result.MediaSources = Clone(result.MediaSources);
+ // 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));
result.PlaySessionId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
}
@@ -311,7 +313,8 @@ namespace MediaBrowser.Api.Playback
return result;
}
- private void SetDeviceSpecificData(Guid itemId,
+ private void SetDeviceSpecificData(
+ Guid itemId,
PlaybackInfoResponse result,
DeviceProfile profile,
AuthorizationInfo auth,
@@ -339,7 +342,8 @@ namespace MediaBrowser.Api.Playback
SortMediaSources(result, maxBitrate);
}
- private void SetDeviceSpecificData(BaseItem item,
+ private void SetDeviceSpecificData(
+ BaseItem item,
MediaSourceInfo mediaSource,
DeviceProfile profile,
AuthorizationInfo auth,
@@ -383,10 +387,12 @@ namespace MediaBrowser.Api.Playback
{
mediaSource.SupportsDirectPlay = false;
}
+
if (!enableDirectStream)
{
mediaSource.SupportsDirectStream = false;
}
+
if (!enableTranscoding)
{
mediaSource.SupportsTranscoding = false;
@@ -405,10 +411,12 @@ namespace MediaBrowser.Api.Playback
user.Policy.EnableAudioPlaybackTranscoding);
}
+ // Beginning of Playback Determination: Attempt DirectPlay first
if (mediaSource.SupportsDirectPlay)
{
- if (mediaSource.IsRemote && forceDirectPlayRemoteMediaSource)
+ if (mediaSource.IsRemote && user.Policy.ForceRemoteSourceTranscoding)
{
+ mediaSource.SupportsDirectPlay = false;
}
else
{
@@ -434,9 +442,9 @@ namespace MediaBrowser.Api.Playback
}
// The MediaSource supports direct stream, now test to see if the client supports it
- var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ?
- streamBuilder.BuildAudioItem(options) :
- streamBuilder.BuildVideoItem(options);
+ var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase)
+ ? streamBuilder.BuildAudioItem(options)
+ : streamBuilder.BuildVideoItem(options);
if (streamInfo == null || !streamInfo.IsDirectStream)
{
@@ -455,36 +463,43 @@ namespace MediaBrowser.Api.Playback
if (mediaSource.SupportsDirectStream)
{
- options.MaxBitrate = GetMaxBitrate(maxBitrate, user);
-
- if (item is Audio)
+ if (mediaSource.IsRemote && user.Policy.ForceRemoteSourceTranscoding)
{
- if (!user.Policy.EnableAudioPlaybackTranscoding)
- {
- options.ForceDirectStream = true;
- }
+ mediaSource.SupportsDirectStream = false;
}
- else if (item is Video)
+ else
{
- if (!user.Policy.EnableAudioPlaybackTranscoding && !user.Policy.EnableVideoPlaybackTranscoding && !user.Policy.EnablePlaybackRemuxing)
+ options.MaxBitrate = GetMaxBitrate(maxBitrate, user);
+
+ if (item is Audio)
{
- options.ForceDirectStream = true;
+ if (!user.Policy.EnableAudioPlaybackTranscoding)
+ {
+ options.ForceDirectStream = true;
+ }
+ }
+ else if (item is Video)
+ {
+ if (!user.Policy.EnableAudioPlaybackTranscoding && !user.Policy.EnableVideoPlaybackTranscoding && !user.Policy.EnablePlaybackRemuxing)
+ {
+ options.ForceDirectStream = true;
+ }
}
- }
// The MediaSource supports direct stream, now test to see if the client supports it
- var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ?
- streamBuilder.BuildAudioItem(options) :
- streamBuilder.BuildVideoItem(options);
+ var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase)
+ ? streamBuilder.BuildAudioItem(options)
+ : streamBuilder.BuildVideoItem(options);
- if (streamInfo == null || !streamInfo.IsDirectStream)
- {
- mediaSource.SupportsDirectStream = false;
- }
+ if (streamInfo == null || !streamInfo.IsDirectStream)
+ {
+ mediaSource.SupportsDirectStream = false;
+ }
- if (streamInfo != null)
- {
- SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token);
+ if (streamInfo != null)
+ {
+ SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token);
+ }
}
}
@@ -493,35 +508,76 @@ namespace MediaBrowser.Api.Playback
options.MaxBitrate = GetMaxBitrate(maxBitrate, user);
// The MediaSource supports direct stream, now test to see if the client supports it
- var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ?
- streamBuilder.BuildAudioItem(options) :
- streamBuilder.BuildVideoItem(options);
+ var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase)
+ ? streamBuilder.BuildAudioItem(options)
+ : streamBuilder.BuildVideoItem(options);
- if (streamInfo != null)
+ if (mediaSource.IsRemote && user.Policy.ForceRemoteSourceTranscoding)
{
- streamInfo.PlaySessionId = playSessionId;
-
- if (streamInfo.PlayMethod == PlayMethod.Transcode)
+ if (streamInfo != null)
{
+ streamInfo.PlaySessionId = playSessionId;
streamInfo.StartPositionTicks = startTimeTicks;
mediaSource.TranscodingUrl = streamInfo.ToUrl("-", auth.Token).TrimStart('-');
+ mediaSource.TranscodingUrl += "&allowVideoStreamCopy=false";
+ if (!allowAudioStreamCopy)
+ {
+ mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false";
+ }
+ mediaSource.TranscodingContainer = streamInfo.Container;
+ mediaSource.TranscodingSubProtocol = streamInfo.SubProtocol;
+
+ // Do this after the above so that StartPositionTicks is set
+ SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token);
+ }
+ }
+ else
+ {
+ if (streamInfo != null)
+ {
+ streamInfo.PlaySessionId = playSessionId;
- if (!allowVideoStreamCopy)
+ if (streamInfo.PlayMethod == PlayMethod.Transcode)
{
- mediaSource.TranscodingUrl += "&allowVideoStreamCopy=false";
+ streamInfo.StartPositionTicks = startTimeTicks;
+ mediaSource.TranscodingUrl = streamInfo.ToUrl("-", auth.Token).TrimStart('-');
+
+ if (!allowVideoStreamCopy)
+ {
+ mediaSource.TranscodingUrl += "&allowVideoStreamCopy=false";
+ }
+ if (!allowAudioStreamCopy)
+ {
+ mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false";
+ }
+ mediaSource.TranscodingContainer = streamInfo.Container;
+ mediaSource.TranscodingSubProtocol = streamInfo.SubProtocol;
}
+
if (!allowAudioStreamCopy)
{
mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false";
}
+
mediaSource.TranscodingContainer = streamInfo.Container;
mediaSource.TranscodingSubProtocol = streamInfo.SubProtocol;
- }
- // Do this after the above so that StartPositionTicks is set
- SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token);
+ // Do this after the above so that StartPositionTicks is set
+ SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token);
+ }
}
}
+
+ foreach (var attachment in mediaSource.MediaAttachments)
+ {
+ attachment.DeliveryUrl = string.Format(
+ CultureInfo.InvariantCulture,
+ "{0}/Videos/{1}/{2}/Attachments/{3}",
+ ServerConfigurationManager.Configuration.BaseUrl,
+ item.Id,
+ mediaSource.Id,
+ attachment.Index);
+ }
}
private long? GetMaxBitrate(long? clientMaxBitrate, User user)
@@ -599,14 +655,11 @@ namespace MediaBrowser.Api.Playback
}).ThenBy(i =>
{
- switch (i.Protocol)
+ return i.Protocol switch
{
- case MediaProtocol.File:
- return 0;
- default:
- return 1;
- }
-
+ MediaProtocol.File => 0,
+ _ => 1,
+ };
}).ThenBy(i =>
{
if (maxBitrate.HasValue)
diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
index ed30dbba6..ed68219c9 100644
--- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
+++ b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
@@ -248,7 +248,7 @@ namespace MediaBrowser.Api.Playback.Progressive
// ContentType = contentType,
// IsHeadRequest = isHeadRequest,
// Path = outputPath,
- // FileShare = FileShareMode.ReadWrite,
+ // FileShare = FileShare.ReadWrite,
// OnComplete = () =>
// {
// if (transcodingJob != null)
diff --git a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs
index 660912065..a53b848f9 100644
--- a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs
+++ b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs
@@ -21,8 +21,6 @@ namespace MediaBrowser.Api.Playback.Progressive
private readonly CancellationToken _cancellationToken;
private readonly Dictionary<string, string> _outputHeaders;
- const int StreamCopyToBufferSize = 81920;
-
private long _bytesWritten = 0;
public long StartPosition { get; set; }
public bool AllowEndOfFile = true;
@@ -52,14 +50,14 @@ namespace MediaBrowser.Api.Playback.Progressive
private Stream GetInputStream(bool allowAsyncFileRead)
{
- var fileOpenOptions = FileOpenOptions.SequentialScan;
+ var fileOptions = FileOptions.SequentialScan;
if (allowAsyncFileRead)
{
- fileOpenOptions |= FileOpenOptions.Asynchronous;
+ fileOptions |= FileOptions.Asynchronous;
}
- return _fileSystem.GetFileStream(_path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, fileOpenOptions);
+ return new FileStream(_path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, IODefaults.FileStreamBufferSize, fileOptions);
}
public async Task WriteToAsync(Stream outputStream, CancellationToken cancellationToken)
@@ -127,7 +125,7 @@ namespace MediaBrowser.Api.Playback.Progressive
private async Task<int> CopyToInternalAsyncWithSyncRead(Stream source, Stream destination, CancellationToken cancellationToken)
{
- var array = new byte[StreamCopyToBufferSize];
+ var array = new byte[IODefaults.CopyToBufferSize];
int bytesRead;
int totalBytesRead = 0;
@@ -154,7 +152,7 @@ namespace MediaBrowser.Api.Playback.Progressive
private async Task<int> CopyToInternalAsync(Stream source, Stream destination, CancellationToken cancellationToken)
{
- var array = new byte[StreamCopyToBufferSize];
+ var array = new byte[IODefaults.CopyToBufferSize];
int bytesRead;
int totalBytesRead = 0;
diff --git a/MediaBrowser.Api/Playback/StreamRequest.cs b/MediaBrowser.Api/Playback/StreamRequest.cs
index 7626cc378..9ba8eda91 100644
--- a/MediaBrowser.Api/Playback/StreamRequest.cs
+++ b/MediaBrowser.Api/Playback/StreamRequest.cs
@@ -1,4 +1,3 @@
-using System;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Services;
diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs
index 7396b5c99..d5d2f58c0 100644
--- a/MediaBrowser.Api/Playback/StreamState.cs
+++ b/MediaBrowser.Api/Playback/StreamState.cs
@@ -103,7 +103,7 @@ namespace MediaBrowser.Api.Playback
_mediaSourceManager = mediaSourceManager;
}
- public override void ReportTranscodingProgress(TimeSpan? transcodingPosition, float framerate, double? percentComplete, long bytesTranscoded, int? bitRate)
+ public override void ReportTranscodingProgress(TimeSpan? transcodingPosition, float? framerate, double? percentComplete, long? bytesTranscoded, int? bitRate)
{
ApiEntryPoint.Instance.ReportTranscodingProgress(TranscodingJob, this, transcodingPosition, framerate, percentComplete, bytesTranscoded, bitRate);
}
diff --git a/MediaBrowser.Api/Playback/UniversalAudioService.cs b/MediaBrowser.Api/Playback/UniversalAudioService.cs
index 9cba9df13..cbf981dfe 100644
--- a/MediaBrowser.Api/Playback/UniversalAudioService.cs
+++ b/MediaBrowser.Api/Playback/UniversalAudioService.cs
@@ -74,7 +74,6 @@ namespace MediaBrowser.Api.Playback
[Authenticated]
public class UniversalAudioService : BaseApiService
{
- private readonly ILoggerFactory _loggerFactory;
private readonly EncodingHelper _encodingHelper;
public UniversalAudioService(
@@ -243,7 +242,6 @@ namespace MediaBrowser.Api.Playback
NetworkManager,
MediaEncoder,
UserManager,
- JsonSerializer,
AuthorizationContext)
{
Request = Request
@@ -300,6 +298,10 @@ namespace MediaBrowser.Api.Playback
var transcodingProfile = deviceProfile.TranscodingProfiles[0];
+ // hls segment container can only be mpegts or fmp4 per ffmpeg documentation
+ // TODO: remove this when we switch back to the segment muxer
+ var supportedHLSContainers = new string[] { "mpegts", "fmp4" };
+
var newRequest = new GetMasterHlsAudioPlaylist
{
AudioBitRate = isStatic ? (int?)null : Convert.ToInt32(Math.Min(request.MaxStreamingBitrate ?? 192000, int.MaxValue)),
@@ -312,7 +314,8 @@ namespace MediaBrowser.Api.Playback
PlaySessionId = playbackInfoResult.PlaySessionId,
StartTimeTicks = request.StartTimeTicks,
Static = isStatic,
- SegmentContainer = request.TranscodingContainer,
+ // fallback to mpegts if device reports some weird value unsupported by hls
+ SegmentContainer = Array.Exists(supportedHLSContainers, element => element == request.TranscodingContainer) ? request.TranscodingContainer : "mpegts",
AudioSampleRate = request.MaxAudioSampleRate,
MaxAudioBitDepth = request.MaxAudioBitDepth,
BreakOnNonKeyFrames = transcodingProfile.BreakOnNonKeyFrames,
diff --git a/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs b/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs
index d9530ffb7..14b9b3618 100644
--- a/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs
+++ b/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs
@@ -28,7 +28,7 @@ namespace MediaBrowser.Api.ScheduledTasks
/// <summary>
/// Initializes a new instance of the <see cref="ScheduledTasksWebSocketListener" /> class.
/// </summary>
- public ScheduledTasksWebSocketListener(ILogger logger, ITaskManager taskManager)
+ public ScheduledTasksWebSocketListener(ILogger<ScheduledTasksWebSocketListener> logger, ITaskManager taskManager)
: base(logger)
{
TaskManager = taskManager;
diff --git a/MediaBrowser.Api/Sessions/ApiKeyService.cs b/MediaBrowser.Api/Sessions/ApiKeyService.cs
new file mode 100644
index 000000000..5102ce0a7
--- /dev/null
+++ b/MediaBrowser.Api/Sessions/ApiKeyService.cs
@@ -0,0 +1,85 @@
+using System;
+using System.Globalization;
+using MediaBrowser.Controller;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Net;
+using MediaBrowser.Controller.Security;
+using MediaBrowser.Controller.Session;
+using MediaBrowser.Model.Services;
+using Microsoft.Extensions.Logging;
+
+namespace MediaBrowser.Api.Sessions
+{
+ [Route("/Auth/Keys", "GET")]
+ [Authenticated(Roles = "Admin")]
+ public class GetKeys
+ {
+ }
+
+ [Route("/Auth/Keys/{Key}", "DELETE")]
+ [Authenticated(Roles = "Admin")]
+ public class RevokeKey
+ {
+ [ApiMember(Name = "Key", Description = "Authentication key", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
+ public string Key { get; set; }
+ }
+
+ [Route("/Auth/Keys", "POST")]
+ [Authenticated(Roles = "Admin")]
+ public class CreateKey
+ {
+ [ApiMember(Name = "App", Description = "Name of the app using the authentication key", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
+ public string App { get; set; }
+ }
+
+ public class ApiKeyService : BaseApiService
+ {
+ private readonly ISessionManager _sessionManager;
+
+ private readonly IAuthenticationRepository _authRepo;
+
+ private readonly IServerApplicationHost _appHost;
+
+ public ApiKeyService(
+ ILogger<ApiKeyService> logger,
+ IServerConfigurationManager serverConfigurationManager,
+ IHttpResultFactory httpResultFactory,
+ ISessionManager sessionManager,
+ IServerApplicationHost appHost,
+ IAuthenticationRepository authRepo)
+ : base(logger, serverConfigurationManager, httpResultFactory)
+ {
+ _sessionManager = sessionManager;
+ _authRepo = authRepo;
+ _appHost = appHost;
+ }
+
+ public void Delete(RevokeKey request)
+ {
+ _sessionManager.RevokeToken(request.Key);
+ }
+
+ public void Post(CreateKey request)
+ {
+ _authRepo.Create(new AuthenticationInfo
+ {
+ AppName = request.App,
+ AccessToken = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture),
+ DateCreated = DateTime.UtcNow,
+ DeviceId = _appHost.SystemId,
+ DeviceName = _appHost.FriendlyName,
+ AppVersion = _appHost.ApplicationVersionString
+ });
+ }
+
+ public object Get(GetKeys request)
+ {
+ var result = _authRepo.Get(new AuthenticationInfoQuery
+ {
+ HasUser = false
+ });
+
+ return result;
+ }
+ }
+}
diff --git a/MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs b/MediaBrowser.Api/Sessions/SessionInfoWebSocketListener.cs
index f1a6622fb..d882aac88 100644
--- a/MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs
+++ b/MediaBrowser.Api/Sessions/SessionInfoWebSocketListener.cs
@@ -5,7 +5,7 @@ using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Session;
using Microsoft.Extensions.Logging;
-namespace MediaBrowser.Api.Session
+namespace MediaBrowser.Api.Sessions
{
/// <summary>
/// Class SessionInfoWebSocketListener
@@ -26,7 +26,7 @@ namespace MediaBrowser.Api.Session
/// <summary>
/// Initializes a new instance of the <see cref="SessionInfoWebSocketListener"/> class.
/// </summary>
- public SessionInfoWebSocketListener(ILogger logger, ISessionManager sessionManager)
+ public SessionInfoWebSocketListener(ILogger<SessionInfoWebSocketListener> logger, ISessionManager sessionManager)
: base(logger)
{
_sessionManager = sessionManager;
diff --git a/MediaBrowser.Api/Session/SessionsService.cs b/MediaBrowser.Api/Sessions/SessionService.cs
index 700861c55..020bb5042 100644
--- a/MediaBrowser.Api/Session/SessionsService.cs
+++ b/MediaBrowser.Api/Sessions/SessionService.cs
@@ -1,40 +1,37 @@
using System;
-using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
-using MediaBrowser.Controller.Security;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Services;
using MediaBrowser.Model.Session;
using Microsoft.Extensions.Logging;
-namespace MediaBrowser.Api.Session
+namespace MediaBrowser.Api.Sessions
{
/// <summary>
- /// Class GetSessions
+ /// Class GetSessions.
/// </summary>
[Route("/Sessions", "GET", Summary = "Gets a list of sessions")]
[Authenticated]
public class GetSessions : IReturn<SessionInfo[]>
{
- [ApiMember(Name = "ControllableByUserId", Description = "Optional. Filter by sessions that a given user is allowed to remote control.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ [ApiMember(Name = "ControllableByUserId", Description = "Filter by sessions that a given user is allowed to remote control.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public Guid ControllableByUserId { get; set; }
- [ApiMember(Name = "DeviceId", Description = "Optional. Filter by device id.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ [ApiMember(Name = "DeviceId", Description = "Filter by device Id.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string DeviceId { get; set; }
public int? ActiveWithinSeconds { get; set; }
}
/// <summary>
- /// Class DisplayContent
+ /// Class DisplayContent.
/// </summary>
[Route("/Sessions/{Id}/Viewing", "POST", Summary = "Instructs a session to browse to an item or view")]
[Authenticated]
@@ -182,7 +179,7 @@ namespace MediaBrowser.Api.Session
[ApiMember(Name = "Id", Description = "Session Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public string Id { get; set; }
- [ApiMember(Name = "UserId", Description = "UserId Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
+ [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public string UserId { get; set; }
}
@@ -230,15 +227,20 @@ namespace MediaBrowser.Api.Session
public string Id { get; set; }
}
- [Route("/Sessions/Logout", "POST", Summary = "Reports that a session has ended")]
+ [Route("/Sessions/Viewing", "POST", Summary = "Reports that a session is viewing an item")]
[Authenticated]
- public class ReportSessionEnded : IReturnVoid
+ public class ReportViewing : IReturnVoid
{
+ [ApiMember(Name = "SessionId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
+ public string SessionId { get; set; }
+
+ [ApiMember(Name = "ItemId", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
+ public string ItemId { get; set; }
}
- [Route("/Auth/Keys", "GET")]
- [Authenticated(Roles = "Admin")]
- public class GetApiKeys
+ [Route("/Sessions/Logout", "POST", Summary = "Reports that a session has ended")]
+ [Authenticated]
+ public class ReportSessionEnded : IReturnVoid
{
}
@@ -254,48 +256,28 @@ namespace MediaBrowser.Api.Session
{
}
- [Route("/Auth/Keys/{Key}", "DELETE")]
- [Authenticated(Roles = "Admin")]
- public class RevokeKey
- {
- [ApiMember(Name = "Key", Description = "Auth Key", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
- public string Key { get; set; }
- }
-
- [Route("/Auth/Keys", "POST")]
- [Authenticated(Roles = "Admin")]
- public class CreateKey
- {
- [ApiMember(Name = "App", Description = "App", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
- public string App { get; set; }
- }
-
/// <summary>
/// Class SessionsService.
/// </summary>
- public class SessionsService : BaseApiService
+ public class SessionService : BaseApiService
{
/// <summary>
- /// The _session manager.
+ /// The session manager.
/// </summary>
private readonly ISessionManager _sessionManager;
private readonly IUserManager _userManager;
private readonly IAuthorizationContext _authContext;
- private readonly IAuthenticationRepository _authRepo;
private readonly IDeviceManager _deviceManager;
private readonly ISessionContext _sessionContext;
- private readonly IServerApplicationHost _appHost;
- public SessionsService(
- ILogger<SessionsService> logger,
+ public SessionService(
+ ILogger<SessionService> logger,
IServerConfigurationManager serverConfigurationManager,
IHttpResultFactory httpResultFactory,
ISessionManager sessionManager,
- IServerApplicationHost appHost,
IUserManager userManager,
IAuthorizationContext authContext,
- IAuthenticationRepository authRepo,
IDeviceManager deviceManager,
ISessionContext sessionContext)
: base(logger, serverConfigurationManager, httpResultFactory)
@@ -303,10 +285,8 @@ namespace MediaBrowser.Api.Session
_sessionManager = sessionManager;
_userManager = userManager;
_authContext = authContext;
- _authRepo = authRepo;
_deviceManager = deviceManager;
_sessionContext = sessionContext;
- _appHost = appHost;
}
public object Get(GetAuthProviders request)
@@ -319,25 +299,6 @@ namespace MediaBrowser.Api.Session
return _userManager.GetPasswordResetProviders();
}
- public void Delete(RevokeKey request)
- {
- _sessionManager.RevokeToken(request.Key);
-
- }
-
- public void Post(CreateKey request)
- {
- _authRepo.Create(new AuthenticationInfo
- {
- AppName = request.App,
- AccessToken = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture),
- DateCreated = DateTime.UtcNow,
- DeviceId = _appHost.SystemId,
- DeviceName = _appHost.FriendlyName,
- AppVersion = _appHost.ApplicationVersionString
- });
- }
-
public void Post(ReportSessionEnded request)
{
var auth = _authContext.GetAuthorizationInfo(Request);
@@ -345,16 +306,6 @@ namespace MediaBrowser.Api.Session
_sessionManager.Logout(auth.Token);
}
- public object Get(GetApiKeys request)
- {
- var result = _authRepo.Get(new AuthenticationInfoQuery
- {
- HasUser = false
- });
-
- return result;
- }
-
/// <summary>
/// Gets the specified request.
/// </summary>
@@ -438,14 +389,12 @@ namespace MediaBrowser.Api.Session
public Task Post(SendSystemCommand request)
{
var name = request.Command;
-
if (Enum.TryParse(name, true, out GeneralCommandType commandType))
{
name = commandType.ToString();
}
var currentSession = GetSession(_sessionContext);
-
var command = new GeneralCommand
{
Name = name,
@@ -518,16 +467,13 @@ namespace MediaBrowser.Api.Session
{
request.Id = GetSession(_sessionContext).Id;
}
+
_sessionManager.ReportCapabilities(request.Id, new ClientCapabilities
{
PlayableMediaTypes = SplitValue(request.PlayableMediaTypes, ','),
-
SupportedCommands = SplitValue(request.SupportedCommands, ','),
-
SupportsMediaControl = request.SupportsMediaControl,
-
SupportsSync = request.SupportsSync,
-
SupportsPersistentIdentifier = request.SupportsPersistentIdentifier
});
}
@@ -538,7 +484,15 @@ namespace MediaBrowser.Api.Session
{
request.Id = GetSession(_sessionContext).Id;
}
+
_sessionManager.ReportCapabilities(request.Id, request);
}
+
+ public void Post(ReportViewing request)
+ {
+ request.SessionId = GetSession(_sessionContext).Id;
+
+ _sessionManager.ReportNowViewingItem(request.SessionId, request.ItemId);
+ }
}
}
diff --git a/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs b/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs
index a036619b8..f8b6ee65d 100644
--- a/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs
+++ b/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs
@@ -1,5 +1,4 @@
using System.Collections.Generic;
-using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Activity;
@@ -24,7 +23,7 @@ namespace MediaBrowser.Api.System
/// </summary>
private readonly IActivityManager _activityManager;
- public ActivityLogWebSocketListener(ILogger logger, IActivityManager activityManager) : base(logger)
+ public ActivityLogWebSocketListener(ILogger<ActivityLogWebSocketListener> logger, IActivityManager activityManager) : base(logger)
{
_activityManager = activityManager;
_activityManager.EntryCreated += _activityManager_EntryCreated;
diff --git a/MediaBrowser.Api/System/SystemService.cs b/MediaBrowser.Api/System/SystemService.cs
index 3a56ba701..3a3eeb8b8 100644
--- a/MediaBrowser.Api/System/SystemService.cs
+++ b/MediaBrowser.Api/System/SystemService.cs
@@ -170,10 +170,10 @@ namespace MediaBrowser.Api.System
// For older files, assume fully static
if (file.LastWriteTimeUtc < DateTime.UtcNow.AddHours(-1))
{
- return ResultFactory.GetStaticFileResult(Request, file.FullName, FileShareMode.Read);
+ return ResultFactory.GetStaticFileResult(Request, file.FullName, FileShare.Read);
}
- return ResultFactory.GetStaticFileResult(Request, file.FullName, FileShareMode.ReadWrite);
+ return ResultFactory.GetStaticFileResult(Request, file.FullName, FileShare.ReadWrite);
}
/// <summary>
diff --git a/MediaBrowser.Api/UserLibrary/GenresService.cs b/MediaBrowser.Api/UserLibrary/GenresService.cs
index 13bb88ca8..1fa272a5f 100644
--- a/MediaBrowser.Api/UserLibrary/GenresService.cs
+++ b/MediaBrowser.Api/UserLibrary/GenresService.cs
@@ -5,7 +5,6 @@ using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
-using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
diff --git a/MediaBrowser.Api/UserLibrary/PersonsService.cs b/MediaBrowser.Api/UserLibrary/PersonsService.cs
index 853eada25..3204e5219 100644
--- a/MediaBrowser.Api/UserLibrary/PersonsService.cs
+++ b/MediaBrowser.Api/UserLibrary/PersonsService.cs
@@ -6,7 +6,6 @@ using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
-using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Services;
diff --git a/MediaBrowser.Api/UserLibrary/PlaystateService.cs b/MediaBrowser.Api/UserLibrary/PlaystateService.cs
index 9d1cf5d9e..d0faca163 100644
--- a/MediaBrowser.Api/UserLibrary/PlaystateService.cs
+++ b/MediaBrowser.Api/UserLibrary/PlaystateService.cs
@@ -103,10 +103,6 @@ namespace MediaBrowser.Api.UserLibrary
[ApiMember(Name = "MediaSourceId", Description = "The id of the MediaSource", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
public string MediaSourceId { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether this <see cref="UpdateUserItemRating" /> is likes.
- /// </summary>
- /// <value><c>true</c> if likes; otherwise, <c>false</c>.</value>
[ApiMember(Name = "CanSeek", Description = "Indicates if the client can seek", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "POST")]
public bool CanSeek { get; set; }
diff --git a/MediaBrowser.Api/UserLibrary/YearsService.cs b/MediaBrowser.Api/UserLibrary/YearsService.cs
index 07b9aff1b..d023ee90a 100644
--- a/MediaBrowser.Api/UserLibrary/YearsService.cs
+++ b/MediaBrowser.Api/UserLibrary/YearsService.cs
@@ -6,7 +6,6 @@ using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
-using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Services;
using Microsoft.Extensions.Logging;
diff --git a/MediaBrowser.Api/UserService.cs b/MediaBrowser.Api/UserService.cs
index e1b01b012..401514349 100644
--- a/MediaBrowser.Api/UserService.cs
+++ b/MediaBrowser.Api/UserService.cs
@@ -240,7 +240,7 @@ namespace MediaBrowser.Api
public class UserService : BaseApiService
{
/// <summary>
- /// The _user manager
+ /// The user manager.
/// </summary>
private readonly IUserManager _userManager;
private readonly ISessionManager _sessionMananger;
@@ -281,7 +281,6 @@ namespace MediaBrowser.Api
{
IsHidden = false,
IsDisabled = false
-
}, true, true);
}
@@ -395,10 +394,11 @@ namespace MediaBrowser.Api
throw new MethodNotAllowedException("Hashed-only passwords are not valid for this API.");
}
+ // Password should always be null
return Post(new AuthenticateUserByName
{
Username = user.Name,
- Password = null, // This should always be null
+ Password = null,
Pw = request.Pw
});
}
diff --git a/MediaBrowser.Common/Configuration/IConfigurationFactory.cs b/MediaBrowser.Common/Configuration/IConfigurationFactory.cs
index 4c4060096..07ca2b58b 100644
--- a/MediaBrowser.Common/Configuration/IConfigurationFactory.cs
+++ b/MediaBrowser.Common/Configuration/IConfigurationFactory.cs
@@ -1,24 +1,47 @@
-#pragma warning disable CS1591
-
using System;
using System.Collections.Generic;
namespace MediaBrowser.Common.Configuration
{
+ /// <summary>
+ /// Provides an interface to retrieve a configuration store. Classes with this interface are scanned for at
+ /// application start to dynamically register configuration for various modules/plugins.
+ /// </summary>
public interface IConfigurationFactory
{
+ /// <summary>
+ /// Get the configuration store for this module.
+ /// </summary>
+ /// <returns>The configuration store.</returns>
IEnumerable<ConfigurationStore> GetConfigurations();
}
+ /// <summary>
+ /// Describes a single entry in the application configuration.
+ /// </summary>
public class ConfigurationStore
{
+ /// <summary>
+ /// Gets or sets the unique identifier for the configuration.
+ /// </summary>
public string Key { get; set; }
+ /// <summary>
+ /// Gets or sets the type used to store the data for this configuration entry.
+ /// </summary>
public Type ConfigurationType { get; set; }
}
+ /// <summary>
+ /// A configuration store that can be validated.
+ /// </summary>
public interface IValidatingConfiguration
{
+ /// <summary>
+ /// Validation method to be invoked before saving the configuration.
+ /// </summary>
+ /// <param name="oldConfig">The old configuration.</param>
+ /// <param name="newConfig">The new configuration.</param>
void Validate(object oldConfig, object newConfig);
}
}
diff --git a/MediaBrowser.Common/Cryptography/PasswordHash.cs b/MediaBrowser.Common/Cryptography/PasswordHash.cs
index 19b8be47a..3e12536ec 100644
--- a/MediaBrowser.Common/Cryptography/PasswordHash.cs
+++ b/MediaBrowser.Common/Cryptography/PasswordHash.cs
@@ -15,24 +15,24 @@ namespace MediaBrowser.Common.Cryptography
public class PasswordHash
{
private readonly Dictionary<string, string> _parameters;
+ private readonly byte[] _salt;
+ private readonly byte[] _hash;
public PasswordHash(string id, byte[] hash)
: this(id, hash, Array.Empty<byte>())
{
-
}
public PasswordHash(string id, byte[] hash, byte[] salt)
: this(id, hash, salt, new Dictionary<string, string>())
{
-
}
public PasswordHash(string id, byte[] hash, byte[] salt, Dictionary<string, string> parameters)
{
Id = id;
- Hash = hash;
- Salt = salt;
+ _hash = hash;
+ _salt = salt;
_parameters = parameters;
}
@@ -45,25 +45,24 @@ namespace MediaBrowser.Common.Cryptography
/// <summary>
/// Gets the additional parameters used by the hash function.
/// </summary>
- /// <value></value>
public IReadOnlyDictionary<string, string> Parameters => _parameters;
/// <summary>
/// Gets the salt used for hashing the password.
/// </summary>
/// <value>Returns the salt used for hashing the password.</value>
- public byte[] Salt { get; }
+ public ReadOnlySpan<byte> Salt => _salt;
/// <summary>
/// Gets the hashed password.
/// </summary>
/// <value>Return the hashed password.</value>
- public byte[] Hash { get; }
+ public ReadOnlySpan<byte> Hash => _hash;
public static PasswordHash Parse(string hashString)
{
- string[] splitted = hashString.Split('$');
// The string should at least contain the hash function and the hash itself
+ string[] splitted = hashString.Split('$');
if (splitted.Length < 3)
{
throw new ArgumentException("String doesn't contain enough segments", nameof(hashString));
@@ -77,7 +76,7 @@ namespace MediaBrowser.Common.Cryptography
// Optional parameters
Dictionary<string, string> parameters = new Dictionary<string, string>();
- if (splitted[index].IndexOf('=') != -1)
+ if (splitted[index].IndexOf('=', StringComparison.Ordinal) != -1)
{
foreach (string paramset in splitted[index++].Split(','))
{
@@ -98,6 +97,7 @@ namespace MediaBrowser.Common.Cryptography
byte[] hash;
byte[] salt;
+
// Check if the string also contains a salt
if (splitted.Length - index == 2)
{
@@ -141,14 +141,14 @@ namespace MediaBrowser.Common.Cryptography
.Append(Id);
SerializeParameters(str);
- if (Salt.Length != 0)
+ if (_salt.Length != 0)
{
str.Append('$')
- .Append(Hex.Encode(Salt, false));
+ .Append(Hex.Encode(_salt, false));
}
return str.Append('$')
- .Append(Hex.Encode(Hash, false)).ToString();
+ .Append(Hex.Encode(_hash, false)).ToString();
}
}
}
diff --git a/MediaBrowser.Common/Events/EventHelper.cs b/MediaBrowser.Common/Events/EventHelper.cs
index b67315df6..c9d3226ac 100644
--- a/MediaBrowser.Common/Events/EventHelper.cs
+++ b/MediaBrowser.Common/Events/EventHelper.cs
@@ -1,15 +1,13 @@
-#pragma warning disable CS1591
-
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.Common.Events
{
- // TODO: @bond Remove
/// <summary>
- /// Class EventHelper
+ /// Class EventHelper.
/// </summary>
+ // TODO: @bond Remove
public static class EventHelper
{
/// <summary>
@@ -40,7 +38,7 @@ namespace MediaBrowser.Common.Events
/// <summary>
/// Queues the event.
/// </summary>
- /// <typeparam name="T"></typeparam>
+ /// <typeparam name="T">Argument type for the <c>handler</c>.</typeparam>
/// <param name="handler">The handler.</param>
/// <param name="sender">The sender.</param>
/// <param name="args">The args.</param>
diff --git a/MediaBrowser.Common/Extensions/BaseExtensions.cs b/MediaBrowser.Common/Extensions/BaseExtensions.cs
index 33473c2be..08964420e 100644
--- a/MediaBrowser.Common/Extensions/BaseExtensions.cs
+++ b/MediaBrowser.Common/Extensions/BaseExtensions.cs
@@ -1,12 +1,12 @@
using System;
+using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
-using System.Security.Cryptography;
namespace MediaBrowser.Common.Extensions
{
/// <summary>
- /// Class BaseExtensions
+ /// Class BaseExtensions.
/// </summary>
public static class BaseExtensions
{
@@ -30,10 +30,13 @@ namespace MediaBrowser.Common.Extensions
/// <returns><see cref="Guid" />.</returns>
public static Guid GetMD5(this string str)
{
+#pragma warning disable CA5351
using (var provider = MD5.Create())
{
return new Guid(provider.ComputeHash(Encoding.Unicode.GetBytes(str)));
}
+
+#pragma warning restore CA5351
}
}
}
diff --git a/MediaBrowser.Common/Extensions/CopyToExtensions.cs b/MediaBrowser.Common/Extensions/CopyToExtensions.cs
index 78a73f07e..2ecbc6539 100644
--- a/MediaBrowser.Common/Extensions/CopyToExtensions.cs
+++ b/MediaBrowser.Common/Extensions/CopyToExtensions.cs
@@ -5,7 +5,7 @@ namespace MediaBrowser.Common.Extensions
/// <summary>
/// Provides <c>CopyTo</c> extensions methods for <see cref="IReadOnlyList{T}" />.
/// </summary>
- public static class CollectionExtensions
+ public static class CopyToExtensions
{
/// <summary>
/// Copies all the elements of the current collection to the specified list
@@ -14,7 +14,7 @@ namespace MediaBrowser.Common.Extensions
/// <param name="source">The current collection that is the source of the elements.</param>
/// <param name="destination">The list that is the destination of the elements copied from the current collection.</param>
/// <param name="index">A 32-bit integer that represents the index in <c>destination</c> at which copying begins.</param>
- /// <typeparam name="T"></typeparam>
+ /// <typeparam name="T">The type of the array.</typeparam>
public static void CopyTo<T>(this IReadOnlyList<T> source, IList<T> destination, int index = 0)
{
for (int i = 0; i < source.Count; i++)
diff --git a/MediaBrowser.Common/Extensions/MethodNotAllowedException.cs b/MediaBrowser.Common/Extensions/MethodNotAllowedException.cs
new file mode 100644
index 000000000..48e758ee4
--- /dev/null
+++ b/MediaBrowser.Common/Extensions/MethodNotAllowedException.cs
@@ -0,0 +1,26 @@
+using System;
+
+namespace MediaBrowser.Common.Extensions
+{
+ /// <summary>
+ /// Class MethodNotAllowedException.
+ /// </summary>
+ public class MethodNotAllowedException : Exception
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MethodNotAllowedException" /> class.
+ /// </summary>
+ public MethodNotAllowedException()
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MethodNotAllowedException" /> class.
+ /// </summary>
+ /// <param name="message">The message.</param>
+ public MethodNotAllowedException(string message)
+ : base(message)
+ {
+ }
+ }
+}
diff --git a/MediaBrowser.Common/Extensions/RateLimitExceededException.cs b/MediaBrowser.Common/Extensions/RateLimitExceededException.cs
new file mode 100644
index 000000000..95802a462
--- /dev/null
+++ b/MediaBrowser.Common/Extensions/RateLimitExceededException.cs
@@ -0,0 +1,25 @@
+#pragma warning disable CS1591
+
+using System;
+
+namespace MediaBrowser.Common.Extensions
+{
+ public class RateLimitExceededException : Exception
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="RateLimitExceededException" /> class.
+ /// </summary>
+ public RateLimitExceededException()
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="RateLimitExceededException" /> class.
+ /// </summary>
+ /// <param name="message">The message.</param>
+ public RateLimitExceededException(string message)
+ : base(message)
+ {
+ }
+ }
+}
diff --git a/MediaBrowser.Common/Extensions/ResourceNotFoundException.cs b/MediaBrowser.Common/Extensions/ResourceNotFoundException.cs
index 9b064a40d..22130c5a1 100644
--- a/MediaBrowser.Common/Extensions/ResourceNotFoundException.cs
+++ b/MediaBrowser.Common/Extensions/ResourceNotFoundException.cs
@@ -1,11 +1,9 @@
-#pragma warning disable CS1591
-
using System;
namespace MediaBrowser.Common.Extensions
{
/// <summary>
- /// Class ResourceNotFoundException
+ /// Class ResourceNotFoundException.
/// </summary>
public class ResourceNotFoundException : Exception
{
@@ -14,7 +12,6 @@ namespace MediaBrowser.Common.Extensions
/// </summary>
public ResourceNotFoundException()
{
-
}
/// <summary>
@@ -24,66 +21,6 @@ namespace MediaBrowser.Common.Extensions
public ResourceNotFoundException(string message)
: base(message)
{
-
- }
- }
-
- /// <summary>
- /// Class MethodNotAllowedException
- /// </summary>
- public class MethodNotAllowedException : Exception
- {
- /// <summary>
- /// Initializes a new instance of the <see cref="MethodNotAllowedException" /> class.
- /// </summary>
- public MethodNotAllowedException()
- {
-
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="MethodNotAllowedException" /> class.
- /// </summary>
- /// <param name="message">The message.</param>
- public MethodNotAllowedException(string message)
- : base(message)
- {
-
- }
- }
-
- public class RemoteServiceUnavailableException : Exception
- {
- public RemoteServiceUnavailableException()
- {
-
- }
-
- public RemoteServiceUnavailableException(string message)
- : base(message)
- {
-
- }
- }
-
- public class RateLimitExceededException : Exception
- {
- /// <summary>
- /// Initializes a new instance of the <see cref="RateLimitExceededException" /> class.
- /// </summary>
- public RateLimitExceededException()
- {
-
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="RateLimitExceededException" /> class.
- /// </summary>
- /// <param name="message">The message.</param>
- public RateLimitExceededException(string message)
- : base(message)
- {
-
}
}
}
diff --git a/MediaBrowser.Common/Extensions/ShuffleExtensions.cs b/MediaBrowser.Common/Extensions/ShuffleExtensions.cs
index 5889d09c4..0432f36b5 100644
--- a/MediaBrowser.Common/Extensions/ShuffleExtensions.cs
+++ b/MediaBrowser.Common/Extensions/ShuffleExtensions.cs
@@ -17,11 +17,22 @@ namespace MediaBrowser.Common.Extensions
/// <typeparam name="T">The type.</typeparam>
public static void Shuffle<T>(this IList<T> list)
{
+ list.Shuffle(_rng);
+ }
+
+ /// <summary>
+ /// Shuffles the items in a list.
+ /// </summary>
+ /// <param name="list">The list that should get shuffled.</param>
+ /// <param name="rng">The random number generator to use.</param>
+ /// <typeparam name="T">The type.</typeparam>
+ public static void Shuffle<T>(this IList<T> list, Random rng)
+ {
int n = list.Count;
while (n > 1)
{
n--;
- int k = _rng.Next(n + 1);
+ int k = rng.Next(n + 1);
T value = list[k];
list[k] = list[n];
list[n] = value;
diff --git a/MediaBrowser.Common/Hex.cs b/MediaBrowser.Common/Hex.cs
index b2d9aea3a..559109f74 100644
--- a/MediaBrowser.Common/Hex.cs
+++ b/MediaBrowser.Common/Hex.cs
@@ -14,11 +14,11 @@ namespace MediaBrowser.Common
internal const int LastHexSymbol = 0x66; // 102: f
/// <summary>
- /// Map from an ASCII char to its hex value shifted,
+ /// Gets a map from an ASCII char to its hex value shifted,
/// e.g. <c>b</c> -> 11. 0xFF means it's not a hex symbol.
/// </summary>
- /// <value></value>
- internal static ReadOnlySpan<byte> HexLookup => new byte[] {
+ internal static ReadOnlySpan<byte> HexLookup => new byte[]
+ {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
@@ -29,10 +29,10 @@ namespace MediaBrowser.Common
};
/// <summary>
- /// Encodes <c>bytes</c> as a hex string.
+ /// Encodes each element of the specified bytes as its hexadecimal string representation.
/// </summary>
- /// <param name="bytes"></param>
- /// <param name="lowercase"></param>
+ /// <param name="bytes">An array of bytes.</param>
+ /// <param name="lowercase"><c>true</c> to use lowercase hexadecimal characters; otherwise <c>false</c>.</param>
/// <returns><c>bytes</c> as a hex string.</returns>
public static string Encode(ReadOnlySpan<byte> bytes, bool lowercase = true)
{
diff --git a/MediaBrowser.Common/IApplicationHost.cs b/MediaBrowser.Common/IApplicationHost.cs
index 6668e98aa..0e282cf53 100644
--- a/MediaBrowser.Common/IApplicationHost.cs
+++ b/MediaBrowser.Common/IApplicationHost.cs
@@ -3,16 +3,22 @@ using System.Collections.Generic;
using System.Threading.Tasks;
using MediaBrowser.Common.Plugins;
using MediaBrowser.Model.Updates;
+using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace MediaBrowser.Common
{
/// <summary>
- /// An interface to be implemented by the applications hosting a kernel
+ /// An interface to be implemented by the applications hosting a kernel.
/// </summary>
public interface IApplicationHost
{
/// <summary>
+ /// Occurs when [has pending restart changed].
+ /// </summary>
+ event EventHandler HasPendingRestartChanged;
+
+ /// <summary>
/// Gets the name.
/// </summary>
/// <value>The name.</value>
@@ -25,13 +31,13 @@ namespace MediaBrowser.Common
string SystemId { get; }
/// <summary>
- /// Gets or sets a value indicating whether this instance has pending kernel reload.
+ /// Gets a value indicating whether this instance has pending kernel reload.
/// </summary>
/// <value><c>true</c> if this instance has pending kernel reload; otherwise, <c>false</c>.</value>
bool HasPendingRestart { get; }
/// <summary>
- /// Gets or sets a value indicating whether this instance is currently shutting down.
+ /// Gets a value indicating whether this instance is currently shutting down.
/// </summary>
/// <value><c>true</c> if this instance is shutting down; otherwise, <c>false</c>.</value>
bool IsShuttingDown { get; }
@@ -43,27 +49,12 @@ namespace MediaBrowser.Common
bool CanSelfRestart { get; }
/// <summary>
- /// Get the version class of the system.
+ /// Gets the version class of the system.
/// </summary>
/// <value><see cref="PackageVersionClass.Release" /> or <see cref="PackageVersionClass.Beta" />.</value>
PackageVersionClass SystemUpdateLevel { get; }
/// <summary>
- /// Occurs when [has pending restart changed].
- /// </summary>
- event EventHandler HasPendingRestartChanged;
-
- /// <summary>
- /// Notifies the pending restart.
- /// </summary>
- void NotifyPendingRestart();
-
- /// <summary>
- /// Restarts this instance.
- /// </summary>
- void Restart();
-
- /// <summary>
/// Gets the application version.
/// </summary>
/// <value>The application version.</value>
@@ -88,6 +79,22 @@ namespace MediaBrowser.Common
string ApplicationUserAgentAddress { get; }
/// <summary>
+ /// Gets the plugins.
+ /// </summary>
+ /// <value>The plugins.</value>
+ IReadOnlyList<IPlugin> Plugins { get; }
+
+ /// <summary>
+ /// Notifies the pending restart.
+ /// </summary>
+ void NotifyPendingRestart();
+
+ /// <summary>
+ /// Restarts this instance.
+ /// </summary>
+ void Restart();
+
+ /// <summary>
/// Gets the exports.
/// </summary>
/// <typeparam name="T">The type.</typeparam>
@@ -98,31 +105,29 @@ namespace MediaBrowser.Common
/// <summary>
/// Resolves this instance.
/// </summary>
- /// <typeparam name="T"></typeparam>
+ /// <typeparam name="T">The <c>Type</c>.</typeparam>
/// <returns>``0.</returns>
T Resolve<T>();
/// <summary>
/// Shuts down.
/// </summary>
+ /// <returns>A task.</returns>
Task Shutdown();
/// <summary>
- /// Gets the plugins.
- /// </summary>
- /// <value>The plugins.</value>
- IPlugin[] Plugins { get; }
-
- /// <summary>
/// Removes the plugin.
/// </summary>
/// <param name="plugin">The plugin.</param>
void RemovePlugin(IPlugin plugin);
/// <summary>
- /// Inits this instance.
+ /// Initializes this instance.
/// </summary>
- Task InitAsync(IServiceCollection serviceCollection);
+ /// <param name="serviceCollection">The service collection.</param>
+ /// <param name="startupConfig">The configuration to use for initialization.</param>
+ /// <returns>A task.</returns>
+ Task InitAsync(IServiceCollection serviceCollection, IConfiguration startupConfig);
/// <summary>
/// Creates the instance.
diff --git a/MediaBrowser.Common/Json/Converters/GuidConverter.cs b/MediaBrowser.Common/Json/Converters/JsonGuidConverter.cs
index 3081e12ee..d35a761f3 100644
--- a/MediaBrowser.Common/Json/Converters/GuidConverter.cs
+++ b/MediaBrowser.Common/Json/Converters/JsonGuidConverter.cs
@@ -7,7 +7,7 @@ namespace MediaBrowser.Common.Json.Converters
/// <summary>
/// Converts a GUID object or value to/from JSON.
/// </summary>
- public class GuidConverter : JsonConverter<Guid>
+ public class JsonGuidConverter : JsonConverter<Guid>
{
/// <inheritdoc />
public override Guid Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
diff --git a/MediaBrowser.Common/Json/Converters/JsonInt32Converter.cs b/MediaBrowser.Common/Json/Converters/JsonInt32Converter.cs
new file mode 100644
index 000000000..fe5dd6cd4
--- /dev/null
+++ b/MediaBrowser.Common/Json/Converters/JsonInt32Converter.cs
@@ -0,0 +1,53 @@
+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)
+ {
+ static void ThrowFormatException() => throw new FormatException("Invalid format for an integer.");
+ ReadOnlySpan<byte> span = stackalloc byte[0];
+
+ if (reader.HasValueSequence)
+ {
+ long sequenceLength = reader.ValueSequence.Length;
+ Span<byte> stackSpan = stackalloc byte[(int)sequenceLength];
+ reader.ValueSequence.CopyTo(stackSpan);
+ span = stackSpan;
+ }
+ else
+ {
+ span = reader.ValueSpan;
+ }
+
+ if (!Utf8Parser.TryParse(span, out int number, out _))
+ {
+ ThrowFormatException();
+ }
+
+ return number;
+ }
+
+ /// <inheritdoc />
+ public override void Write(Utf8JsonWriter writer, int value, JsonSerializerOptions options)
+ {
+ static void ThrowInvalidOperationException() => throw new InvalidOperationException();
+ Span<byte> span = stackalloc byte[16];
+ if (Utf8Formatter.TryFormat(value, span, out int bytesWritten))
+ {
+ writer.WriteStringValue(span.Slice(0, bytesWritten));
+ }
+
+ ThrowInvalidOperationException();
+ }
+ }
+}
diff --git a/MediaBrowser.Common/Json/JsonDefaults.cs b/MediaBrowser.Common/Json/JsonDefaults.cs
index 4ba0d5a1a..4a6ee0a79 100644
--- a/MediaBrowser.Common/Json/JsonDefaults.cs
+++ b/MediaBrowser.Common/Json/JsonDefaults.cs
@@ -21,7 +21,7 @@ namespace MediaBrowser.Common.Json
WriteIndented = false
};
- options.Converters.Add(new GuidConverter());
+ options.Converters.Add(new JsonGuidConverter());
options.Converters.Add(new JsonStringEnumConverter());
return options;
diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj
index 889fbfa5a..04e0ee67a 100644
--- a/MediaBrowser.Common/MediaBrowser.Common.csproj
+++ b/MediaBrowser.Common/MediaBrowser.Common.csproj
@@ -12,8 +12,9 @@
</ItemGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.0.0" />
- <PackageReference Include="Microsoft.Net.Http.Headers" Version="2.2.0" />
+ <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.1" />
+ <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.1.1" />
+ <PackageReference Include="Microsoft.Net.Http.Headers" Version="2.2.8" />
</ItemGroup>
<ItemGroup>
@@ -27,9 +28,16 @@
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
- <PropertyGroup>
- <!-- We need at least C# 7.1 for the "default literal" feature-->
- <LangVersion>latest</LangVersion>
+ <!-- Code analyzers-->
+ <ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
+ <!-- <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8" PrivateAssets="All" /> -->
+ <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
+ <!-- <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" /> -->
+ <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
+ </ItemGroup>
+
+ <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
+ <CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
diff --git a/MediaBrowser.Common/Net/HttpRequestOptions.cs b/MediaBrowser.Common/Net/HttpRequestOptions.cs
index 18c4b181f..51962001e 100644
--- a/MediaBrowser.Common/Net/HttpRequestOptions.cs
+++ b/MediaBrowser.Common/Net/HttpRequestOptions.cs
@@ -8,11 +8,22 @@ using Microsoft.Net.Http.Headers;
namespace MediaBrowser.Common.Net
{
/// <summary>
- /// Class HttpRequestOptions
+ /// Class HttpRequestOptions.
/// </summary>
public class HttpRequestOptions
{
/// <summary>
+ /// Initializes a new instance of the <see cref="HttpRequestOptions"/> class.
+ /// </summary>
+ public HttpRequestOptions()
+ {
+ RequestHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+
+ CacheMode = CacheMode.None;
+ DecompressionMethod = CompressionMethod.Deflate;
+ }
+
+ /// <summary>
/// Gets or sets the URL.
/// </summary>
/// <value>The URL.</value>
@@ -71,14 +82,17 @@ namespace MediaBrowser.Common.Net
public string RequestContentType { get; set; }
public string RequestContent { get; set; }
+
public byte[] RequestContentBytes { get; set; }
public bool BufferContent { get; set; }
public bool LogErrorResponseBody { get; set; }
+
public bool EnableKeepAlive { get; set; }
public CacheMode CacheMode { get; set; }
+
public TimeSpan CacheLength { get; set; }
public bool EnableDefaultUserAgent { get; set; }
@@ -89,17 +103,6 @@ namespace MediaBrowser.Common.Net
return value;
}
-
- /// <summary>
- /// Initializes a new instance of the <see cref="HttpRequestOptions"/> class.
- /// </summary>
- public HttpRequestOptions()
- {
- RequestHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
-
- CacheMode = CacheMode.None;
- DecompressionMethod = CompressionMethod.Deflate;
- }
}
public enum CacheMode
diff --git a/MediaBrowser.Common/Net/HttpResponseInfo.cs b/MediaBrowser.Common/Net/HttpResponseInfo.cs
index 0de034b0e..d7f7a5622 100644
--- a/MediaBrowser.Common/Net/HttpResponseInfo.cs
+++ b/MediaBrowser.Common/Net/HttpResponseInfo.cs
@@ -1,5 +1,3 @@
-#pragma warning disable CS1591
-
using System;
using System.IO;
using System.Net;
@@ -8,10 +6,24 @@ using System.Net.Http.Headers;
namespace MediaBrowser.Common.Net
{
/// <summary>
- /// Class HttpResponseInfo
+ /// Class HttpResponseInfo.
/// </summary>
public class HttpResponseInfo : IDisposable
{
+#pragma warning disable CS1591
+ public HttpResponseInfo()
+ {
+ }
+
+ public HttpResponseInfo(HttpResponseHeaders headers, HttpContentHeaders contentHeader)
+ {
+ Headers = headers;
+ ContentHeaders = contentHeader;
+ }
+
+#pragma warning restore CS1591
+#pragma warning restore SA1600
+
/// <summary>
/// Gets or sets the type of the content.
/// </summary>
@@ -60,21 +72,10 @@ namespace MediaBrowser.Common.Net
/// <value>The content headers.</value>
public HttpContentHeaders ContentHeaders { get; set; }
- public HttpResponseInfo()
- {
-
- }
-
- public HttpResponseInfo(HttpResponseHeaders headers, HttpContentHeaders contentHeader)
- {
- Headers = headers;
- ContentHeaders = contentHeader;
- }
-
/// <inheritdoc />
public void Dispose()
{
- // Only IDisposable for backwards compatibility
+ // backwards compatibility
}
}
}
diff --git a/MediaBrowser.Common/Net/IHttpClient.cs b/MediaBrowser.Common/Net/IHttpClient.cs
index 23ba34173..534e22edd 100644
--- a/MediaBrowser.Common/Net/IHttpClient.cs
+++ b/MediaBrowser.Common/Net/IHttpClient.cs
@@ -1,12 +1,12 @@
using System;
using System.IO;
-using System.Threading.Tasks;
using System.Net.Http;
+using System.Threading.Tasks;
namespace MediaBrowser.Common.Net
{
/// <summary>
- /// Interface IHttpClient
+ /// Interface IHttpClient.
/// </summary>
public interface IHttpClient
{
diff --git a/MediaBrowser.Common/Net/INetworkManager.cs b/MediaBrowser.Common/Net/INetworkManager.cs
index 0b99dc910..3ba75abd8 100644
--- a/MediaBrowser.Common/Net/INetworkManager.cs
+++ b/MediaBrowser.Common/Net/INetworkManager.cs
@@ -11,20 +11,20 @@ namespace MediaBrowser.Common.Net
{
event EventHandler NetworkChanged;
+ Func<string[]> LocalSubnetsFn { get; set; }
+
/// <summary>
- /// Gets a random port number that is currently available
+ /// Gets a random port number that is currently available.
/// </summary>
/// <returns>System.Int32.</returns>
int GetRandomUnusedTcpPort();
int GetRandomUnusedUdpPort();
- Func<string[]> LocalSubnetsFn { get; set; }
-
/// <summary>
- /// Returns MAC Address from first Network Card in Computer
+ /// Returns the MAC Address from first Network Card in Computer.
/// </summary>
- /// <returns>[string] MAC Address</returns>
+ /// <returns>The MAC Address.</returns>
List<PhysicalAddress> GetMacAddresses();
/// <summary>
diff --git a/MediaBrowser.Common/Plugins/BasePlugin.cs b/MediaBrowser.Common/Plugins/BasePlugin.cs
index 6ef891d66..b24d10ff1 100644
--- a/MediaBrowser.Common/Plugins/BasePlugin.cs
+++ b/MediaBrowser.Common/Plugins/BasePlugin.cs
@@ -1,4 +1,4 @@
-#pragma warning disable CS1591
+#pragma warning disable SA1402
using System;
using System.IO;
@@ -8,10 +8,13 @@ using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Common.Plugins
{
+ /// <summary>
+ /// Provides a common base class for all plugins.
+ /// </summary>
public abstract class BasePlugin : IPlugin, IPluginAssembly
{
/// <summary>
- /// Gets the name of the plugin
+ /// Gets the name of the plugin.
/// </summary>
/// <value>The name.</value>
public abstract string Name { get; }
@@ -29,18 +32,24 @@ namespace MediaBrowser.Common.Plugins
public virtual Guid Id { get; private set; }
/// <summary>
- /// Gets the plugin version
+ /// Gets the plugin version.
/// </summary>
/// <value>The version.</value>
public Version Version { get; private set; }
/// <summary>
- /// Gets the path to the assembly file
+ /// Gets the path to the assembly file.
/// </summary>
/// <value>The assembly file path.</value>
public string AssemblyFilePath { get; private set; }
/// <summary>
+ /// Gets the full path to the data folder, where the plugin can store any miscellaneous files needed.
+ /// </summary>
+ /// <value>The data folder path.</value>
+ public string DataFolderPath { get; private set; }
+
+ /// <summary>
/// Gets the plugin info.
/// </summary>
/// <returns>PluginInfo.</returns>
@@ -62,9 +71,9 @@ namespace MediaBrowser.Common.Plugins
/// </summary>
public virtual void OnUninstalling()
{
-
}
+ /// <inheritdoc />
public void SetAttributes(string assemblyFilePath, string dataFolderPath, Version assemblyVersion)
{
AssemblyFilePath = assemblyFilePath;
@@ -72,26 +81,49 @@ namespace MediaBrowser.Common.Plugins
Version = assemblyVersion;
}
+ /// <inheritdoc />
public void SetId(Guid assemblyId)
{
Id = assemblyId;
}
-
- /// <summary>
- /// Gets the full path to the data folder, where the plugin can store any miscellaneous files needed
- /// </summary>
- /// <value>The data folder path.</value>
- public string DataFolderPath { get; private set; }
}
/// <summary>
- /// Provides a common base class for all plugins
+ /// Provides a common base class for all plugins.
/// </summary>
/// <typeparam name="TConfigurationType">The type of the T configuration type.</typeparam>
public abstract class BasePlugin<TConfigurationType> : BasePlugin, IHasPluginConfiguration
where TConfigurationType : BasePluginConfiguration
{
/// <summary>
+ /// The configuration sync lock.
+ /// </summary>
+ private readonly object _configurationSyncLock = new object();
+
+ /// <summary>
+ /// The save lock.
+ /// </summary>
+ private readonly object _configurationSaveLock = new object();
+
+ private Action<string> _directoryCreateFn;
+
+ /// <summary>
+ /// The configuration.
+ /// </summary>
+ private TConfigurationType _configuration;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="BasePlugin{TConfigurationType}" /> class.
+ /// </summary>
+ /// <param name="applicationPaths">The application paths.</param>
+ /// <param name="xmlSerializer">The XML serializer.</param>
+ protected BasePlugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer)
+ {
+ ApplicationPaths = applicationPaths;
+ XmlSerializer = xmlSerializer;
+ }
+
+ /// <summary>
/// Gets the application paths.
/// </summary>
/// <value>The application paths.</value>
@@ -104,34 +136,19 @@ namespace MediaBrowser.Common.Plugins
protected IXmlSerializer XmlSerializer { get; private set; }
/// <summary>
- /// Gets the type of configuration this plugin uses
+ /// Gets the type of configuration this plugin uses.
/// </summary>
/// <value>The type of the configuration.</value>
public Type ConfigurationType => typeof(TConfigurationType);
- private Action<string> _directoryCreateFn;
- public void SetStartupInfo(Action<string> directoryCreateFn)
- {
- // hack alert, until the .net core transition is complete
- _directoryCreateFn = directoryCreateFn;
- }
-
/// <summary>
- /// Gets the name the assembly file
+ /// Gets the name the assembly file.
/// </summary>
/// <value>The name of the assembly file.</value>
protected string AssemblyFileName => Path.GetFileName(AssemblyFilePath);
/// <summary>
- /// The _configuration sync lock
- /// </summary>
- private readonly object _configurationSyncLock = new object();
- /// <summary>
- /// The _configuration
- /// </summary>
- private TConfigurationType _configuration;
- /// <summary>
- /// Gets the plugin's configuration
+ /// Gets or sets the plugin's configuration.
/// </summary>
/// <value>The configuration.</value>
public TConfigurationType Configuration
@@ -149,55 +166,54 @@ namespace MediaBrowser.Common.Plugins
}
}
}
+
return _configuration;
}
- protected set => _configuration = value;
- }
- private TConfigurationType LoadConfiguration()
- {
- var path = ConfigurationFilePath;
-
- try
- {
- return (TConfigurationType)XmlSerializer.DeserializeFromFile(typeof(TConfigurationType), path);
- }
- catch
- {
- return (TConfigurationType)Activator.CreateInstance(typeof(TConfigurationType));
- }
+ protected set => _configuration = value;
}
/// <summary>
- /// Gets the name of the configuration file. Subclasses should override
+ /// Gets the name of the configuration file. Subclasses should override.
/// </summary>
/// <value>The name of the configuration file.</value>
public virtual string ConfigurationFileName => Path.ChangeExtension(AssemblyFileName, ".xml");
/// <summary>
- /// Gets the full path to the configuration file
+ /// Gets the full path to the configuration file.
/// </summary>
/// <value>The configuration file path.</value>
public string ConfigurationFilePath => Path.Combine(ApplicationPaths.PluginConfigurationsPath, ConfigurationFileName);
/// <summary>
- /// Initializes a new instance of the <see cref="BasePlugin{TConfigurationType}" /> class.
+ /// Gets the plugin's configuration.
/// </summary>
- /// <param name="applicationPaths">The application paths.</param>
- /// <param name="xmlSerializer">The XML serializer.</param>
- protected BasePlugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer)
+ /// <value>The configuration.</value>
+ BasePluginConfiguration IHasPluginConfiguration.Configuration => Configuration;
+
+ /// <inheritdoc />
+ public void SetStartupInfo(Action<string> directoryCreateFn)
{
- ApplicationPaths = applicationPaths;
- XmlSerializer = xmlSerializer;
+ // hack alert, until the .net core transition is complete
+ _directoryCreateFn = directoryCreateFn;
}
- /// <summary>
- /// The _save lock
- /// </summary>
- private readonly object _configurationSaveLock = new object();
+ private TConfigurationType LoadConfiguration()
+ {
+ var path = ConfigurationFilePath;
+
+ try
+ {
+ return (TConfigurationType)XmlSerializer.DeserializeFromFile(typeof(TConfigurationType), path);
+ }
+ catch
+ {
+ return (TConfigurationType)Activator.CreateInstance(typeof(TConfigurationType));
+ }
+ }
/// <summary>
- /// Saves the current configuration to the file system
+ /// Saves the current configuration to the file system.
/// </summary>
public virtual void SaveConfiguration()
{
@@ -209,12 +225,7 @@ namespace MediaBrowser.Common.Plugins
}
}
- /// <summary>
- /// Completely overwrites the current configuration with a new copy
- /// Returns true or false indicating success or failure
- /// </summary>
- /// <param name="configuration">The configuration.</param>
- /// <exception cref="ArgumentNullException">configuration</exception>
+ /// <inheritdoc />
public virtual void UpdateConfiguration(BasePluginConfiguration configuration)
{
if (configuration == null)
@@ -227,12 +238,7 @@ namespace MediaBrowser.Common.Plugins
SaveConfiguration();
}
- /// <summary>
- /// Gets the plugin's configuration
- /// </summary>
- /// <value>The configuration.</value>
- BasePluginConfiguration IHasPluginConfiguration.Configuration => Configuration;
-
+ /// <inheritdoc />
public override PluginInfo GetPluginInfo()
{
var info = base.GetPluginInfo();
@@ -242,10 +248,4 @@ namespace MediaBrowser.Common.Plugins
return info;
}
}
-
- public interface IPluginAssembly
- {
- void SetAttributes(string assemblyFilePath, string dataFolderPath, Version assemblyVersion);
- void SetId(Guid assemblyId);
- }
}
diff --git a/MediaBrowser.Common/Plugins/IPlugin.cs b/MediaBrowser.Common/Plugins/IPlugin.cs
index 7bd90c964..d34820961 100644
--- a/MediaBrowser.Common/Plugins/IPlugin.cs
+++ b/MediaBrowser.Common/Plugins/IPlugin.cs
@@ -6,12 +6,12 @@ using MediaBrowser.Model.Plugins;
namespace MediaBrowser.Common.Plugins
{
/// <summary>
- /// Interface IPlugin
+ /// Interface IPlugin.
/// </summary>
public interface IPlugin
{
/// <summary>
- /// Gets the name of the plugin
+ /// Gets the name of the plugin.
/// </summary>
/// <value>The name.</value>
string Name { get; }
@@ -29,19 +29,19 @@ namespace MediaBrowser.Common.Plugins
Guid Id { get; }
/// <summary>
- /// Gets the plugin version
+ /// Gets the plugin version.
/// </summary>
/// <value>The version.</value>
Version Version { get; }
/// <summary>
- /// Gets the path to the assembly file
+ /// Gets the path to the assembly file.
/// </summary>
/// <value>The assembly file path.</value>
string AssemblyFilePath { get; }
/// <summary>
- /// Gets the full path to the data folder, where the plugin can store any miscellaneous files needed
+ /// Gets the full path to the data folder, where the plugin can store any miscellaneous files needed.
/// </summary>
/// <value>The data folder path.</value>
string DataFolderPath { get; }
@@ -61,24 +61,24 @@ namespace MediaBrowser.Common.Plugins
public interface IHasPluginConfiguration
{
/// <summary>
- /// Gets the type of configuration this plugin uses
+ /// Gets the type of configuration this plugin uses.
/// </summary>
/// <value>The type of the configuration.</value>
Type ConfigurationType { get; }
/// <summary>
- /// Completely overwrites the current configuration with a new copy
- /// Returns true or false indicating success or failure
+ /// Gets the plugin's configuration.
/// </summary>
- /// <param name="configuration">The configuration.</param>
- /// <exception cref="ArgumentNullException">configuration</exception>
- void UpdateConfiguration(BasePluginConfiguration configuration);
+ /// <value>The configuration.</value>
+ BasePluginConfiguration Configuration { get; }
/// <summary>
- /// Gets the plugin's configuration
+ /// Completely overwrites the current configuration with a new copy
+ /// Returns true or false indicating success or failure.
/// </summary>
- /// <value>The configuration.</value>
- BasePluginConfiguration Configuration { get; }
+ /// <param name="configuration">The configuration.</param>
+ /// <exception cref="ArgumentNullException"><c>configuration</c> is <c>null</c>.</exception>
+ void UpdateConfiguration(BasePluginConfiguration configuration);
void SetStartupInfo(Action<string> directoryCreateFn);
}
diff --git a/MediaBrowser.Common/Plugins/IPluginAssembly.cs b/MediaBrowser.Common/Plugins/IPluginAssembly.cs
new file mode 100644
index 000000000..6df4fbb76
--- /dev/null
+++ b/MediaBrowser.Common/Plugins/IPluginAssembly.cs
@@ -0,0 +1,13 @@
+#pragma warning disable CS1591
+
+using System;
+
+namespace MediaBrowser.Common.Plugins
+{
+ public interface IPluginAssembly
+ {
+ void SetAttributes(string assemblyFilePath, string dataFolderPath, Version assemblyVersion);
+
+ void SetId(Guid assemblyId);
+ }
+}
diff --git a/MediaBrowser.Common/Updates/IInstallationManager.cs b/MediaBrowser.Common/Updates/IInstallationManager.cs
index e49812f15..8ea492261 100644
--- a/MediaBrowser.Common/Updates/IInstallationManager.cs
+++ b/MediaBrowser.Common/Updates/IInstallationManager.cs
@@ -103,17 +103,16 @@ namespace MediaBrowser.Common.Updates
Task InstallPackage(PackageVersionInfo package, CancellationToken cancellationToken = default);
/// <summary>
- /// Uninstalls a plugin
+ /// Uninstalls a plugin.
/// </summary>
/// <param name="plugin">The plugin.</param>
- /// <exception cref="ArgumentException"></exception>
void UninstallPlugin(IPlugin plugin);
/// <summary>
- /// Cancels the installation
+ /// Cancels the installation.
/// </summary>
- /// <param name="id">The id of the package that is being installed</param>
- /// <returns>Returns true if the install was cancelled</returns>
+ /// <param name="id">The id of the package that is being installed.</param>
+ /// <returns>Returns true if the install was cancelled.</returns>
bool CancelInstallation(Guid id);
}
}
diff --git a/MediaBrowser.Controller/Authentication/AuthenticationResult.cs b/MediaBrowser.Controller/Authentication/AuthenticationResult.cs
index 3c65156e3..4249a9a66 100644
--- a/MediaBrowser.Controller/Authentication/AuthenticationResult.cs
+++ b/MediaBrowser.Controller/Authentication/AuthenticationResult.cs
@@ -1,14 +1,18 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Dto;
-
namespace MediaBrowser.Controller.Authentication
{
public class AuthenticationResult
{
public UserDto User { get; set; }
+
public SessionInfo SessionInfo { get; set; }
+
public string AccessToken { get; set; }
+
public string ServerId { get; set; }
}
}
diff --git a/MediaBrowser.Controller/Channels/IChannel.cs b/MediaBrowser.Controller/Channels/IChannel.cs
index f8ed98a45..c44e20d1a 100644
--- a/MediaBrowser.Controller/Channels/IChannel.cs
+++ b/MediaBrowser.Controller/Channels/IChannel.cs
@@ -64,7 +64,7 @@ namespace MediaBrowser.Controller.Channels
/// </summary>
/// <param name="type">The type.</param>
/// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{DynamicImageInfo}.</returns>
+ /// <returns>Task{DynamicImageResponse}.</returns>
Task<DynamicImageResponse> GetChannelImage(ImageType type, CancellationToken cancellationToken);
/// <summary>
diff --git a/MediaBrowser.Controller/Drawing/IImageProcessor.cs b/MediaBrowser.Controller/Drawing/IImageProcessor.cs
index a58a11bd1..79399807f 100644
--- a/MediaBrowser.Controller/Drawing/IImageProcessor.cs
+++ b/MediaBrowser.Controller/Drawing/IImageProcessor.cs
@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Entities;
@@ -21,19 +20,11 @@ namespace MediaBrowser.Controller.Drawing
IReadOnlyCollection<string> SupportedInputFormats { get; }
/// <summary>
- /// Gets the image enhancers.
- /// </summary>
- /// <value>The image enhancers.</value>
- IReadOnlyCollection<IImageEnhancer> ImageEnhancers { get; set; }
-
- /// <summary>
/// Gets a value indicating whether [supports image collage creation].
/// </summary>
/// <value><c>true</c> if [supports image collage creation]; otherwise, <c>false</c>.</value>
bool SupportsImageCollageCreation { get; }
- IImageEncoder ImageEncoder { get; set; }
-
/// <summary>
/// Gets the dimensions of the image.
/// </summary>
@@ -59,14 +50,6 @@ namespace MediaBrowser.Controller.Drawing
ImageDimensions GetImageDimensions(BaseItem item, ItemImageInfo info, bool updateItem);
/// <summary>
- /// Gets the supported enhancers.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="imageType">Type of the image.</param>
- /// <returns>IEnumerable{IImageEnhancer}.</returns>
- IEnumerable<IImageEnhancer> GetSupportedEnhancers(BaseItem item, ImageType imageType);
-
- /// <summary>
/// Gets the image cache tag.
/// </summary>
/// <param name="item">The item.</param>
@@ -76,15 +59,6 @@ namespace MediaBrowser.Controller.Drawing
string GetImageCacheTag(BaseItem item, ChapterInfo info);
/// <summary>
- /// Gets the image cache tag.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="image">The image.</param>
- /// <param name="imageEnhancers">The image enhancers.</param>
- /// <returns>Guid.</returns>
- string GetImageCacheTag(BaseItem item, ItemImageInfo image, IReadOnlyCollection<IImageEnhancer> imageEnhancers);
-
- /// <summary>
/// Processes the image.
/// </summary>
/// <param name="options">The options.</param>
@@ -100,15 +74,6 @@ namespace MediaBrowser.Controller.Drawing
Task<(string path, string mimeType, DateTime dateModified)> ProcessImage(ImageProcessingOptions options);
/// <summary>
- /// Gets the enhanced image.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="imageType">Type of the image.</param>
- /// <param name="imageIndex">Index of the image.</param>
- /// <returns>Task{System.String}.</returns>
- Task<string> GetEnhancedImage(BaseItem item, ImageType imageType, int imageIndex);
-
- /// <summary>
/// Gets the supported image output formats.
/// </summary>
/// <returns><see cref="IReadOnlyCollection{ImageOutput}" />.</returns>
diff --git a/MediaBrowser.Controller/Drawing/ImageHelper.cs b/MediaBrowser.Controller/Drawing/ImageHelper.cs
index 432cf8042..d5a5f547e 100644
--- a/MediaBrowser.Controller/Drawing/ImageHelper.cs
+++ b/MediaBrowser.Controller/Drawing/ImageHelper.cs
@@ -19,8 +19,6 @@ namespace MediaBrowser.Controller.Drawing
return GetSizeEstimate(options);
}
- public static IImageProcessor ImageProcessor { get; set; }
-
private static ImageDimensions GetSizeEstimate(ImageProcessingOptions options)
{
if (options.Width.HasValue && options.Height.HasValue)
diff --git a/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs b/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs
index 29addf6e6..870e0278e 100644
--- a/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs
+++ b/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs
@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Drawing;
namespace MediaBrowser.Controller.Drawing
@@ -34,8 +33,6 @@ namespace MediaBrowser.Controller.Drawing
public int Quality { get; set; }
- public IReadOnlyCollection<IImageEnhancer> Enhancers { get; set; }
-
public IReadOnlyCollection<ImageFormat> SupportedOutputFormats { get; set; }
public bool AddPlayedIndicator { get; set; }
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
index efe0d3cf7..5e3056ccb 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
@@ -198,6 +198,7 @@ namespace MediaBrowser.Controller.Entities.Audio
return true;
}
}
+
return base.RequiresRefresh();
}
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs
index 1fd706857..623262407 100644
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ b/MediaBrowser.Controller/Entities/BaseItem.cs
@@ -19,7 +19,6 @@ using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Library;
@@ -34,7 +33,7 @@ namespace MediaBrowser.Controller.Entities
/// <summary>
/// Class BaseItem
/// </summary>
- public abstract class BaseItem : IHasProviderIds, IHasLookupInfo<ItemLookupInfo>
+ public abstract class BaseItem : IHasProviderIds, IHasLookupInfo<ItemLookupInfo>, IEquatable<BaseItem>
{
/// <summary>
/// The supported image extensions
@@ -388,15 +387,12 @@ namespace MediaBrowser.Controller.Entities
while (thisMarker < s1.Length)
{
- if (thisMarker >= s1.Length)
- {
- break;
- }
char thisCh = s1[thisMarker];
var thisChunk = new StringBuilder();
+ bool isNumeric = char.IsDigit(thisCh);
- while ((thisMarker < s1.Length) && (thisChunk.Length == 0 || SortHelper.InChunk(thisCh, thisChunk[0])))
+ while (thisMarker < s1.Length && char.IsDigit(thisCh) == isNumeric)
{
thisChunk.Append(thisCh);
thisMarker++;
@@ -407,7 +403,6 @@ namespace MediaBrowser.Controller.Entities
}
}
- var isNumeric = thisChunk.Length > 0 && char.IsDigit(thisChunk[0]);
list.Add(new Tuple<StringBuilder, bool>(thisChunk, isNumeric));
}
@@ -1098,6 +1093,7 @@ namespace MediaBrowser.Controller.Entities
Id = item.Id.ToString("N", CultureInfo.InvariantCulture),
Protocol = protocol ?? MediaProtocol.File,
MediaStreams = MediaSourceManager.GetMediaStreams(item.Id),
+ MediaAttachments = MediaSourceManager.GetMediaAttachments(item.Id),
Name = GetMediaSourceName(item),
Path = enablePathSubstitution ? GetMappedPath(item, item.Path, protocol) : item.Path,
RunTimeTicks = item.RunTimeTicks,
@@ -2194,13 +2190,9 @@ namespace MediaBrowser.Controller.Entities
/// <summary>
/// Do whatever refreshing is necessary when the filesystem pertaining to this item has changed.
/// </summary>
- /// <returns>Task.</returns>
public virtual void ChangedExternally()
{
- ProviderManager.QueueRefresh(Id, new MetadataRefreshOptions(new DirectoryService(FileSystem))
- {
-
- }, RefreshPriority.High);
+ ProviderManager.QueueRefresh(Id, new MetadataRefreshOptions(new DirectoryService(FileSystem)), RefreshPriority.High);
}
/// <summary>
@@ -2231,7 +2223,6 @@ namespace MediaBrowser.Controller.Entities
existingImage.Width = image.Width;
existingImage.Height = image.Height;
}
-
else
{
var current = ImageInfos;
@@ -2274,7 +2265,6 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
/// <param name="type">The type.</param>
/// <param name="index">The index.</param>
- /// <returns>Task.</returns>
public void DeleteImage(ImageType type, int index)
{
var info = GetImageInfo(type, index);
@@ -2312,7 +2302,7 @@ namespace MediaBrowser.Controller.Entities
}
/// <summary>
- /// Validates that images within the item are still on the file system
+ /// Validates that images within the item are still on the filesystem.
/// </summary>
public bool ValidateImages(IDirectoryService directoryService)
{
@@ -2606,7 +2596,7 @@ namespace MediaBrowser.Controller.Entities
}
/// <summary>
- /// This is called before any metadata refresh and returns true or false indicating if changes were made
+ /// This is called before any metadata refresh and returns true if changes were made.
/// </summary>
public virtual bool BeforeMetadataRefresh(bool replaceAllMetdata)
{
@@ -2666,36 +2656,43 @@ namespace MediaBrowser.Controller.Entities
newOptions.ForceSave = true;
ownedItem.Genres = item.Genres;
}
+
if (!item.Studios.SequenceEqual(ownedItem.Studios, StringComparer.Ordinal))
{
newOptions.ForceSave = true;
ownedItem.Studios = item.Studios;
}
+
if (!item.ProductionLocations.SequenceEqual(ownedItem.ProductionLocations, StringComparer.Ordinal))
{
newOptions.ForceSave = true;
ownedItem.ProductionLocations = item.ProductionLocations;
}
+
if (item.CommunityRating != ownedItem.CommunityRating)
{
ownedItem.CommunityRating = item.CommunityRating;
newOptions.ForceSave = true;
}
+
if (item.CriticRating != ownedItem.CriticRating)
{
ownedItem.CriticRating = item.CriticRating;
newOptions.ForceSave = true;
}
+
if (!string.Equals(item.Overview, ownedItem.Overview, StringComparison.Ordinal))
{
ownedItem.Overview = item.Overview;
newOptions.ForceSave = true;
}
+
if (!string.Equals(item.OfficialRating, ownedItem.OfficialRating, StringComparison.Ordinal))
{
ownedItem.OfficialRating = item.OfficialRating;
newOptions.ForceSave = true;
}
+
if (!string.Equals(item.CustomRating, ownedItem.CustomRating, StringComparison.Ordinal))
{
ownedItem.CustomRating = item.CustomRating;
@@ -2904,11 +2901,17 @@ namespace MediaBrowser.Controller.Entities
}
public virtual bool IsHD => Height >= 720;
+
public bool IsShortcut { get; set; }
+
public string ShortcutPath { get; set; }
+
public int Width { get; set; }
+
public int Height { get; set; }
+
public Guid[] ExtraIds { get; set; }
+
public virtual long GetRunTimeTicksForPlayState()
{
return RunTimeTicks ?? 0;
@@ -2918,5 +2921,17 @@ namespace MediaBrowser.Controller.Entities
public static readonly IReadOnlyCollection<ExtraType> DisplayExtraTypes = new[] { Model.Entities.ExtraType.BehindTheScenes, Model.Entities.ExtraType.Clip, Model.Entities.ExtraType.DeletedScene, Model.Entities.ExtraType.Interview, Model.Entities.ExtraType.Sample, Model.Entities.ExtraType.Scene };
public virtual bool SupportsExternalTransfer => false;
+
+ /// <inheritdoc />
+ public override bool Equals(object obj)
+ {
+ return obj is BaseItem baseItem && this.Equals(baseItem);
+ }
+
+ /// <inheritdoc />
+ public bool Equals(BaseItem item) => Object.Equals(Id, item?.Id);
+
+ /// <inheritdoc />
+ public override int GetHashCode() => HashCode.Combine(Id);
}
}
diff --git a/MediaBrowser.Controller/Entities/Book.cs b/MediaBrowser.Controller/Entities/Book.cs
index 44c35374d..dcad2554b 100644
--- a/MediaBrowser.Controller/Entities/Book.cs
+++ b/MediaBrowser.Controller/Entities/Book.cs
@@ -13,8 +13,10 @@ namespace MediaBrowser.Controller.Entities
[JsonIgnore]
public string SeriesPresentationUniqueKey { get; set; }
+
[JsonIgnore]
public string SeriesName { get; set; }
+
[JsonIgnore]
public Guid SeriesId { get; set; }
@@ -22,10 +24,12 @@ namespace MediaBrowser.Controller.Entities
{
return SeriesName;
}
+
public string FindSeriesName()
{
return SeriesName;
}
+
public string FindSeriesPresentationUniqueKey()
{
return SeriesPresentationUniqueKey;
diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs
index 07fbe6035..c72bd487e 100644
--- a/MediaBrowser.Controller/Entities/Folder.cs
+++ b/MediaBrowser.Controller/Entities/Folder.cs
@@ -322,10 +322,10 @@ namespace MediaBrowser.Controller.Entities
ProviderManager.OnRefreshProgress(this, 5);
}
- //build a dictionary of the current children we have now by Id so we can compare quickly and easily
+ // Build a dictionary of the current children we have now by Id so we can compare quickly and easily
var currentChildren = GetActualChildrenDictionary();
- //create a list for our validated children
+ // Create a list for our validated children
var newItems = new List<BaseItem>();
cancellationToken.ThrowIfCancellationRequested();
@@ -391,7 +391,7 @@ namespace MediaBrowser.Controller.Entities
var folder = this;
innerProgress.RegisterAction(p =>
{
- double newPct = .80 * p + 10;
+ double newPct = 0.80 * p + 10;
progress.Report(newPct);
ProviderManager.OnRefreshProgress(folder, newPct);
});
@@ -421,7 +421,7 @@ namespace MediaBrowser.Controller.Entities
var folder = this;
innerProgress.RegisterAction(p =>
{
- double newPct = .10 * p + 90;
+ double newPct = 0.10 * p + 90;
progress.Report(newPct);
if (recursive)
{
@@ -807,11 +807,45 @@ namespace MediaBrowser.Controller.Entities
return false;
}
+ private static BaseItem[] SortItemsByRequest(InternalItemsQuery query, IReadOnlyList<BaseItem> items)
+ {
+ var ids = query.ItemIds;
+ int size = items.Count;
+
+ // ids can potentially contain non-unique guids, but query result cannot,
+ // so we include only first occurrence of each guid
+ var positions = new Dictionary<Guid, int>(size);
+ int index = 0;
+ for (int i = 0; i < ids.Length; i++)
+ {
+ if (positions.TryAdd(ids[i], index))
+ {
+ index++;
+ }
+ }
+
+ var newItems = new BaseItem[size];
+ for (int i = 0; i < size; i++)
+ {
+ var item = items[i];
+ newItems[positions[item.Id]] = item;
+ }
+
+ return newItems;
+ }
+
public QueryResult<BaseItem> GetItems(InternalItemsQuery query)
{
if (query.ItemIds.Length > 0)
{
- return LibraryManager.GetItemsResult(query);
+ var result = LibraryManager.GetItemsResult(query);
+
+ if (query.OrderBy.Count == 0 && query.ItemIds.Length > 1)
+ {
+ result.Items = SortItemsByRequest(query, result.Items);
+ }
+
+ return result;
}
return GetItemsInternal(query);
@@ -823,7 +857,14 @@ namespace MediaBrowser.Controller.Entities
if (query.ItemIds.Length > 0)
{
- return LibraryManager.GetItemList(query);
+ var result = LibraryManager.GetItemList(query);
+
+ if (query.OrderBy.Count == 0 && query.ItemIds.Length > 1)
+ {
+ return SortItemsByRequest(query, result);
+ }
+
+ return result.ToArray();
}
return GetItemsInternal(query).Items;
diff --git a/MediaBrowser.Controller/Entities/Person.cs b/MediaBrowser.Controller/Entities/Person.cs
index d9b4b2206..64e216e69 100644
--- a/MediaBrowser.Controller/Entities/Person.cs
+++ b/MediaBrowser.Controller/Entities/Person.cs
@@ -135,57 +135,4 @@ namespace MediaBrowser.Controller.Entities
return hasChanges;
}
}
-
- /// <summary>
- /// This is the small Person stub that is attached to BaseItems
- /// </summary>
- public class PersonInfo : IHasProviderIds
- {
- public PersonInfo()
- {
- ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- }
-
- public Guid ItemId { get; set; }
-
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
- /// <summary>
- /// Gets or sets the role.
- /// </summary>
- /// <value>The role.</value>
- public string Role { get; set; }
- /// <summary>
- /// Gets or sets the type.
- /// </summary>
- /// <value>The type.</value>
- public string Type { get; set; }
-
- /// <summary>
- /// Gets or sets the sort order - ascending
- /// </summary>
- /// <value>The sort order.</value>
- public int? SortOrder { get; set; }
-
- public string ImageUrl { get; set; }
-
- public Dictionary<string, string> ProviderIds { get; set; }
-
- /// <summary>
- /// Returns a <see cref="string" /> that represents this instance.
- /// </summary>
- /// <returns>A <see cref="string" /> that represents this instance.</returns>
- public override string ToString()
- {
- return Name;
- }
-
- public bool IsType(string type)
- {
- return string.Equals(Type, type, StringComparison.OrdinalIgnoreCase) || string.Equals(Role, type, StringComparison.OrdinalIgnoreCase);
- }
- }
}
diff --git a/MediaBrowser.Controller/Entities/PersonInfo.cs b/MediaBrowser.Controller/Entities/PersonInfo.cs
new file mode 100644
index 000000000..e90c55a8a
--- /dev/null
+++ b/MediaBrowser.Controller/Entities/PersonInfo.cs
@@ -0,0 +1,63 @@
+using System;
+using System.Linq;
+using System.Collections.Generic;
+using MediaBrowser.Model.Entities;
+
+namespace MediaBrowser.Controller.Entities
+{
+ /// <summary>
+ /// This is a small Person stub that is attached to BaseItems.
+ /// </summary>
+ public sealed class PersonInfo : IHasProviderIds
+ {
+ public PersonInfo()
+ {
+ ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+ }
+
+ public Guid ItemId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ /// <value>The name.</value>
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets or sets the role.
+ /// </summary>
+ /// <value>The role.</value>
+ public string Role { get; set; }
+
+ /// <summary>
+ /// Gets or sets the type.
+ /// </summary>
+ /// <value>The type.</value>
+ public string Type { get; set; }
+
+ /// <summary>
+ /// Gets or sets the ascending sort order.
+ /// </summary>
+ /// <value>The sort order.</value>
+ public int? SortOrder { get; set; }
+
+ public string ImageUrl { get; set; }
+
+ public Dictionary<string, string> ProviderIds { get; set; }
+
+ /// <summary>
+ /// Returns a <see cref="string" /> that represents this instance.
+ /// </summary>
+ /// <returns>A <see cref="string" /> that represents this instance.</returns>
+ public override string ToString()
+ {
+ return Name;
+ }
+
+ public bool IsType(string type)
+ {
+ return string.Equals(Type, type, StringComparison.OrdinalIgnoreCase)
+ || string.Equals(Role, type, StringComparison.OrdinalIgnoreCase);
+ }
+ }
+}
diff --git a/MediaBrowser.Controller/Entities/Photo.cs b/MediaBrowser.Controller/Entities/Photo.cs
index 86d62add9..5ebc9f16a 100644
--- a/MediaBrowser.Controller/Entities/Photo.cs
+++ b/MediaBrowser.Controller/Entities/Photo.cs
@@ -41,10 +41,10 @@ namespace MediaBrowser.Controller.Entities
public override double GetDefaultPrimaryImageAspectRatio()
{
// REVIEW: @bond
- if (Width.HasValue && Height.HasValue)
+ if (Width != 0 && Height != 0)
{
- double width = Width.Value;
- double height = Height.Value;
+ double width = Width;
+ double height = Height;
if (Orientation.HasValue)
{
@@ -67,8 +67,6 @@ namespace MediaBrowser.Controller.Entities
return base.GetDefaultPrimaryImageAspectRatio();
}
- public new int? Width { get; set; }
- public new int? Height { get; set; }
public string CameraMake { get; set; }
public string CameraModel { get; set; }
public string Software { get; set; }
diff --git a/MediaBrowser.Controller/Entities/UserRootFolder.cs b/MediaBrowser.Controller/Entities/UserRootFolder.cs
index 7fcf48a48..8a68f830c 100644
--- a/MediaBrowser.Controller/Entities/UserRootFolder.cs
+++ b/MediaBrowser.Controller/Entities/UserRootFolder.cs
@@ -60,14 +60,7 @@ namespace MediaBrowser.Controller.Entities
PresetViews = query.PresetViews
});
- var itemsArray = result;
- var totalCount = itemsArray.Length;
-
- return new QueryResult<BaseItem>
- {
- TotalRecordCount = totalCount,
- Items = itemsArray //TODO Fix The co-variant conversion between Folder[] and BaseItem[], this can generate runtime issues.
- };
+ return UserViewBuilder.SortAndPage(result, null, query, LibraryManager, true);
}
public override int GetChildCount(User user)
diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs
index af4d227bc..c3ea7f347 100644
--- a/MediaBrowser.Controller/Entities/Video.cs
+++ b/MediaBrowser.Controller/Entities/Video.cs
@@ -7,7 +7,6 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Dto;
diff --git a/MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs b/MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs
index 76c9b4b26..48316499a 100644
--- a/MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs
+++ b/MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs
@@ -13,24 +13,37 @@ namespace MediaBrowser.Controller.Extensions
public const string FfmpegProbeSizeKey = "FFmpeg:probesize";
/// <summary>
- /// The key for the FFmpeg analyse duration option.
+ /// The key for the FFmpeg analyze duration option.
/// </summary>
public const string FfmpegAnalyzeDurationKey = "FFmpeg:analyzeduration";
/// <summary>
- /// Retrieves the FFmpeg probe size from the <see cref="IConfiguration" />.
+ /// The key for a setting that indicates whether playlists should allow duplicate entries.
/// </summary>
- /// <param name="configuration">This configuration.</param>
+ public const string PlaylistsAllowDuplicatesKey = "playlists:allowDuplicates";
+
+ /// <summary>
+ /// Gets the FFmpeg probe size from the <see cref="IConfiguration" />.
+ /// </summary>
+ /// <param name="configuration">The configuration to read the setting from.</param>
/// <returns>The FFmpeg probe size option.</returns>
public static string GetFFmpegProbeSize(this IConfiguration configuration)
=> configuration[FfmpegProbeSizeKey];
/// <summary>
- /// Retrieves the FFmpeg analyse duration from the <see cref="IConfiguration" />.
+ /// Gets the FFmpeg analyze duration from the <see cref="IConfiguration" />.
/// </summary>
- /// <param name="configuration">This configuration.</param>
- /// <returns>The FFmpeg analyse duration option.</returns>
+ /// <param name="configuration">The configuration to read the setting from.</param>
+ /// <returns>The FFmpeg analyze duration option.</returns>
public static string GetFFmpegAnalyzeDuration(this IConfiguration configuration)
=> configuration[FfmpegAnalyzeDurationKey];
+
+ /// <summary>
+ /// Gets a value indicating whether playlists should allow duplicate entries from the <see cref="IConfiguration"/>.
+ /// </summary>
+ /// <param name="configuration">The configuration to read the setting from.</param>
+ /// <returns>True if playlists should allow duplicates, otherwise false.</returns>
+ public static bool DoPlaylistsAllowDuplicates(this IConfiguration configuration)
+ => configuration.GetValue<bool>(PlaylistsAllowDuplicatesKey);
}
}
diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs
index b3c56bdd5..25f0905eb 100644
--- a/MediaBrowser.Controller/IServerApplicationHost.cs
+++ b/MediaBrowser.Controller/IServerApplicationHost.cs
@@ -71,13 +71,15 @@ namespace MediaBrowser.Controller
/// <summary>
/// Gets the local API URL.
/// </summary>
- /// <param name="host">The host.</param>
- /// <returns>System.String.</returns>
- string GetLocalApiUrl(string host);
+ /// <param name="hostname">The hostname.</param>
+ /// <returns>The local API URL.</returns>
+ string GetLocalApiUrl(ReadOnlySpan<char> hostname);
/// <summary>
/// Gets the local API URL.
/// </summary>
+ /// <param name="address">The IP address.</param>
+ /// <returns>The local API URL.</returns>
string GetLocalApiUrl(IPAddress address);
void LaunchUrl(string url);
diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs
index 511356aa4..2e1c97f67 100644
--- a/MediaBrowser.Controller/Library/ILibraryManager.cs
+++ b/MediaBrowser.Controller/Library/ILibraryManager.cs
@@ -157,7 +157,8 @@ namespace MediaBrowser.Controller.Library
/// <param name="introProviders">The intro providers.</param>
/// <param name="itemComparers">The item comparers.</param>
/// <param name="postscanTasks">The postscan tasks.</param>
- void AddParts(IEnumerable<IResolverIgnoreRule> rules,
+ void AddParts(
+ IEnumerable<IResolverIgnoreRule> rules,
IEnumerable<IItemResolver> resolvers,
IEnumerable<IIntroProvider> introProviders,
IEnumerable<IBaseItemComparer> itemComparers,
@@ -349,9 +350,6 @@ namespace MediaBrowser.Controller.Library
/// <returns><c>true</c> if [is audio file] [the specified path]; otherwise, <c>false</c>.</returns>
bool IsAudioFile(string path);
- bool IsAudioFile(string path, LibraryOptions libraryOptions);
- bool IsVideoFile(string path, LibraryOptions libraryOptions);
-
/// <summary>
/// Gets the season number from path.
/// </summary>
diff --git a/MediaBrowser.Controller/Library/IMediaSourceManager.cs b/MediaBrowser.Controller/Library/IMediaSourceManager.cs
index fbae4edb0..0ceabd0e6 100644
--- a/MediaBrowser.Controller/Library/IMediaSourceManager.cs
+++ b/MediaBrowser.Controller/Library/IMediaSourceManager.cs
@@ -39,9 +39,23 @@ namespace MediaBrowser.Controller.Library
List<MediaStream> GetMediaStreams(MediaStreamQuery query);
/// <summary>
+ /// Gets the media attachments.
+ /// </summary>
+ /// <param name="itemId">The item identifier.</param>
+ /// <returns>IEnumerable&lt;MediaAttachment&gt;.</returns>
+ List<MediaAttachment> GetMediaAttachments(Guid itemId);
+
+ /// <summary>
+ /// Gets the media attachments.
+ /// </summary>
+ /// <param name="query">The query.</param>
+ /// <returns>IEnumerable&lt;MediaAttachment&gt;.</returns>
+ List<MediaAttachment> GetMediaAttachments(MediaAttachmentQuery query);
+
+ /// <summary>
/// Gets the playack media sources.
/// </summary>
- Task<List<MediaSourceInfo>> GetPlayackMediaSources(BaseItem item, User user, bool allowMediaProbe, bool enablePathSubstitution, CancellationToken cancellationToken);
+ Task<List<MediaSourceInfo>> GetPlaybackMediaSources(BaseItem item, User user, bool allowMediaProbe, bool enablePathSubstitution, CancellationToken cancellationToken);
/// <summary>
/// Gets the static media sources.
diff --git a/MediaBrowser.Controller/Library/IMediaSourceProvider.cs b/MediaBrowser.Controller/Library/IMediaSourceProvider.cs
index 9e74879fc..ec7798551 100644
--- a/MediaBrowser.Controller/Library/IMediaSourceProvider.cs
+++ b/MediaBrowser.Controller/Library/IMediaSourceProvider.cs
@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
diff --git a/MediaBrowser.Controller/Library/Profiler.cs b/MediaBrowser.Controller/Library/Profiler.cs
index 9fe175a7c..46a97d181 100644
--- a/MediaBrowser.Controller/Library/Profiler.cs
+++ b/MediaBrowser.Controller/Library/Profiler.cs
@@ -28,7 +28,7 @@ namespace MediaBrowser.Controller.Library
/// </summary>
/// <param name="name">The name.</param>
/// <param name="logger">The logger.</param>
- public Profiler(string name, ILogger logger)
+ public Profiler(string name, ILogger<Profiler> logger)
{
this._name = name;
diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
index 60c76ef7d..bcca9e4a1 100644
--- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj
+++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
@@ -8,7 +8,8 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.0.0" />
+ <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.1" />
+ <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="3.1.1" />
</ItemGroup>
<ItemGroup>
@@ -26,4 +27,16 @@
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
+ <!-- Code Analyzers-->
+ <ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
+ <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8" PrivateAssets="All" />
+ <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
+ <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" />
+ <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
+ </ItemGroup>
+
+ <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
+ <CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
+ </PropertyGroup>
+
</Project>
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index 020f0553e..342c76414 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -475,7 +475,7 @@ namespace MediaBrowser.Controller.MediaEncoding
.Append(' ');
}
- if (state.IsVideoRequest
+ if (state.IsVideoRequest
&& string.Equals(encodingOptions.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase))
{
var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, encodingOptions);
@@ -486,11 +486,11 @@ namespace MediaBrowser.Controller.MediaEncoding
if (!string.IsNullOrEmpty(videoDecoder) && videoDecoder.Contains("qsv", StringComparison.OrdinalIgnoreCase))
{
arg.Append("-hwaccel qsv ");
- }
- else
+ }
+ else
{
arg.Append("-init_hw_device qsv=hw -filter_hw_device hw ");
- }
+ }
}
arg.Append(videoDecoder + " ");
@@ -653,7 +653,7 @@ namespace MediaBrowser.Controller.MediaEncoding
// _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(fallbackFontPath));
// using (var stream = _assemblyInfo.GetManifestResourceStream(GetType(), GetType().Namespace + ".DroidSansFallback.ttf"))
// {
- // using (var fileStream = _fileSystem.GetFileStream(fallbackFontPath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
+ // using (var fileStream = new FileStream(fallbackFontPath, FileMode.Create, FileAccess.Write, FileShare.Read))
// {
// stream.CopyTo(fileStream);
// }
@@ -1624,22 +1624,22 @@ namespace MediaBrowser.Controller.MediaEncoding
// Setup default filtergraph utilizing FFMpeg overlay() and FFMpeg scale() (see the return of this function for index reference)
var retStr = " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay{3}\"";
- if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase))
+ if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase))
{
/*
QSV in FFMpeg can now setup hardware overlay for transcodes.
For software decoding and hardware encoding option, frames must be hwuploaded into hardware
- with fixed frame size.
+ with fixed frame size.
*/
if (!string.IsNullOrEmpty(videoDecoder) && videoDecoder.Contains("qsv", StringComparison.OrdinalIgnoreCase))
{
retStr = " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay_qsv=x=(W-w)/2:y=(H-h)/2{3}\"";
- }
- else
+ }
+ else
{
retStr = " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]hwupload=extra_hw_frames=64[v];[v][sub]overlay_qsv=x=(W-w)/2:y=(H-h)/2{3}\"";
}
- }
+ }
return string.Format(
CultureInfo.InvariantCulture,
@@ -1731,8 +1731,8 @@ namespace MediaBrowser.Controller.MediaEncoding
vaapi_or_qsv,
outputWidth,
outputHeight));
- }
- else
+ }
+ else
{
filters.Add(string.Format(CultureInfo.InvariantCulture, "scale_{0}=format=nv12", vaapi_or_qsv));
}
@@ -1979,8 +1979,8 @@ namespace MediaBrowser.Controller.MediaEncoding
var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, options);
- // If we are software decoding, and hardware encoding
- if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase)
+ // If we are software decoding, and hardware encoding
+ if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase)
&& (string.IsNullOrEmpty(videoDecoder) || !videoDecoder.Contains("qsv", StringComparison.OrdinalIgnoreCase)))
{
filters.Add("format=nv12|qsv");
@@ -2642,22 +2642,9 @@ namespace MediaBrowser.Controller.MediaEncoding
else
return "-hwaccel dxva2";
}
-
- switch (videoStream.Codec.ToLowerInvariant())
+ else
{
- case "avc":
- case "h264":
- if (_mediaEncoder.SupportsDecoder("h264_amf") && encodingOptions.HardwareDecodingCodecs.Contains("h264", StringComparer.OrdinalIgnoreCase))
- {
- return "-c:v h264_amf";
- }
- break;
- case "mpeg2video":
- if (_mediaEncoder.SupportsDecoder("hevc_amf") && encodingOptions.HardwareDecodingCodecs.Contains("mpeg2video", StringComparer.OrdinalIgnoreCase))
- {
- return "-c:v mpeg2_mmal";
- }
- break;
+ return "-hwaccel vaapi";
}
}
}
@@ -2770,7 +2757,7 @@ namespace MediaBrowser.Controller.MediaEncoding
if (!state.RunTimeTicks.HasValue)
{
- args += " -flags -global_header -fflags +genpts";
+ args += " -fflags +genpts";
}
}
else
@@ -2815,11 +2802,6 @@ namespace MediaBrowser.Controller.MediaEncoding
{
args += " " + qualityParam.Trim();
}
-
- if (!state.RunTimeTicks.HasValue)
- {
- args += " -flags -global_header";
- }
}
if (!string.IsNullOrEmpty(state.OutputVideoSync))
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
index 34af3b156..38ef33caf 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
@@ -316,11 +316,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (VideoStream != null && VideoStream.Width.HasValue && VideoStream.Height.HasValue)
{
- var size = new ImageDimensions
- {
- Width = VideoStream.Width.Value,
- Height = VideoStream.Height.Value
- };
+ var size = new ImageDimensions(VideoStream.Width.Value, VideoStream.Height.Value);
var newSize = DrawingUtils.Resize(size,
BaseRequest.Width ?? 0,
@@ -346,11 +342,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (VideoStream != null && VideoStream.Width.HasValue && VideoStream.Height.HasValue)
{
- var size = new ImageDimensions
- {
- Width = VideoStream.Width.Value,
- Height = VideoStream.Height.Value
- };
+ var size = new ImageDimensions(VideoStream.Width.Value, VideoStream.Height.Value);
var newSize = DrawingUtils.Resize(size,
BaseRequest.Width ?? 0,
@@ -665,7 +657,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
public IProgress<double> Progress { get; set; }
- public virtual void ReportTranscodingProgress(TimeSpan? transcodingPosition, float framerate, double? percentComplete, long bytesTranscoded, int? bitRate)
+ public virtual void ReportTranscodingProgress(TimeSpan? transcodingPosition, float? framerate, double? percentComplete, long? bytesTranscoded, int? bitRate)
{
Progress.Report(percentComplete.Value);
}
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs
index d64feb2f7..addc88174 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
-using System.Globalization;
using System.Linq;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Services;
diff --git a/MediaBrowser.Controller/MediaEncoding/IAttachmentExtractor.cs b/MediaBrowser.Controller/MediaEncoding/IAttachmentExtractor.cs
new file mode 100644
index 000000000..7c7e84de6
--- /dev/null
+++ b/MediaBrowser.Controller/MediaEncoding/IAttachmentExtractor.cs
@@ -0,0 +1,17 @@
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Model.Entities;
+
+namespace MediaBrowser.Controller.MediaEncoding
+{
+ public interface IAttachmentExtractor
+ {
+ Task<(MediaAttachment attachment, Stream stream)> GetAttachment(
+ BaseItem item,
+ string mediaSourceId,
+ int attachmentStreamIndex,
+ CancellationToken cancellationToken);
+ }
+}
diff --git a/MediaBrowser.Controller/MediaEncoding/JobLogger.cs b/MediaBrowser.Controller/MediaEncoding/JobLogger.cs
index ac989f6ba..c9f64c707 100644
--- a/MediaBrowser.Controller/MediaEncoding/JobLogger.cs
+++ b/MediaBrowser.Controller/MediaEncoding/JobLogger.cs
@@ -4,7 +4,6 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
-using MediaBrowser.Model.Extensions;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.Controller.MediaEncoding
@@ -90,6 +89,15 @@ namespace MediaBrowser.Controller.MediaEncoding
framerate = val;
}
}
+ else if (part.StartsWith("fps=", StringComparison.OrdinalIgnoreCase))
+ {
+ var rate = part.Split(new[] { '=' }, 2)[^1];
+
+ if (float.TryParse(rate, NumberStyles.Any, _usCulture, out var val))
+ {
+ framerate = val;
+ }
+ }
else if (state.RunTimeTicks.HasValue &&
part.StartsWith("time=", StringComparison.OrdinalIgnoreCase))
{
@@ -146,7 +154,7 @@ namespace MediaBrowser.Controller.MediaEncoding
if (framerate.HasValue || percent.HasValue)
{
- state.ReportTranscodingProgress(transcodingPosition, 0, percent, 0, bitRate);
+ state.ReportTranscodingProgress(transcodingPosition, framerate, percent, bytesTranscoded, bitRate);
}
}
}
diff --git a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs
index ee5c1a165..b710318ee 100644
--- a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs
+++ b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs
@@ -22,7 +22,7 @@ namespace MediaBrowser.Controller.Net
/// <summary>
/// The _active connections
/// </summary>
- protected readonly List<Tuple<IWebSocketConnection, CancellationTokenSource, TStateType>> ActiveConnections =
+ private readonly List<Tuple<IWebSocketConnection, CancellationTokenSource, TStateType>> _activeConnections =
new List<Tuple<IWebSocketConnection, CancellationTokenSource, TStateType>>();
/// <summary>
@@ -100,9 +100,9 @@ namespace MediaBrowser.Controller.Net
InitialDelayMs = dueTimeMs
};
- lock (ActiveConnections)
+ lock (_activeConnections)
{
- ActiveConnections.Add(new Tuple<IWebSocketConnection, CancellationTokenSource, TStateType>(message.Connection, cancellationTokenSource, state));
+ _activeConnections.Add(new Tuple<IWebSocketConnection, CancellationTokenSource, TStateType>(message.Connection, cancellationTokenSource, state));
}
}
@@ -110,9 +110,9 @@ namespace MediaBrowser.Controller.Net
{
Tuple<IWebSocketConnection, CancellationTokenSource, TStateType>[] tuples;
- lock (ActiveConnections)
+ lock (_activeConnections)
{
- tuples = ActiveConnections
+ tuples = _activeConnections
.Where(c =>
{
if (c.Item1.State == WebSocketState.Open && !c.Item2.IsCancellationRequested)
@@ -180,9 +180,9 @@ namespace MediaBrowser.Controller.Net
/// <param name="message">The message.</param>
private void Stop(WebSocketMessageInfo message)
{
- lock (ActiveConnections)
+ lock (_activeConnections)
{
- var connection = ActiveConnections.FirstOrDefault(c => c.Item1 == message.Connection);
+ var connection = _activeConnections.FirstOrDefault(c => c.Item1 == message.Connection);
if (connection != null)
{
@@ -212,9 +212,9 @@ namespace MediaBrowser.Controller.Net
//TODO Investigate and properly fix.
}
- lock (ActiveConnections)
+ lock (_activeConnections)
{
- ActiveConnections.Remove(connection);
+ _activeConnections.Remove(connection);
}
}
@@ -226,9 +226,9 @@ namespace MediaBrowser.Controller.Net
{
if (dispose)
{
- lock (ActiveConnections)
+ lock (_activeConnections)
{
- foreach (var connection in ActiveConnections.ToArray())
+ foreach (var connection in _activeConnections.ToArray())
{
DisposeConnection(connection);
}
diff --git a/MediaBrowser.Controller/Net/IAuthService.cs b/MediaBrowser.Controller/Net/IAuthService.cs
index 4c9120e0c..9132404a0 100644
--- a/MediaBrowser.Controller/Net/IAuthService.cs
+++ b/MediaBrowser.Controller/Net/IAuthService.cs
@@ -1,3 +1,5 @@
+#nullable enable
+
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Services;
using Microsoft.AspNetCore.Http;
@@ -7,6 +9,6 @@ namespace MediaBrowser.Controller.Net
public interface IAuthService
{
void Authenticate(IRequest request, IAuthenticationAttributes authAttribtues);
- User Authenticate(HttpRequest request, IAuthenticationAttributes authAttribtues);
+ User? Authenticate(HttpRequest request, IAuthenticationAttributes authAttribtues);
}
}
diff --git a/MediaBrowser.Controller/Net/IHttpResultFactory.cs b/MediaBrowser.Controller/Net/IHttpResultFactory.cs
index ff9ecf8af..25404fa78 100644
--- a/MediaBrowser.Controller/Net/IHttpResultFactory.cs
+++ b/MediaBrowser.Controller/Net/IHttpResultFactory.cs
@@ -2,8 +2,6 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
-
-using MediaBrowser.Model.IO;
using MediaBrowser.Model.Services;
namespace MediaBrowser.Controller.Net
@@ -66,7 +64,7 @@ namespace MediaBrowser.Controller.Net
/// <param name="path">The path.</param>
/// <param name="fileShare">The file share.</param>
/// <returns>System.Object.</returns>
- Task<object> GetStaticFileResult(IRequest requestContext, string path, FileShareMode fileShare = FileShareMode.Read);
+ Task<object> GetStaticFileResult(IRequest requestContext, string path, FileShare fileShare = FileShare.Read);
/// <summary>
/// Gets the static file result.
diff --git a/MediaBrowser.Controller/Net/IWebSocketConnection.cs b/MediaBrowser.Controller/Net/IWebSocketConnection.cs
index 566897b31..31eb7ccb7 100644
--- a/MediaBrowser.Controller/Net/IWebSocketConnection.cs
+++ b/MediaBrowser.Controller/Net/IWebSocketConnection.cs
@@ -3,7 +3,6 @@ using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Net;
-using MediaBrowser.Model.Services;
using Microsoft.AspNetCore.Http;
namespace MediaBrowser.Controller.Net
diff --git a/MediaBrowser.Controller/Net/StaticResultOptions.cs b/MediaBrowser.Controller/Net/StaticResultOptions.cs
index 7a179913a..071beaed1 100644
--- a/MediaBrowser.Controller/Net/StaticResultOptions.cs
+++ b/MediaBrowser.Controller/Net/StaticResultOptions.cs
@@ -3,8 +3,6 @@ using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
-using MediaBrowser.Model.IO;
-
namespace MediaBrowser.Controller.Net
{
public class StaticResultOptions
@@ -24,12 +22,12 @@ namespace MediaBrowser.Controller.Net
public string Path { get; set; }
public long? ContentLength { get; set; }
- public FileShareMode FileShare { get; set; }
+ public FileShare FileShare { get; set; }
public StaticResultOptions()
{
ResponseHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- FileShare = FileShareMode.Read;
+ FileShare = FileShare.Read;
}
}
diff --git a/MediaBrowser.Controller/Persistence/IItemRepository.cs b/MediaBrowser.Controller/Persistence/IItemRepository.cs
index 47e0f3453..5a5b7f58f 100644
--- a/MediaBrowser.Controller/Persistence/IItemRepository.cs
+++ b/MediaBrowser.Controller/Persistence/IItemRepository.cs
@@ -79,6 +79,21 @@ namespace MediaBrowser.Controller.Persistence
void SaveMediaStreams(Guid id, List<MediaStream> streams, CancellationToken cancellationToken);
/// <summary>
+ /// Gets the media attachments.
+ /// </summary>
+ /// <param name="query">The query.</param>
+ /// <returns>IEnumerable{MediaAttachment}.</returns>
+ List<MediaAttachment> GetMediaAttachments(MediaAttachmentQuery query);
+
+ /// <summary>
+ /// Saves the media attachments.
+ /// </summary>
+ /// <param name="id">The identifier.</param>
+ /// <param name="attachments">The attachments.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ void SaveMediaAttachments(Guid id, IReadOnlyList<MediaAttachment> attachments, CancellationToken cancellationToken);
+
+ /// <summary>
/// Gets the item ids.
/// </summary>
/// <param name="query">The query.</param>
diff --git a/MediaBrowser.Controller/Persistence/MediaAttachmentQuery.cs b/MediaBrowser.Controller/Persistence/MediaAttachmentQuery.cs
new file mode 100644
index 000000000..e3b2d4665
--- /dev/null
+++ b/MediaBrowser.Controller/Persistence/MediaAttachmentQuery.cs
@@ -0,0 +1,19 @@
+using System;
+
+namespace MediaBrowser.Controller.Persistence
+{
+ public class MediaAttachmentQuery
+ {
+ /// <summary>
+ /// Gets or sets the index.
+ /// </summary>
+ /// <value>The index.</value>
+ public int? Index { get; set; }
+
+ /// <summary>
+ /// Gets or sets the item identifier.
+ /// </summary>
+ /// <value>The item identifier.</value>
+ public Guid ItemId { get; set; }
+ }
+}
diff --git a/MediaBrowser.Controller/Playlists/IPlaylistManager.cs b/MediaBrowser.Controller/Playlists/IPlaylistManager.cs
index 5001f6842..544cd2643 100644
--- a/MediaBrowser.Controller/Playlists/IPlaylistManager.cs
+++ b/MediaBrowser.Controller/Playlists/IPlaylistManager.cs
@@ -29,7 +29,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, IEnumerable<Guid> itemIds, Guid userId);
+ void AddToPlaylist(string playlistId, ICollection<Guid> itemIds, Guid userId);
/// <summary>
/// Removes from playlist.
diff --git a/MediaBrowser.Controller/Plugins/IServerEntryPoint.cs b/MediaBrowser.Controller/Plugins/IServerEntryPoint.cs
index e57929989..1e8654c4d 100644
--- a/MediaBrowser.Controller/Plugins/IServerEntryPoint.cs
+++ b/MediaBrowser.Controller/Plugins/IServerEntryPoint.cs
@@ -4,16 +4,22 @@ using System.Threading.Tasks;
namespace MediaBrowser.Controller.Plugins
{
/// <summary>
- /// Interface IServerEntryPoint
+ /// Represents an entry point for a module in the application. This interface is scanned for automatically and
+ /// provides a hook to initialize the module at application start.
+ /// The entry point can additionally be flagged as a pre-startup task by implementing the
+ /// <see cref="IRunBeforeStartup"/> interface.
/// </summary>
public interface IServerEntryPoint : IDisposable
{
/// <summary>
- /// Runs this instance.
+ /// Run the initialization for this module. This method is invoked at application start.
/// </summary>
Task RunAsync();
}
+ /// <summary>
+ /// Indicates that a <see cref="IServerEntryPoint"/> should be invoked as a pre-startup task.
+ /// </summary>
public interface IRunBeforeStartup
{
diff --git a/MediaBrowser.Controller/Providers/AlbumInfo.cs b/MediaBrowser.Controller/Providers/AlbumInfo.cs
index ac6b86c1d..dbda4843f 100644
--- a/MediaBrowser.Controller/Providers/AlbumInfo.cs
+++ b/MediaBrowser.Controller/Providers/AlbumInfo.cs
@@ -16,6 +16,7 @@ namespace MediaBrowser.Controller.Providers
/// </summary>
/// <value>The artist provider ids.</value>
public Dictionary<string, string> ArtistProviderIds { get; set; }
+
public List<SongInfo> SongInfos { get; set; }
public AlbumInfo()
diff --git a/MediaBrowser.Controller/Providers/BoxSetInfo.cs b/MediaBrowser.Controller/Providers/BoxSetInfo.cs
index 4cbe2d6ef..d23f2b9bf 100644
--- a/MediaBrowser.Controller/Providers/BoxSetInfo.cs
+++ b/MediaBrowser.Controller/Providers/BoxSetInfo.cs
@@ -2,6 +2,5 @@ namespace MediaBrowser.Controller.Providers
{
public class BoxSetInfo : ItemLookupInfo
{
-
}
}
diff --git a/MediaBrowser.Controller/Providers/DirectoryService.cs b/MediaBrowser.Controller/Providers/DirectoryService.cs
index 303b03a21..ca470872b 100644
--- a/MediaBrowser.Controller/Providers/DirectoryService.cs
+++ b/MediaBrowser.Controller/Providers/DirectoryService.cs
@@ -26,7 +26,6 @@ namespace MediaBrowser.Controller.Providers
{
entries = _fileSystem.GetFileSystemEntries(path).ToArray();
- //_cache.TryAdd(path, entries);
_cache[path] = entries;
}
@@ -56,7 +55,6 @@ namespace MediaBrowser.Controller.Providers
if (file != null && file.Exists)
{
- //_fileCache.TryAdd(path, file);
_fileCache[path] = file;
}
else
@@ -66,7 +64,6 @@ namespace MediaBrowser.Controller.Providers
}
return file;
- //return _fileSystem.GetFileInfo(path);
}
public List<string> GetFilePaths(string path)
diff --git a/MediaBrowser.Controller/Providers/DynamicImageInfo.cs b/MediaBrowser.Controller/Providers/DynamicImageInfo.cs
deleted file mode 100644
index 0791783c6..000000000
--- a/MediaBrowser.Controller/Providers/DynamicImageInfo.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using MediaBrowser.Model.Entities;
-
-namespace MediaBrowser.Controller.Providers
-{
- public class DynamicImageInfo
- {
- public string ImageId { get; set; }
- public ImageType Type { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/Providers/DynamicImageResponse.cs b/MediaBrowser.Controller/Providers/DynamicImageResponse.cs
index 11c7ccbe5..7c1371702 100644
--- a/MediaBrowser.Controller/Providers/DynamicImageResponse.cs
+++ b/MediaBrowser.Controller/Providers/DynamicImageResponse.cs
@@ -8,9 +8,13 @@ namespace MediaBrowser.Controller.Providers
public class DynamicImageResponse
{
public string Path { get; set; }
+
public MediaProtocol Protocol { get; set; }
+
public Stream Stream { get; set; }
+
public ImageFormat Format { get; set; }
+
public bool HasImage { get; set; }
public void SetFormatFromMimeType(string mimeType)
diff --git a/MediaBrowser.Controller/Providers/EpisodeInfo.cs b/MediaBrowser.Controller/Providers/EpisodeInfo.cs
index 6ecf4edc5..55c41ff82 100644
--- a/MediaBrowser.Controller/Providers/EpisodeInfo.cs
+++ b/MediaBrowser.Controller/Providers/EpisodeInfo.cs
@@ -10,6 +10,7 @@ namespace MediaBrowser.Controller.Providers
public int? IndexNumberEnd { get; set; }
public bool IsMissingEpisode { get; set; }
+
public string SeriesDisplayOrder { get; set; }
public EpisodeInfo()
diff --git a/MediaBrowser.Controller/Providers/ExtraInfo.cs b/MediaBrowser.Controller/Providers/ExtraInfo.cs
deleted file mode 100644
index 413ff2e78..000000000
--- a/MediaBrowser.Controller/Providers/ExtraInfo.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using MediaBrowser.Model.Entities;
-
-namespace MediaBrowser.Controller.Providers
-{
- public class ExtraInfo
- {
- public string Path { get; set; }
-
- public LocationType LocationType { get; set; }
-
- public bool IsDownloadable { get; set; }
-
- public ExtraType ExtraType { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/Providers/ExtraSource.cs b/MediaBrowser.Controller/Providers/ExtraSource.cs
deleted file mode 100644
index 46b246fbc..000000000
--- a/MediaBrowser.Controller/Providers/ExtraSource.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace MediaBrowser.Controller.Providers
-{
- public enum ExtraSource
- {
- Local = 1,
- Metadata = 2,
- Remote = 3
- }
-}
diff --git a/MediaBrowser.Controller/Providers/ICustomMetadataProvider.cs b/MediaBrowser.Controller/Providers/ICustomMetadataProvider.cs
index 2d5a0b364..6b4c9feb5 100644
--- a/MediaBrowser.Controller/Providers/ICustomMetadataProvider.cs
+++ b/MediaBrowser.Controller/Providers/ICustomMetadataProvider.cs
@@ -13,7 +13,7 @@ namespace MediaBrowser.Controller.Providers
where TItemType : BaseItem
{
/// <summary>
- /// Fetches the asynchronous.
+ /// Fetches the metadata asynchronously.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="options">The options.</param>
diff --git a/MediaBrowser.Controller/Providers/IDirectoryService.cs b/MediaBrowser.Controller/Providers/IDirectoryService.cs
index 8059d2bd1..b304fc335 100644
--- a/MediaBrowser.Controller/Providers/IDirectoryService.cs
+++ b/MediaBrowser.Controller/Providers/IDirectoryService.cs
@@ -6,10 +6,13 @@ namespace MediaBrowser.Controller.Providers
public interface IDirectoryService
{
FileSystemMetadata[] GetFileSystemEntries(string path);
+
List<FileSystemMetadata> GetFiles(string path);
+
FileSystemMetadata GetFile(string path);
List<string> GetFilePaths(string path);
+
List<string> GetFilePaths(string path, bool clearCache);
}
}
diff --git a/MediaBrowser.Controller/Providers/IExtrasProvider.cs b/MediaBrowser.Controller/Providers/IExtrasProvider.cs
deleted file mode 100644
index fa31635cb..000000000
--- a/MediaBrowser.Controller/Providers/IExtrasProvider.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using MediaBrowser.Controller.Entities;
-
-namespace MediaBrowser.Controller.Providers
-{
- public interface IExtrasProvider
- {
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- string Name { get; }
-
- /// <summary>
- /// Supportses the specified item.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
- bool Supports(BaseItem item);
- }
-}
diff --git a/MediaBrowser.Controller/Providers/IForcedProvider.cs b/MediaBrowser.Controller/Providers/IForcedProvider.cs
index 35fa29d94..5ae4a56ef 100644
--- a/MediaBrowser.Controller/Providers/IForcedProvider.cs
+++ b/MediaBrowser.Controller/Providers/IForcedProvider.cs
@@ -1,7 +1,7 @@
namespace MediaBrowser.Controller.Providers
{
/// <summary>
- /// This is a marker interface that will cause a provider to run even if IsLocked=true
+ /// This is a marker interface that will cause a provider to run even if an item is locked from changes.
/// </summary>
public interface IForcedProvider
{
diff --git a/MediaBrowser.Controller/Providers/IImageEnhancer.cs b/MediaBrowser.Controller/Providers/IImageEnhancer.cs
deleted file mode 100644
index c27c00ca2..000000000
--- a/MediaBrowser.Controller/Providers/IImageEnhancer.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Drawing;
-using MediaBrowser.Model.Entities;
-
-namespace MediaBrowser.Controller.Providers
-{
- public interface IImageEnhancer
- {
- /// <summary>
- /// Return true only if the given image for the given item will be enhanced by this enhancer.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="imageType">Type of the image.</param>
- /// <returns><c>true</c> if this enhancer will enhance the supplied image for the supplied item, <c>false</c> otherwise</returns>
- bool Supports(BaseItem item, ImageType imageType);
-
- /// <summary>
- /// Gets the priority or order in which this enhancer should be run.
- /// </summary>
- /// <value>The priority.</value>
- MetadataProviderPriority Priority { get; }
-
- /// <summary>
- /// Return a key incorporating all configuration information related to this item
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="imageType">Type of the image.</param>
- /// <returns>Cache key relating to the current state of this item and configuration</returns>
- string GetConfigurationCacheKey(BaseItem item, ImageType imageType);
-
- /// <summary>
- /// Gets the size of the enhanced image.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="imageType">Type of the image.</param>
- /// <param name="imageIndex">Index of the image.</param>
- /// <param name="originalImageSize">Size of the original image.</param>
- /// <returns>ImageSize.</returns>
- ImageDimensions GetEnhancedImageSize(BaseItem item, ImageType imageType, int imageIndex, ImageDimensions originalImageSize);
-
- EnhancedImageInfo GetEnhancedImageInfo(BaseItem item, string inputFile, ImageType imageType, int imageIndex);
-
- /// <summary>
- /// Enhances the image async.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="inputFile">The input file.</param>
- /// <param name="outputFile">The output file.</param>
- /// <param name="imageType">Type of the image.</param>
- /// <param name="imageIndex">Index of the image.</param>
- /// <returns>Task{Image}.</returns>
- /// <exception cref="System.ArgumentNullException"></exception>
- Task EnhanceImageAsync(BaseItem item, string inputFile, string outputFile, ImageType imageType, int imageIndex);
- }
-
- public class EnhancedImageInfo
- {
- public bool RequiresTransparency { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/Providers/IImageProvider.cs b/MediaBrowser.Controller/Providers/IImageProvider.cs
index 2df3d5ff8..29ab323f8 100644
--- a/MediaBrowser.Controller/Providers/IImageProvider.cs
+++ b/MediaBrowser.Controller/Providers/IImageProvider.cs
@@ -3,7 +3,7 @@ using MediaBrowser.Controller.Entities;
namespace MediaBrowser.Controller.Providers
{
/// <summary>
- /// Interface IImageProvider
+ /// Interface IImageProvider.
/// </summary>
public interface IImageProvider
{
@@ -14,10 +14,10 @@ namespace MediaBrowser.Controller.Providers
string Name { get; }
/// <summary>
- /// Supportses the specified item.
+ /// Supports the specified item.
/// </summary>
/// <param name="item">The item.</param>
- /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
+ /// <returns><c>true</c> if the provider supports the item.</returns>
bool Supports(BaseItem item);
}
}
diff --git a/MediaBrowser.Controller/Providers/ILocalImageFileProvider.cs b/MediaBrowser.Controller/Providers/ILocalImageFileProvider.cs
deleted file mode 100644
index 72bc56390..000000000
--- a/MediaBrowser.Controller/Providers/ILocalImageFileProvider.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using System.Collections.Generic;
-using MediaBrowser.Controller.Entities;
-
-namespace MediaBrowser.Controller.Providers
-{
- public interface ILocalImageFileProvider : ILocalImageProvider
- {
- List<LocalImageInfo> GetImages(BaseItem item, IDirectoryService directoryService);
- }
-}
diff --git a/MediaBrowser.Controller/Providers/ILocalImageProvider.cs b/MediaBrowser.Controller/Providers/ILocalImageProvider.cs
index 09aaab3de..463c81376 100644
--- a/MediaBrowser.Controller/Providers/ILocalImageProvider.cs
+++ b/MediaBrowser.Controller/Providers/ILocalImageProvider.cs
@@ -1,9 +1,13 @@
+using System.Collections.Generic;
+using MediaBrowser.Controller.Entities;
+
namespace MediaBrowser.Controller.Providers
{
/// <summary>
- /// This is just a marker interface
+ /// This is just a marker interface.
/// </summary>
public interface ILocalImageProvider : IImageProvider
{
+ List<LocalImageInfo> GetImages(BaseItem item, IDirectoryService directoryService);
}
}
diff --git a/MediaBrowser.Controller/Providers/ILocalMetadataProvider.cs b/MediaBrowser.Controller/Providers/ILocalMetadataProvider.cs
index 2a2c379f6..44fb1b394 100644
--- a/MediaBrowser.Controller/Providers/ILocalMetadataProvider.cs
+++ b/MediaBrowser.Controller/Providers/ILocalMetadataProvider.cs
@@ -17,8 +17,9 @@ namespace MediaBrowser.Controller.Providers
/// <param name="info">The information.</param>
/// <param name="directoryService">The directory service.</param>
/// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{MetadataResult{`0}}.</returns>
- Task<MetadataResult<TItemType>> GetMetadata(ItemInfo info,
+ /// <returns>Task{MetadataResult{0}}.</returns>
+ Task<MetadataResult<TItemType>> GetMetadata(
+ ItemInfo info,
IDirectoryService directoryService,
CancellationToken cancellationToken);
}
diff --git a/MediaBrowser.Controller/Providers/IMetadataService.cs b/MediaBrowser.Controller/Providers/IMetadataService.cs
index 49f6a7830..21204e6d3 100644
--- a/MediaBrowser.Controller/Providers/IMetadataService.cs
+++ b/MediaBrowser.Controller/Providers/IMetadataService.cs
@@ -12,8 +12,9 @@ namespace MediaBrowser.Controller.Providers
/// Determines whether this instance can refresh the specified item.
/// </summary>
/// <param name="item">The item.</param>
- /// <returns><c>true</c> if this instance can refresh the specified item; otherwise, <c>false</c>.</returns>
+ /// <returns><c>true</c> if this instance can refresh the specified item.</returns>
bool CanRefresh(BaseItem item);
+
bool CanRefreshPrimary(Type type);
/// <summary>
diff --git a/MediaBrowser.Controller/Providers/IPreRefreshProvider.cs b/MediaBrowser.Controller/Providers/IPreRefreshProvider.cs
index 058010e1a..28da27ae7 100644
--- a/MediaBrowser.Controller/Providers/IPreRefreshProvider.cs
+++ b/MediaBrowser.Controller/Providers/IPreRefreshProvider.cs
@@ -2,6 +2,5 @@ namespace MediaBrowser.Controller.Providers
{
public interface IPreRefreshProvider : ICustomMetadataProvider
{
-
}
}
diff --git a/MediaBrowser.Controller/Providers/IProviderManager.cs b/MediaBrowser.Controller/Providers/IProviderManager.cs
index 925ace895..254b27460 100644
--- a/MediaBrowser.Controller/Providers/IProviderManager.cs
+++ b/MediaBrowser.Controller/Providers/IProviderManager.cs
@@ -14,7 +14,7 @@ using MediaBrowser.Model.Providers;
namespace MediaBrowser.Controller.Providers
{
/// <summary>
- /// Interface IProviderManager
+ /// Interface IProviderManager.
/// </summary>
public interface IProviderManager
{
@@ -159,13 +159,17 @@ namespace MediaBrowser.Controller.Providers
Dictionary<Guid, Guid> GetRefreshQueue();
void OnRefreshStart(BaseItem item);
+
void OnRefreshProgress(BaseItem item, double progress);
+
void OnRefreshComplete(BaseItem item);
double? GetRefreshProgress(Guid id);
event EventHandler<GenericEventArgs<BaseItem>> RefreshStarted;
+
event EventHandler<GenericEventArgs<BaseItem>> RefreshCompleted;
+
event EventHandler<GenericEventArgs<Tuple<BaseItem, double>>> RefreshProgress;
}
diff --git a/MediaBrowser.Controller/Providers/IRemoteImageProvider.cs b/MediaBrowser.Controller/Providers/IRemoteImageProvider.cs
index e56bba3e3..68a968f90 100644
--- a/MediaBrowser.Controller/Providers/IRemoteImageProvider.cs
+++ b/MediaBrowser.Controller/Providers/IRemoteImageProvider.cs
@@ -9,7 +9,7 @@ using MediaBrowser.Model.Providers;
namespace MediaBrowser.Controller.Providers
{
/// <summary>
- /// Interface IImageProvider
+ /// Interface IImageProvider.
/// </summary>
public interface IRemoteImageProvider : IImageProvider
{
diff --git a/MediaBrowser.Controller/Providers/ItemInfo.cs b/MediaBrowser.Controller/Providers/ItemInfo.cs
index f29a8aa70..d61153dfa 100644
--- a/MediaBrowser.Controller/Providers/ItemInfo.cs
+++ b/MediaBrowser.Controller/Providers/ItemInfo.cs
@@ -23,10 +23,15 @@ namespace MediaBrowser.Controller.Providers
}
public Type ItemType { get; set; }
+
public string Path { get; set; }
+
public string ContainingFolderPath { get; set; }
+
public VideoType VideoType { get; set; }
+
public bool IsInMixedFolder { get; set; }
+
public bool IsPlaceHolder { get; set; }
}
}
diff --git a/MediaBrowser.Controller/Providers/ItemLookupInfo.cs b/MediaBrowser.Controller/Providers/ItemLookupInfo.cs
index 0aaab9a94..4707b0c7f 100644
--- a/MediaBrowser.Controller/Providers/ItemLookupInfo.cs
+++ b/MediaBrowser.Controller/Providers/ItemLookupInfo.cs
@@ -11,29 +11,37 @@ namespace MediaBrowser.Controller.Providers
/// </summary>
/// <value>The name.</value>
public string Name { get; set; }
+
/// <summary>
/// Gets or sets the metadata language.
/// </summary>
/// <value>The metadata language.</value>
public string MetadataLanguage { get; set; }
+
/// <summary>
/// Gets or sets the metadata country code.
/// </summary>
/// <value>The metadata country code.</value>
public string MetadataCountryCode { get; set; }
+
/// <summary>
/// Gets or sets the provider ids.
/// </summary>
/// <value>The provider ids.</value>
public Dictionary<string, string> ProviderIds { get; set; }
+
/// <summary>
/// Gets or sets the year.
/// </summary>
/// <value>The year.</value>
public int? Year { get; set; }
+
public int? IndexNumber { get; set; }
+
public int? ParentIndexNumber { get; set; }
+
public DateTime? PremiereDate { get; set; }
+
public bool IsAutomated { get; set; }
public ItemLookupInfo()
diff --git a/MediaBrowser.Controller/Providers/LocalImageInfo.cs b/MediaBrowser.Controller/Providers/LocalImageInfo.cs
index 24cded79b..184281025 100644
--- a/MediaBrowser.Controller/Providers/LocalImageInfo.cs
+++ b/MediaBrowser.Controller/Providers/LocalImageInfo.cs
@@ -6,6 +6,7 @@ namespace MediaBrowser.Controller.Providers
public class LocalImageInfo
{
public FileSystemMetadata FileInfo { get; set; }
+
public ImageType Type { get; set; }
}
}
diff --git a/MediaBrowser.Controller/Providers/MetadataProviderPriority.cs b/MediaBrowser.Controller/Providers/MetadataProviderPriority.cs
deleted file mode 100644
index 0076bb972..000000000
--- a/MediaBrowser.Controller/Providers/MetadataProviderPriority.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-namespace MediaBrowser.Controller.Providers
-{
- /// <summary>
- /// Determines when a provider should execute, relative to others
- /// </summary>
- public enum MetadataProviderPriority
- {
- // Run this provider at the beginning
- /// <summary>
- /// The first
- /// </summary>
- First = 1,
-
- // Run this provider after all first priority providers
- /// <summary>
- /// The second
- /// </summary>
- Second = 2,
-
- // Run this provider after all second priority providers
- /// <summary>
- /// The third
- /// </summary>
- Third = 3,
-
- /// <summary>
- /// The fourth
- /// </summary>
- Fourth = 4,
-
- Fifth = 5,
-
- // Run this provider last
- /// <summary>
- /// The last
- /// </summary>
- Last = 999
- }
-}
diff --git a/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs b/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs
index b3eb8cdd1..0a473b80c 100644
--- a/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs
+++ b/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs
@@ -13,11 +13,13 @@ namespace MediaBrowser.Controller.Providers
public bool ReplaceAllMetadata { get; set; }
public MetadataRefreshMode MetadataRefreshMode { get; set; }
+
public RemoteSearchResult SearchResult { get; set; }
public string[] RefreshPaths { get; set; }
public bool ForceSave { get; set; }
+
public bool EnableRemoteContentProbe { get; set; }
public MetadataRefreshOptions(IDirectoryService directoryService)
diff --git a/MediaBrowser.Controller/Providers/MetadataResult.cs b/MediaBrowser.Controller/Providers/MetadataResult.cs
index ebff81b7f..59adaedfa 100644
--- a/MediaBrowser.Controller/Providers/MetadataResult.cs
+++ b/MediaBrowser.Controller/Providers/MetadataResult.cs
@@ -8,6 +8,7 @@ namespace MediaBrowser.Controller.Providers
public class MetadataResult<T>
{
public List<LocalImageInfo> Images { get; set; }
+
public List<UserItemData> UserDataList { get; set; }
public MetadataResult()
@@ -19,10 +20,15 @@ namespace MediaBrowser.Controller.Providers
public List<PersonInfo> People { get; set; }
public bool HasMetadata { get; set; }
+
public T Item { get; set; }
+
public string ResultLanguage { get; set; }
+
public string Provider { get; set; }
+
public bool QueriedById { get; set; }
+
public void AddPerson(PersonInfo p)
{
if (People == null)
diff --git a/MediaBrowser.Controller/Providers/MovieInfo.cs b/MediaBrowser.Controller/Providers/MovieInfo.cs
index c9a766fe7..5b2c3ed03 100644
--- a/MediaBrowser.Controller/Providers/MovieInfo.cs
+++ b/MediaBrowser.Controller/Providers/MovieInfo.cs
@@ -2,6 +2,5 @@ namespace MediaBrowser.Controller.Providers
{
public class MovieInfo : ItemLookupInfo
{
-
}
}
diff --git a/MediaBrowser.Controller/Providers/PersonLookupInfo.cs b/MediaBrowser.Controller/Providers/PersonLookupInfo.cs
index 3fa6e50b7..a6218c039 100644
--- a/MediaBrowser.Controller/Providers/PersonLookupInfo.cs
+++ b/MediaBrowser.Controller/Providers/PersonLookupInfo.cs
@@ -2,6 +2,5 @@ namespace MediaBrowser.Controller.Providers
{
public class PersonLookupInfo : ItemLookupInfo
{
-
}
}
diff --git a/MediaBrowser.Controller/Providers/RemoteSearchQuery.cs b/MediaBrowser.Controller/Providers/RemoteSearchQuery.cs
index 078125673..a2ac6c9ae 100644
--- a/MediaBrowser.Controller/Providers/RemoteSearchQuery.cs
+++ b/MediaBrowser.Controller/Providers/RemoteSearchQuery.cs
@@ -10,14 +10,14 @@ namespace MediaBrowser.Controller.Providers
public Guid ItemId { get; set; }
/// <summary>
- /// If set will only search within the given provider
+ /// Will only search within the given provider when set.
/// </summary>
public string SearchProviderName { get; set; }
/// <summary>
- /// Gets or sets a value indicating whether [include disabled providers].
+ /// Gets or sets a value indicating whether disabled providers should be included.
/// </summary>
- /// <value><c>true</c> if [include disabled providers]; otherwise, <c>false</c>.</value>
+ /// <value><c>true</c> if disabled providers should be included.</value>
public bool IncludeDisabledProviders { get; set; }
}
}
diff --git a/MediaBrowser.Controller/Session/SessionInfo.cs b/MediaBrowser.Controller/Session/SessionInfo.cs
index acda6a416..f1f10a3a3 100644
--- a/MediaBrowser.Controller/Session/SessionInfo.cs
+++ b/MediaBrowser.Controller/Session/SessionInfo.cs
@@ -107,6 +107,8 @@ namespace MediaBrowser.Controller.Session
public BaseItem FullNowPlayingItem { get; set; }
+ public BaseItemDto NowViewingItem { get; set; }
+
/// <summary>
/// Gets or sets the device id.
/// </summary>
@@ -127,12 +129,6 @@ namespace MediaBrowser.Controller.Session
public ISessionController[] SessionControllers { get; set; }
/// <summary>
- /// Gets or sets the application icon URL.
- /// </summary>
- /// <value>The application icon URL.</value>
- public string AppIconUrl { get; set; }
-
- /// <summary>
/// Gets or sets the supported commands.
/// </summary>
/// <value>The supported commands.</value>
@@ -245,11 +241,6 @@ namespace MediaBrowser.Controller.Session
SessionControllers = controllers.ToArray();
}
- public bool ContainsUser(string userId)
- {
- return ContainsUser(new Guid(userId));
- }
-
public bool ContainsUser(Guid userId)
{
if (UserId.Equals(userId))
@@ -259,7 +250,7 @@ namespace MediaBrowser.Controller.Session
foreach (var additionalUser in AdditionalUsers)
{
- if (userId.Equals(userId))
+ if (additionalUser.UserId.Equals(userId))
{
return true;
}
@@ -321,7 +312,7 @@ namespace MediaBrowser.Controller.Session
var newPositionTicks = positionTicks + ProgressIncrement;
var item = progressInfo.Item;
- long? runtimeTicks = item == null ? null : item.RunTimeTicks;
+ long? runtimeTicks = item?.RunTimeTicks;
// Don't report beyond the runtime
if (runtimeTicks.HasValue && newPositionTicks >= runtimeTicks.Value)
diff --git a/MediaBrowser.Controller/Sorting/AlphanumComparator.cs b/MediaBrowser.Controller/Sorting/AlphanumComparator.cs
new file mode 100644
index 000000000..de7f72d1c
--- /dev/null
+++ b/MediaBrowser.Controller/Sorting/AlphanumComparator.cs
@@ -0,0 +1,135 @@
+#nullable enable
+
+using System;
+using System.Collections.Generic;
+
+namespace MediaBrowser.Controller.Sorting
+{
+ public class AlphanumComparator : IComparer<string?>
+ {
+ public static int CompareValues(string? s1, string? s2)
+ {
+ if (s1 == null && s2 == null)
+ {
+ return 0;
+ }
+ else if (s1 == null)
+ {
+ return -1;
+ }
+ else if (s2 == null)
+ {
+ return 1;
+ }
+
+ int len1 = s1.Length;
+ int len2 = s2.Length;
+
+ // Early return for empty strings
+ if (len1 == 0 && len2 == 0)
+ {
+ return 0;
+ }
+ else if (len1 == 0)
+ {
+ return -1;
+ }
+ else if (len2 == 0)
+ {
+ return 1;
+ }
+
+ int pos1 = 0;
+ int pos2 = 0;
+
+ do
+ {
+ int start1 = pos1;
+ int start2 = pos2;
+
+ bool isNum1 = char.IsDigit(s1[pos1++]);
+ bool isNum2 = char.IsDigit(s2[pos2++]);
+
+ while (pos1 < len1 && char.IsDigit(s1[pos1]) == isNum1)
+ {
+ pos1++;
+ }
+
+ while (pos2 < len2 && char.IsDigit(s2[pos2]) == isNum2)
+ {
+ pos2++;
+ }
+
+ var span1 = s1.AsSpan(start1, pos1 - start1);
+ var span2 = s2.AsSpan(start2, pos2 - start2);
+
+ if (isNum1 && isNum2)
+ {
+ // Trim leading zeros so we can compare the length
+ // of the strings to find the largest number
+ span1 = span1.TrimStart('0');
+ span2 = span2.TrimStart('0');
+ var span1Len = span1.Length;
+ var span2Len = span2.Length;
+ if (span1Len < span2Len)
+ {
+ return -1;
+ }
+ else if (span1Len > span2Len)
+ {
+ return 1;
+ }
+ else if (span1Len >= 20) // Number is probably too big for a ulong
+ {
+ // Trim all the first digits that are the same
+ int i = 0;
+ while (i < span1Len && span1[i] == span2[i])
+ {
+ i++;
+ }
+
+ // If there are no more digits it's the same number
+ if (i == span1Len)
+ {
+ continue;
+ }
+
+ // Only need to compare the most significant digit
+ span1 = span1.Slice(i, 1);
+ span2 = span2.Slice(i, 1);
+ }
+
+ if (!ulong.TryParse(span1, out var num1)
+ || !ulong.TryParse(span2, out var num2))
+ {
+ return 0;
+ }
+ else if (num1 < num2)
+ {
+ return -1;
+ }
+ else if (num1 > num2)
+ {
+ return 1;
+ }
+ }
+ else
+ {
+ int result = span1.CompareTo(span2, StringComparison.InvariantCulture);
+ if (result != 0)
+ {
+ return result;
+ }
+ }
+ } while (pos1 < len1 && pos2 < len2);
+
+ return len1 - len2;
+ }
+
+ /// <inheritdoc />
+ 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 111f4f17f..f5ee574a2 100644
--- a/MediaBrowser.Controller/Sorting/SortExtensions.cs
+++ b/MediaBrowser.Controller/Sorting/SortExtensions.cs
@@ -7,137 +7,25 @@ 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, new AlphanumComparator());
+ return list.OrderBy(getName, _comparer);
}
public static IEnumerable<T> OrderByStringDescending<T>(this IEnumerable<T> list, Func<T, string> getName)
{
- return list.OrderByDescending(getName, new AlphanumComparator());
+ return list.OrderByDescending(getName, _comparer);
}
public static IOrderedEnumerable<T> ThenByString<T>(this IOrderedEnumerable<T> list, Func<T, string> getName)
{
- return list.ThenBy(getName, new AlphanumComparator());
+ return list.ThenBy(getName, _comparer);
}
public static IOrderedEnumerable<T> ThenByStringDescending<T>(this IOrderedEnumerable<T> list, Func<T, string> getName)
{
- return list.ThenByDescending(getName, new AlphanumComparator());
- }
-
- private class AlphanumComparator : IComparer<string>
- {
- private enum ChunkType { Alphanumeric, Numeric };
-
- private static bool InChunk(char ch, char otherCh)
- {
- var type = ChunkType.Alphanumeric;
-
- if (char.IsDigit(otherCh))
- {
- type = ChunkType.Numeric;
- }
-
- if ((type == ChunkType.Alphanumeric && char.IsDigit(ch))
- || (type == ChunkType.Numeric && !char.IsDigit(ch)))
- {
- return false;
- }
-
- return true;
- }
-
- public static int CompareValues(string s1, string s2)
- {
- if (s1 == null || s2 == null)
- {
- return 0;
- }
-
- int thisMarker = 0, thisNumericChunk = 0;
- int thatMarker = 0, thatNumericChunk = 0;
-
- while ((thisMarker < s1.Length) || (thatMarker < s2.Length))
- {
- if (thisMarker >= s1.Length)
- {
- return -1;
- }
- else if (thatMarker >= s2.Length)
- {
- return 1;
- }
- char thisCh = s1[thisMarker];
- char thatCh = s2[thatMarker];
-
- var thisChunk = new StringBuilder();
- var thatChunk = new StringBuilder();
-
- while ((thisMarker < s1.Length) && (thisChunk.Length == 0 || InChunk(thisCh, thisChunk[0])))
- {
- thisChunk.Append(thisCh);
- thisMarker++;
-
- if (thisMarker < s1.Length)
- {
- thisCh = s1[thisMarker];
- }
- }
-
- while ((thatMarker < s2.Length) && (thatChunk.Length == 0 || InChunk(thatCh, thatChunk[0])))
- {
- thatChunk.Append(thatCh);
- thatMarker++;
-
- if (thatMarker < s2.Length)
- {
- thatCh = s2[thatMarker];
- }
- }
-
- int result = 0;
- // If both chunks contain numeric characters, sort them numerically
- if (char.IsDigit(thisChunk[0]) && char.IsDigit(thatChunk[0]))
- {
- if (!int.TryParse(thisChunk.ToString(), out thisNumericChunk))
- {
- return 0;
- }
- if (!int.TryParse(thatChunk.ToString(), out thatNumericChunk))
- {
- return 0;
- }
-
- if (thisNumericChunk < thatNumericChunk)
- {
- result = -1;
- }
-
- if (thisNumericChunk > thatNumericChunk)
- {
- result = 1;
- }
- }
- else
- {
- result = thisChunk.ToString().CompareTo(thatChunk.ToString());
- }
-
- if (result != 0)
- {
- return result;
- }
- }
-
- return 0;
- }
-
- public int Compare(string x, string y)
- {
- return CompareValues(x, y);
- }
+ return list.ThenByDescending(getName, _comparer);
}
}
}
diff --git a/MediaBrowser.Controller/Sorting/SortHelper.cs b/MediaBrowser.Controller/Sorting/SortHelper.cs
deleted file mode 100644
index 05981d975..000000000
--- a/MediaBrowser.Controller/Sorting/SortHelper.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-namespace MediaBrowser.Controller.Sorting
-{
- public static class SortHelper
- {
- private enum ChunkType { Alphanumeric, Numeric };
-
- public static bool InChunk(char ch, char otherCh)
- {
- var type = ChunkType.Alphanumeric;
-
- if (char.IsDigit(otherCh))
- {
- type = ChunkType.Numeric;
- }
-
- if ((type == ChunkType.Alphanumeric && char.IsDigit(ch))
- || (type == ChunkType.Numeric && !char.IsDigit(ch)))
- {
- return false;
- }
-
- return true;
- }
- }
-}
diff --git a/MediaBrowser.LocalMetadata/Images/CollectionFolderImageProvider.cs b/MediaBrowser.LocalMetadata/Images/CollectionFolderImageProvider.cs
index 206e761bb..3bab1243c 100644
--- a/MediaBrowser.LocalMetadata/Images/CollectionFolderImageProvider.cs
+++ b/MediaBrowser.LocalMetadata/Images/CollectionFolderImageProvider.cs
@@ -5,7 +5,7 @@ using MediaBrowser.Model.IO;
namespace MediaBrowser.LocalMetadata.Images
{
- public class CollectionFolderLocalImageProvider : ILocalImageFileProvider, IHasOrder
+ public class CollectionFolderLocalImageProvider : ILocalImageProvider, IHasOrder
{
private readonly IFileSystem _fileSystem;
diff --git a/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs b/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs
index 443f3fbb5..2f4cca5ff 100644
--- a/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs
+++ b/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs
@@ -10,7 +10,7 @@ using MediaBrowser.Model.IO;
namespace MediaBrowser.LocalMetadata.Images
{
- public class EpisodeLocalLocalImageProvider : ILocalImageFileProvider, IHasOrder
+ public class EpisodeLocalLocalImageProvider : ILocalImageProvider, IHasOrder
{
private readonly IFileSystem _fileSystem;
diff --git a/MediaBrowser.LocalMetadata/Images/InternalMetadataFolderImageProvider.cs b/MediaBrowser.LocalMetadata/Images/InternalMetadataFolderImageProvider.cs
index 25a8ad596..795933ce9 100644
--- a/MediaBrowser.LocalMetadata/Images/InternalMetadataFolderImageProvider.cs
+++ b/MediaBrowser.LocalMetadata/Images/InternalMetadataFolderImageProvider.cs
@@ -9,7 +9,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.LocalMetadata.Images
{
- public class InternalMetadataFolderImageProvider : ILocalImageFileProvider, IHasOrder
+ public class InternalMetadataFolderImageProvider : ILocalImageProvider, IHasOrder
{
private readonly IServerConfigurationManager _config;
private readonly IFileSystem _fileSystem;
diff --git a/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs b/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs
index 7c330ad86..16807f5a4 100644
--- a/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs
+++ b/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs
@@ -13,7 +13,7 @@ using MediaBrowser.Model.IO;
namespace MediaBrowser.LocalMetadata.Images
{
- public class LocalImageProvider : ILocalImageFileProvider, IHasOrder
+ public class LocalImageProvider : ILocalImageProvider, IHasOrder
{
private readonly IFileSystem _fileSystem;
@@ -30,7 +30,7 @@ namespace MediaBrowser.LocalMetadata.Images
{
if (item.SupportsLocalMetadata)
{
- // Episode has it's own provider
+ // Episode has its own provider
if (item is Episode || item is Audio || item is Photo)
{
return false;
diff --git a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs
index 59c8f4da5..d4b98182f 100644
--- a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs
+++ b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs
@@ -9,7 +9,6 @@ using System.Xml;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.IO;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.LocalMetadata.Parsers
diff --git a/MediaBrowser.LocalMetadata/Providers/BoxSetXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/BoxSetXmlProvider.cs
index 2e303efab..b2e3bc9e2 100644
--- a/MediaBrowser.LocalMetadata/Providers/BoxSetXmlProvider.cs
+++ b/MediaBrowser.LocalMetadata/Providers/BoxSetXmlProvider.cs
@@ -16,7 +16,7 @@ namespace MediaBrowser.LocalMetadata.Providers
private readonly ILogger _logger;
private readonly IProviderManager _providerManager;
- public BoxSetXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager)
+ public BoxSetXmlProvider(IFileSystem fileSystem, ILogger<BoxSetXmlProvider> logger, IProviderManager providerManager)
: base(fileSystem)
{
_logger = logger;
diff --git a/MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs
index d111ae9ba..df8107bad 100644
--- a/MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs
+++ b/MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs
@@ -13,7 +13,10 @@ namespace MediaBrowser.LocalMetadata.Providers
private readonly ILogger _logger;
private readonly IProviderManager _providerManager;
- public PlaylistXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager)
+ public PlaylistXmlProvider(
+ IFileSystem fileSystem,
+ ILogger<PlaylistXmlProvider> logger,
+ IProviderManager providerManager)
: base(fileSystem)
{
_logger = logger;
diff --git a/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs
index bd727bcdf..ba1d850e3 100644
--- a/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs
+++ b/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs
@@ -1,5 +1,4 @@
using System;
-using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
@@ -93,7 +92,7 @@ namespace MediaBrowser.LocalMetadata.Savers
// On Windows, savint the file will fail if the file is hidden or readonly
FileSystem.SetAttributes(path, false, false);
- using (var filestream = FileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
+ using (var filestream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read))
{
stream.CopyTo(filestream);
}
diff --git a/MediaBrowser.LocalMetadata/Savers/BoxSetXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/BoxSetXmlSaver.cs
index ea939e33b..1dc09bf18 100644
--- a/MediaBrowser.LocalMetadata/Savers/BoxSetXmlSaver.cs
+++ b/MediaBrowser.LocalMetadata/Savers/BoxSetXmlSaver.cs
@@ -30,7 +30,7 @@ namespace MediaBrowser.LocalMetadata.Savers
return Path.Combine(item.Path, "collection.xml");
}
- public BoxSetXmlSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger)
+ public BoxSetXmlSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger<BoxSetXmlSaver> logger)
: base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger)
{
}
diff --git a/MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs
index 35a431fa4..bbb0a3501 100644
--- a/MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs
+++ b/MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs
@@ -11,6 +11,11 @@ namespace MediaBrowser.LocalMetadata.Savers
{
public class PlaylistXmlSaver : BaseXmlSaver
{
+ /// <summary>
+ /// The default file name to use when creating a new playlist.
+ /// </summary>
+ public const string DefaultPlaylistFilename = "playlist.xml";
+
public override bool IsEnabledFor(BaseItem item, ItemUpdateType updateType)
{
if (!item.SupportsLocalMetadata)
@@ -45,10 +50,10 @@ namespace MediaBrowser.LocalMetadata.Savers
return Path.ChangeExtension(itemPath, ".xml");
}
- return Path.Combine(path, "playlist.xml");
+ return Path.Combine(path, DefaultPlaylistFilename);
}
- public PlaylistXmlSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger)
+ public PlaylistXmlSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger<PlaylistXmlSaver> logger)
: base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger)
{
}
diff --git a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs
new file mode 100644
index 000000000..c530c9fd8
--- /dev/null
+++ b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs
@@ -0,0 +1,281 @@
+using System;
+using System.Diagnostics;
+using System.Collections.Concurrent;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Extensions;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.MediaEncoding;
+using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.MediaInfo;
+using Microsoft.Extensions.Logging;
+
+namespace MediaBrowser.MediaEncoding.Attachments
+{
+ public class AttachmentExtractor : IAttachmentExtractor, IDisposable
+ {
+ private readonly ILogger _logger;
+ private readonly IApplicationPaths _appPaths;
+ private readonly IFileSystem _fileSystem;
+ private readonly IMediaEncoder _mediaEncoder;
+ private readonly IMediaSourceManager _mediaSourceManager;
+
+ private readonly ConcurrentDictionary<string, SemaphoreSlim> _semaphoreLocks =
+ new ConcurrentDictionary<string, SemaphoreSlim>();
+
+ private bool _disposed = false;
+
+ public AttachmentExtractor(
+ ILogger<AttachmentExtractor> logger,
+ IApplicationPaths appPaths,
+ IFileSystem fileSystem,
+ IMediaEncoder mediaEncoder,
+ IMediaSourceManager mediaSourceManager)
+ {
+ _logger = logger;
+ _appPaths = appPaths;
+ _fileSystem = fileSystem;
+ _mediaEncoder = mediaEncoder;
+ _mediaSourceManager = mediaSourceManager;
+ }
+
+ /// <inheritdoc />
+ public async Task<(MediaAttachment attachment, Stream stream)> GetAttachment(BaseItem item, string mediaSourceId, int attachmentStreamIndex, CancellationToken cancellationToken)
+ {
+ if (item == null)
+ {
+ throw new ArgumentNullException(nameof(item));
+ }
+
+ if (string.IsNullOrWhiteSpace(mediaSourceId))
+ {
+ throw new ArgumentNullException(nameof(mediaSourceId));
+ }
+
+ var mediaSources = await _mediaSourceManager.GetPlaybackMediaSources(item, null, true, false, cancellationToken).ConfigureAwait(false);
+ var mediaSource = mediaSources
+ .FirstOrDefault(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase));
+ if (mediaSource == null)
+ {
+ throw new ResourceNotFoundException($"MediaSource {mediaSourceId} not found");
+ }
+
+ var mediaAttachment = mediaSource.MediaAttachments
+ .FirstOrDefault(i => i.Index == attachmentStreamIndex);
+ if (mediaAttachment == null)
+ {
+ throw new ResourceNotFoundException($"MediaSource {mediaSourceId} has no attachment with stream index {attachmentStreamIndex}");
+ }
+
+ var attachmentStream = await GetAttachmentStream(mediaSource, mediaAttachment, cancellationToken)
+ .ConfigureAwait(false);
+
+ return (mediaAttachment, attachmentStream);
+ }
+
+ private async Task<Stream> GetAttachmentStream(
+ MediaSourceInfo mediaSource,
+ MediaAttachment mediaAttachment,
+ CancellationToken cancellationToken)
+ {
+ var attachmentPath = await GetReadableFile(mediaSource.Path, mediaSource.Path, mediaSource.Protocol, mediaAttachment, cancellationToken).ConfigureAwait(false);
+ return File.OpenRead(attachmentPath);
+ }
+
+ private async Task<string> GetReadableFile(
+ string mediaPath,
+ string inputFile,
+ MediaProtocol protocol,
+ MediaAttachment mediaAttachment,
+ CancellationToken cancellationToken)
+ {
+ var outputPath = GetAttachmentCachePath(mediaPath, protocol, mediaAttachment.Index);
+ await ExtractAttachment(inputFile, protocol, mediaAttachment.Index, outputPath, cancellationToken)
+ .ConfigureAwait(false);
+
+ return outputPath;
+ }
+
+ private async Task ExtractAttachment(
+ string inputFile,
+ MediaProtocol protocol,
+ int attachmentStreamIndex,
+ string outputPath,
+ CancellationToken cancellationToken)
+ {
+ var semaphore = _semaphoreLocks.GetOrAdd(outputPath, key => new SemaphoreSlim(1, 1));
+
+ await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
+
+ try
+ {
+ if (!File.Exists(outputPath))
+ {
+ await ExtractAttachmentInternal(
+ _mediaEncoder.GetInputArgument(new[] { inputFile }, protocol),
+ attachmentStreamIndex,
+ outputPath,
+ cancellationToken).ConfigureAwait(false);
+ }
+ }
+ finally
+ {
+ semaphore.Release();
+ }
+ }
+
+ private async Task ExtractAttachmentInternal(
+ string inputPath,
+ int attachmentStreamIndex,
+ string outputPath,
+ CancellationToken cancellationToken)
+ {
+ if (string.IsNullOrEmpty(inputPath))
+ {
+ throw new ArgumentNullException(nameof(inputPath));
+ }
+
+ if (string.IsNullOrEmpty(outputPath))
+ {
+ throw new ArgumentNullException(nameof(outputPath));
+ }
+
+ Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
+
+ var processArgs = string.Format(
+ CultureInfo.InvariantCulture,
+ "-dump_attachment:{1} {2} -i {0} -t 0 -f null null",
+ inputPath,
+ attachmentStreamIndex,
+ outputPath);
+ var startInfo = new ProcessStartInfo
+ {
+ Arguments = processArgs,
+ FileName = _mediaEncoder.EncoderPath,
+ UseShellExecute = false,
+ CreateNoWindow = true,
+ WindowStyle = ProcessWindowStyle.Hidden,
+ ErrorDialog = false
+ };
+ var process = new Process
+ {
+ StartInfo = startInfo
+ };
+
+ _logger.LogInformation("{File} {Arguments}", process.StartInfo.FileName, process.StartInfo.Arguments);
+
+ process.Start();
+
+ var processTcs = new TaskCompletionSource<bool>();
+ process.EnableRaisingEvents = true;
+ process.Exited += (sender, args) => processTcs.TrySetResult(true);
+ var unregister = cancellationToken.Register(() => processTcs.TrySetResult(process.HasExited));
+ var ranToCompletion = await processTcs.Task.ConfigureAwait(false);
+ unregister.Dispose();
+
+ if (!ranToCompletion)
+ {
+ try
+ {
+ _logger.LogWarning("Killing ffmpeg attachment extraction process");
+ process.Kill();
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error killing attachment extraction process");
+ }
+ }
+
+ var exitCode = ranToCompletion ? process.ExitCode : -1;
+
+ process.Dispose();
+
+ var failed = false;
+
+ if (exitCode != 0)
+ {
+ failed = true;
+
+ _logger.LogWarning("Deleting extracted attachment {Path} due to failure: {ExitCode}", outputPath, exitCode);
+ try
+ {
+ if (File.Exists(outputPath))
+ {
+ _fileSystem.DeleteFile(outputPath);
+ }
+ }
+ catch (IOException ex)
+ {
+ _logger.LogError(ex, "Error deleting extracted attachment {Path}", outputPath);
+ }
+ }
+ else if (!File.Exists(outputPath))
+ {
+ failed = true;
+ }
+
+ if (failed)
+ {
+ var msg = $"ffmpeg attachment extraction failed for {inputPath} to {outputPath}";
+
+ _logger.LogError(msg);
+
+ throw new InvalidOperationException(msg);
+ }
+ else
+ {
+ _logger.LogInformation("ffmpeg attachment extraction completed for {Path} to {Path}", inputPath, outputPath);
+ }
+ }
+
+ private string GetAttachmentCachePath(string mediaPath, MediaProtocol protocol, int attachmentStreamIndex)
+ {
+ string filename;
+ if (protocol == MediaProtocol.File)
+ {
+ var date = _fileSystem.GetLastWriteTimeUtc(mediaPath);
+ filename = (mediaPath + attachmentStreamIndex.ToString(CultureInfo.InvariantCulture) + "_" + date.Ticks.ToString(CultureInfo.InvariantCulture)).GetMD5().ToString("D");
+ }
+ else
+ {
+ filename = (mediaPath + attachmentStreamIndex.ToString(CultureInfo.InvariantCulture)).GetMD5().ToString("D");
+ }
+
+ var prefix = filename.Substring(0, 1);
+ return Path.Combine(_appPaths.DataPath, "attachments", prefix, filename);
+ }
+
+ /// <inheritdoc />
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ /// <summary>
+ /// Releases unmanaged and - optionally - managed resources.
+ /// </summary>
+ /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
+ protected virtual void Dispose(bool disposing)
+ {
+ if (_disposed)
+ {
+ return;
+ }
+
+ if (disposing)
+ {
+
+ }
+
+ _disposed = true;
+ }
+ }
+}
diff --git a/MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs b/MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs
index 91c8b2792..e040286ab 100644
--- a/MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs
+++ b/MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs
@@ -1,16 +1,27 @@
using System;
-using System.Collections.Generic;
using System.Linq;
using BDInfo.IO;
using MediaBrowser.Model.IO;
namespace MediaBrowser.MediaEncoding.BdInfo
{
- class BdInfoDirectoryInfo : BDInfo.IO.IDirectoryInfo
+ class BdInfoDirectoryInfo : IDirectoryInfo
{
- IFileSystem _fileSystem = null;
+ private readonly IFileSystem _fileSystem = null;
- FileSystemMetadata _impl = null;
+ private readonly FileSystemMetadata _impl = null;
+
+ public BdInfoDirectoryInfo(IFileSystem fileSystem, string path)
+ {
+ _fileSystem = fileSystem;
+ _impl = _fileSystem.GetDirectoryInfo(path);
+ }
+
+ private BdInfoDirectoryInfo(IFileSystem fileSystem, FileSystemMetadata impl)
+ {
+ _fileSystem = fileSystem;
+ _impl = impl;
+ }
public string Name => _impl.Name;
@@ -25,22 +36,11 @@ namespace MediaBrowser.MediaEncoding.BdInfo
{
return new BdInfoDirectoryInfo(_fileSystem, parentFolder);
}
+
return null;
}
}
- public BdInfoDirectoryInfo(IFileSystem fileSystem, string path)
- {
- _fileSystem = fileSystem;
- _impl = _fileSystem.GetDirectoryInfo(path);
- }
-
- private BdInfoDirectoryInfo(IFileSystem fileSystem, FileSystemMetadata impl)
- {
- _fileSystem = fileSystem;
- _impl = impl;
- }
-
public IDirectoryInfo[] GetDirectories()
{
return Array.ConvertAll(_fileSystem.GetDirectories(_impl.FullName).ToArray(),
@@ -50,20 +50,20 @@ namespace MediaBrowser.MediaEncoding.BdInfo
public IFileInfo[] GetFiles()
{
return Array.ConvertAll(_fileSystem.GetFiles(_impl.FullName).ToArray(),
- x => new BdInfoFileInfo(_fileSystem, x));
+ x => new BdInfoFileInfo(x));
}
public IFileInfo[] GetFiles(string searchPattern)
{
return Array.ConvertAll(_fileSystem.GetFiles(_impl.FullName, new[] { searchPattern }, false, false).ToArray(),
- x => new BdInfoFileInfo(_fileSystem, x));
+ x => new BdInfoFileInfo(x));
}
public IFileInfo[] GetFiles(string searchPattern, System.IO.SearchOption searchOption)
{
return Array.ConvertAll(_fileSystem.GetFiles(_impl.FullName, new[] { searchPattern }, false,
searchOption.HasFlag(System.IO.SearchOption.AllDirectories)).ToArray(),
- x => new BdInfoFileInfo(_fileSystem, x));
+ x => new BdInfoFileInfo(x));
}
public static IDirectoryInfo FromFileSystemPath(Model.IO.IFileSystem fs, string path)
diff --git a/MediaBrowser.MediaEncoding/BdInfo/BdInfoFileInfo.cs b/MediaBrowser.MediaEncoding/BdInfo/BdInfoFileInfo.cs
index de9d7cb78..a6ff4f767 100644
--- a/MediaBrowser.MediaEncoding/BdInfo/BdInfoFileInfo.cs
+++ b/MediaBrowser.MediaEncoding/BdInfo/BdInfoFileInfo.cs
@@ -1,11 +1,10 @@
+using System.IO;
using MediaBrowser.Model.IO;
namespace MediaBrowser.MediaEncoding.BdInfo
{
class BdInfoFileInfo : BDInfo.IO.IFileInfo
{
- IFileSystem _fileSystem = null;
-
FileSystemMetadata _impl = null;
public string Name => _impl.Name;
@@ -18,18 +17,17 @@ namespace MediaBrowser.MediaEncoding.BdInfo
public bool IsDir => _impl.IsDirectory;
- public BdInfoFileInfo(IFileSystem fileSystem, FileSystemMetadata impl)
+ public BdInfoFileInfo(FileSystemMetadata impl)
{
- _fileSystem = fileSystem;
_impl = impl;
}
public System.IO.Stream OpenRead()
{
- return _fileSystem.GetFileStream(FullName,
- FileOpenMode.Open,
- FileAccessMode.Read,
- FileShareMode.Read);
+ return new FileStream(FullName,
+ FileMode.Open,
+ FileAccess.Read,
+ FileShare.Read);
}
public System.IO.StreamReader OpenText()
diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
index 1feca0ec9..f5decdc0d 100644
--- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
@@ -55,7 +55,9 @@ namespace MediaBrowser.MediaEncoding.Encoder
"h264_vaapi",
"hevc_vaapi",
"h264_v4l2m2m",
- "ac3"
+ "ac3",
+ "h264_amf",
+ "hevc_amf"
};
// Try and use the individual library versions to determine a FFmpeg version
diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
index 6bcd6cd46..4123f0203 100644
--- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
@@ -397,7 +397,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
try
{
result = await JsonSerializer.DeserializeAsync<InternalMediaInfoResult>(
- process.StandardOutput.BaseStream).ConfigureAwait(false);
+ process.StandardOutput.BaseStream,
+ cancellationToken: cancellationToken).ConfigureAwait(false);
}
catch
{
@@ -406,24 +407,24 @@ namespace MediaBrowser.MediaEncoding.Encoder
throw;
}
- if (result == null || (result.streams == null && result.format == null))
+ if (result == null || (result.Streams == null && result.Format == null))
{
throw new Exception("ffprobe failed - streams and format are both null.");
}
- if (result.streams != null)
+ if (result.Streams != null)
{
// Normalize aspect ratio if invalid
- foreach (var stream in result.streams)
+ foreach (var stream in result.Streams)
{
- if (string.Equals(stream.display_aspect_ratio, "0:1", StringComparison.OrdinalIgnoreCase))
+ if (string.Equals(stream.DisplayAspectRatio, "0:1", StringComparison.OrdinalIgnoreCase))
{
- stream.display_aspect_ratio = string.Empty;
+ stream.DisplayAspectRatio = string.Empty;
}
- if (string.Equals(stream.sample_aspect_ratio, "0:1", StringComparison.OrdinalIgnoreCase))
+ if (string.Equals(stream.SampleAspectRatio, "0:1", StringComparison.OrdinalIgnoreCase))
{
- stream.sample_aspect_ratio = string.Empty;
+ stream.SampleAspectRatio = string.Empty;
}
}
}
@@ -778,6 +779,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
_runningProcesses.Add(process);
}
}
+
private void StopProcess(ProcessWrapper process, int waitTimeMs)
{
try
@@ -786,18 +788,16 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
return;
}
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error in WaitForExit");
- }
- try
- {
_logger.LogInformation("Killing ffmpeg process");
process.Process.Kill();
}
+ catch (InvalidOperationException)
+ {
+ // The process has already exited or
+ // there is no process associated with this Process object.
+ }
catch (Exception ex)
{
_logger.LogError(ex, "Error killing process");
@@ -849,14 +849,92 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
}
+ /// <inheritdoc />
public Task ConvertImage(string inputPath, string outputPath)
{
throw new NotImplementedException();
}
+ /// <inheritdoc />
public IEnumerable<string> GetPrimaryPlaylistVobFiles(string path, IIsoMount isoMount, uint? titleNumber)
{
- throw new NotImplementedException();
+ // min size 300 mb
+ const long MinPlayableSize = 314572800;
+
+ var root = isoMount != null ? isoMount.MountedPath : path;
+
+ // Try to eliminate menus and intros by skipping all files at the front of the list that are less than the minimum size
+ // Once we reach a file that is at least the minimum, return all subsequent ones
+ var allVobs = _fileSystem.GetFiles(root, true)
+ .Where(file => string.Equals(file.Extension, ".vob", StringComparison.OrdinalIgnoreCase))
+ .OrderBy(i => i.FullName)
+ .ToList();
+
+ // If we didn't find any satisfying the min length, just take them all
+ if (allVobs.Count == 0)
+ {
+ _logger.LogWarning("No vobs found in dvd structure.");
+ return Enumerable.Empty<string>();
+ }
+
+ if (titleNumber.HasValue)
+ {
+ var prefix = string.Format(
+ CultureInfo.InvariantCulture,
+ titleNumber.Value >= 10 ? "VTS_{0}_" : "VTS_0{0}_",
+ titleNumber.Value);
+ var vobs = allVobs.Where(i => i.Name.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)).ToList();
+
+ if (vobs.Count > 0)
+ {
+ var minSizeVobs = vobs
+ .SkipWhile(f => f.Length < MinPlayableSize)
+ .ToList();
+
+ return minSizeVobs.Count == 0 ? vobs.Select(i => i.FullName) : minSizeVobs.Select(i => i.FullName);
+ }
+
+ _logger.LogWarning("Could not determine vob file list for {0} using DvdLib. Will scan using file sizes.", path);
+ }
+
+ var files = allVobs
+ .SkipWhile(f => f.Length < MinPlayableSize)
+ .ToList();
+
+ // If we didn't find any satisfying the min length, just take them all
+ if (files.Count == 0)
+ {
+ _logger.LogWarning("Vob size filter resulted in zero matches. Taking all vobs.");
+ files = allVobs;
+ }
+
+ // Assuming they're named "vts_05_01", take all files whose second part matches that of the first file
+ if (files.Count > 0)
+ {
+ var parts = _fileSystem.GetFileNameWithoutExtension(files[0]).Split('_');
+
+ if (parts.Length == 3)
+ {
+ var title = parts[1];
+
+ files = files.TakeWhile(f =>
+ {
+ var fileParts = _fileSystem.GetFileNameWithoutExtension(f).Split('_');
+
+ return fileParts.Length == 3 && string.Equals(title, fileParts[1], StringComparison.OrdinalIgnoreCase);
+
+ }).ToList();
+
+ // If this resulted in not getting any vobs, just take them all
+ if (files.Count == 0)
+ {
+ _logger.LogWarning("Vob filename filter resulted in zero matches. Taking all vobs.");
+ files = allVobs;
+ }
+ }
+ }
+
+ return files.Select(i => i.FullName);
}
public bool CanExtractSubtitles(string codec)
diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
index 783457bda..a312dcd70 100644
--- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
+++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
@@ -18,8 +18,8 @@
<ItemGroup>
<PackageReference Include="BDInfo" Version="0.7.6.1" />
- <PackageReference Include="System.Text.Encoding.CodePages" Version="4.6.0" />
- <PackageReference Include="UTF.Unknown" Version="2.2.0" />
+ <PackageReference Include="System.Text.Encoding.CodePages" Version="4.7.0" />
+ <PackageReference Include="UTF.Unknown" Version="2.3.0" />
</ItemGroup>
</Project>
diff --git a/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs b/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs
index e4eabaf38..78dc7b607 100644
--- a/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs
+++ b/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs
@@ -16,24 +16,19 @@ namespace MediaBrowser.MediaEncoding.Probing
throw new ArgumentNullException(nameof(result));
}
- if (result.format != null && result.format.tags != null)
+ if (result.Format != null && result.Format.Tags != null)
{
- result.format.tags = ConvertDictionaryToCaseInSensitive(result.format.tags);
+ result.Format.Tags = ConvertDictionaryToCaseInsensitive(result.Format.Tags);
}
- if (result.streams != null)
+ if (result.Streams != null)
{
// Convert all dictionaries to case insensitive
- foreach (var stream in result.streams)
+ foreach (var stream in result.Streams)
{
- if (stream.tags != null)
+ if (stream.Tags != null)
{
- stream.tags = ConvertDictionaryToCaseInSensitive(stream.tags);
- }
-
- if (stream.disposition != null)
- {
- stream.disposition = ConvertDictionaryToCaseInSensitive(stream.disposition);
+ stream.Tags = ConvertDictionaryToCaseInsensitive(stream.Tags);
}
}
}
@@ -45,7 +40,7 @@ namespace MediaBrowser.MediaEncoding.Probing
/// <param name="tags">The tags.</param>
/// <param name="key">The key.</param>
/// <returns>System.String.</returns>
- public static string GetDictionaryValue(Dictionary<string, string> tags, string key)
+ public static string GetDictionaryValue(IReadOnlyDictionary<string, string> tags, string key)
{
if (tags == null)
{
@@ -103,7 +98,7 @@ namespace MediaBrowser.MediaEncoding.Probing
/// </summary>
/// <param name="dict">The dict.</param>
/// <returns>Dictionary{System.StringSystem.String}.</returns>
- private static Dictionary<string, string> ConvertDictionaryToCaseInSensitive(Dictionary<string, string> dict)
+ private static Dictionary<string, string> ConvertDictionaryToCaseInsensitive(IReadOnlyDictionary<string, string> dict)
{
return new Dictionary<string, string>(dict, StringComparer.OrdinalIgnoreCase);
}
diff --git a/MediaBrowser.MediaEncoding/Probing/InternalMediaInfoResult.cs b/MediaBrowser.MediaEncoding/Probing/InternalMediaInfoResult.cs
index cc9d27608..0e319c1a8 100644
--- a/MediaBrowser.MediaEncoding/Probing/InternalMediaInfoResult.cs
+++ b/MediaBrowser.MediaEncoding/Probing/InternalMediaInfoResult.cs
@@ -1,9 +1,10 @@
using System.Collections.Generic;
+using System.Text.Json.Serialization;
namespace MediaBrowser.MediaEncoding.Probing
{
/// <summary>
- /// Class MediaInfoResult
+ /// Class MediaInfoResult.
/// </summary>
public class InternalMediaInfoResult
{
@@ -11,331 +12,21 @@ namespace MediaBrowser.MediaEncoding.Probing
/// Gets or sets the streams.
/// </summary>
/// <value>The streams.</value>
- public MediaStreamInfo[] streams { get; set; }
+ [JsonPropertyName("streams")]
+ public IReadOnlyList<MediaStreamInfo> Streams { get; set; }
/// <summary>
/// Gets or sets the format.
/// </summary>
/// <value>The format.</value>
- public MediaFormatInfo format { get; set; }
+ [JsonPropertyName("format")]
+ public MediaFormatInfo Format { get; set; }
/// <summary>
/// Gets or sets the chapters.
/// </summary>
/// <value>The chapters.</value>
- public MediaChapter[] Chapters { get; set; }
- }
-
- public class MediaChapter
- {
- public int id { get; set; }
- public string time_base { get; set; }
- public long start { get; set; }
- public string start_time { get; set; }
- public long end { get; set; }
- public string end_time { get; set; }
- public Dictionary<string, string> tags { get; set; }
- }
-
- /// <summary>
- /// Represents a stream within the output
- /// </summary>
- public class MediaStreamInfo
- {
- /// <summary>
- /// Gets or sets the index.
- /// </summary>
- /// <value>The index.</value>
- public int index { get; set; }
-
- /// <summary>
- /// Gets or sets the profile.
- /// </summary>
- /// <value>The profile.</value>
- public string profile { get; set; }
-
- /// <summary>
- /// Gets or sets the codec_name.
- /// </summary>
- /// <value>The codec_name.</value>
- public string codec_name { get; set; }
-
- /// <summary>
- /// Gets or sets the codec_long_name.
- /// </summary>
- /// <value>The codec_long_name.</value>
- public string codec_long_name { get; set; }
-
- /// <summary>
- /// Gets or sets the codec_type.
- /// </summary>
- /// <value>The codec_type.</value>
- public string codec_type { get; set; }
-
- /// <summary>
- /// Gets or sets the sample_rate.
- /// </summary>
- /// <value>The sample_rate.</value>
- public string sample_rate { get; set; }
-
- /// <summary>
- /// Gets or sets the channels.
- /// </summary>
- /// <value>The channels.</value>
- public int channels { get; set; }
-
- /// <summary>
- /// Gets or sets the channel_layout.
- /// </summary>
- /// <value>The channel_layout.</value>
- public string channel_layout { get; set; }
-
- /// <summary>
- /// Gets or sets the avg_frame_rate.
- /// </summary>
- /// <value>The avg_frame_rate.</value>
- public string avg_frame_rate { get; set; }
-
- /// <summary>
- /// Gets or sets the duration.
- /// </summary>
- /// <value>The duration.</value>
- public string duration { get; set; }
-
- /// <summary>
- /// Gets or sets the bit_rate.
- /// </summary>
- /// <value>The bit_rate.</value>
- public string bit_rate { get; set; }
-
- /// <summary>
- /// Gets or sets the width.
- /// </summary>
- /// <value>The width.</value>
- public int width { get; set; }
-
- /// <summary>
- /// Gets or sets the refs.
- /// </summary>
- /// <value>The refs.</value>
- public int refs { get; set; }
-
- /// <summary>
- /// Gets or sets the height.
- /// </summary>
- /// <value>The height.</value>
- public int height { get; set; }
-
- /// <summary>
- /// Gets or sets the display_aspect_ratio.
- /// </summary>
- /// <value>The display_aspect_ratio.</value>
- public string display_aspect_ratio { get; set; }
-
- /// <summary>
- /// Gets or sets the tags.
- /// </summary>
- /// <value>The tags.</value>
- public Dictionary<string, string> tags { get; set; }
-
- /// <summary>
- /// Gets or sets the bits_per_sample.
- /// </summary>
- /// <value>The bits_per_sample.</value>
- public int bits_per_sample { get; set; }
-
- /// <summary>
- /// Gets or sets the bits_per_raw_sample.
- /// </summary>
- /// <value>The bits_per_raw_sample.</value>
- public int bits_per_raw_sample { get; set; }
-
- /// <summary>
- /// Gets or sets the r_frame_rate.
- /// </summary>
- /// <value>The r_frame_rate.</value>
- public string r_frame_rate { get; set; }
-
- /// <summary>
- /// Gets or sets the has_b_frames.
- /// </summary>
- /// <value>The has_b_frames.</value>
- public int has_b_frames { get; set; }
-
- /// <summary>
- /// Gets or sets the sample_aspect_ratio.
- /// </summary>
- /// <value>The sample_aspect_ratio.</value>
- public string sample_aspect_ratio { get; set; }
-
- /// <summary>
- /// Gets or sets the pix_fmt.
- /// </summary>
- /// <value>The pix_fmt.</value>
- public string pix_fmt { get; set; }
-
- /// <summary>
- /// Gets or sets the level.
- /// </summary>
- /// <value>The level.</value>
- public int level { get; set; }
-
- /// <summary>
- /// Gets or sets the time_base.
- /// </summary>
- /// <value>The time_base.</value>
- public string time_base { get; set; }
-
- /// <summary>
- /// Gets or sets the start_time.
- /// </summary>
- /// <value>The start_time.</value>
- public string start_time { get; set; }
-
- /// <summary>
- /// Gets or sets the codec_time_base.
- /// </summary>
- /// <value>The codec_time_base.</value>
- public string codec_time_base { get; set; }
-
- /// <summary>
- /// Gets or sets the codec_tag.
- /// </summary>
- /// <value>The codec_tag.</value>
- public string codec_tag { get; set; }
-
- /// <summary>
- /// Gets or sets the codec_tag_string.
- /// </summary>
- /// <value>The codec_tag_string.</value>
- public string codec_tag_string { get; set; }
-
- /// <summary>
- /// Gets or sets the sample_fmt.
- /// </summary>
- /// <value>The sample_fmt.</value>
- public string sample_fmt { get; set; }
-
- /// <summary>
- /// Gets or sets the dmix_mode.
- /// </summary>
- /// <value>The dmix_mode.</value>
- public string dmix_mode { get; set; }
-
- /// <summary>
- /// Gets or sets the start_pts.
- /// </summary>
- /// <value>The start_pts.</value>
- public string start_pts { get; set; }
-
- /// <summary>
- /// Gets or sets the is_avc.
- /// </summary>
- /// <value>The is_avc.</value>
- public string is_avc { get; set; }
-
- /// <summary>
- /// Gets or sets the nal_length_size.
- /// </summary>
- /// <value>The nal_length_size.</value>
- public string nal_length_size { get; set; }
-
- /// <summary>
- /// Gets or sets the ltrt_cmixlev.
- /// </summary>
- /// <value>The ltrt_cmixlev.</value>
- public string ltrt_cmixlev { get; set; }
-
- /// <summary>
- /// Gets or sets the ltrt_surmixlev.
- /// </summary>
- /// <value>The ltrt_surmixlev.</value>
- public string ltrt_surmixlev { get; set; }
-
- /// <summary>
- /// Gets or sets the loro_cmixlev.
- /// </summary>
- /// <value>The loro_cmixlev.</value>
- public string loro_cmixlev { get; set; }
-
- /// <summary>
- /// Gets or sets the loro_surmixlev.
- /// </summary>
- /// <value>The loro_surmixlev.</value>
- public string loro_surmixlev { get; set; }
-
- public string field_order { get; set; }
-
- /// <summary>
- /// Gets or sets the disposition.
- /// </summary>
- /// <value>The disposition.</value>
- public Dictionary<string, string> disposition { get; set; }
- }
-
- /// <summary>
- /// Class MediaFormat
- /// </summary>
- public class MediaFormatInfo
- {
- /// <summary>
- /// Gets or sets the filename.
- /// </summary>
- /// <value>The filename.</value>
- public string filename { get; set; }
-
- /// <summary>
- /// Gets or sets the nb_streams.
- /// </summary>
- /// <value>The nb_streams.</value>
- public int nb_streams { get; set; }
-
- /// <summary>
- /// Gets or sets the format_name.
- /// </summary>
- /// <value>The format_name.</value>
- public string format_name { get; set; }
-
- /// <summary>
- /// Gets or sets the format_long_name.
- /// </summary>
- /// <value>The format_long_name.</value>
- public string format_long_name { get; set; }
-
- /// <summary>
- /// Gets or sets the start_time.
- /// </summary>
- /// <value>The start_time.</value>
- public string start_time { get; set; }
-
- /// <summary>
- /// Gets or sets the duration.
- /// </summary>
- /// <value>The duration.</value>
- public string duration { get; set; }
-
- /// <summary>
- /// Gets or sets the size.
- /// </summary>
- /// <value>The size.</value>
- public string size { get; set; }
-
- /// <summary>
- /// Gets or sets the bit_rate.
- /// </summary>
- /// <value>The bit_rate.</value>
- public string bit_rate { get; set; }
-
- /// <summary>
- /// Gets or sets the probe_score.
- /// </summary>
- /// <value>The probe_score.</value>
- public int probe_score { get; set; }
-
- /// <summary>
- /// Gets or sets the tags.
- /// </summary>
- /// <value>The tags.</value>
- public Dictionary<string, string> tags { get; set; }
+ [JsonPropertyName("chapters")]
+ public IReadOnlyList<MediaChapter> Chapters { get; set; }
}
}
diff --git a/MediaBrowser.MediaEncoding/Probing/MediaChapter.cs b/MediaBrowser.MediaEncoding/Probing/MediaChapter.cs
new file mode 100644
index 000000000..6a45ccf49
--- /dev/null
+++ b/MediaBrowser.MediaEncoding/Probing/MediaChapter.cs
@@ -0,0 +1,32 @@
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+
+namespace MediaBrowser.MediaEncoding.Probing
+{
+ /// <summary>
+ /// Class MediaChapter.
+ /// </summary>
+ public class MediaChapter
+ {
+ [JsonPropertyName("id")]
+ public int Id { get; set; }
+
+ [JsonPropertyName("time_base")]
+ public string TimeBase { get; set; }
+
+ [JsonPropertyName("start")]
+ public long Start { get; set; }
+
+ [JsonPropertyName("start_time")]
+ public string StartTime { get; set; }
+
+ [JsonPropertyName("end")]
+ public long End { get; set; }
+
+ [JsonPropertyName("end_time")]
+ public string EndTime { get; set; }
+
+ [JsonPropertyName("tags")]
+ public IReadOnlyDictionary<string, string> Tags { get; set; }
+ }
+}
diff --git a/MediaBrowser.MediaEncoding/Probing/MediaFormatInfo.cs b/MediaBrowser.MediaEncoding/Probing/MediaFormatInfo.cs
new file mode 100644
index 000000000..8af122ef9
--- /dev/null
+++ b/MediaBrowser.MediaEncoding/Probing/MediaFormatInfo.cs
@@ -0,0 +1,81 @@
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+
+namespace MediaBrowser.MediaEncoding.Probing
+{
+ /// <summary>
+ /// Class MediaFormat.
+ /// </summary>
+ public class MediaFormatInfo
+ {
+ /// <summary>
+ /// Gets or sets the filename.
+ /// </summary>
+ /// <value>The filename.</value>
+ [JsonPropertyName("filename")]
+ public string FileName { get; set; }
+
+ /// <summary>
+ /// Gets or sets the nb_streams.
+ /// </summary>
+ /// <value>The nb_streams.</value>
+ [JsonPropertyName("nb_streams")]
+ public int NbStreams { get; set; }
+
+ /// <summary>
+ /// Gets or sets the format_name.
+ /// </summary>
+ /// <value>The format_name.</value>
+ [JsonPropertyName("format_name")]
+ public string FormatName { get; set; }
+
+ /// <summary>
+ /// Gets or sets the format_long_name.
+ /// </summary>
+ /// <value>The format_long_name.</value>
+ [JsonPropertyName("format_long_name")]
+ public string FormatLongName { get; set; }
+
+ /// <summary>
+ /// Gets or sets the start_time.
+ /// </summary>
+ /// <value>The start_time.</value>
+ [JsonPropertyName("start_time")]
+ public string StartTime { get; set; }
+
+ /// <summary>
+ /// Gets or sets the duration.
+ /// </summary>
+ /// <value>The duration.</value>
+ [JsonPropertyName("duration")]
+ public string Duration { get; set; }
+
+ /// <summary>
+ /// Gets or sets the size.
+ /// </summary>
+ /// <value>The size.</value>
+ [JsonPropertyName("size")]
+ public string Size { get; set; }
+
+ /// <summary>
+ /// Gets or sets the bit_rate.
+ /// </summary>
+ /// <value>The bit_rate.</value>
+ [JsonPropertyName("bit_rate")]
+ public string BitRate { get; set; }
+
+ /// <summary>
+ /// Gets or sets the probe_score.
+ /// </summary>
+ /// <value>The probe_score.</value>
+ [JsonPropertyName("probe_score")]
+ public int ProbeScore { get; set; }
+
+ /// <summary>
+ /// Gets or sets the tags.
+ /// </summary>
+ /// <value>The tags.</value>
+ [JsonPropertyName("tags")]
+ public IReadOnlyDictionary<string, string> Tags { get; set; }
+ }
+}
diff --git a/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs b/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs
new file mode 100644
index 000000000..0b2f1d231
--- /dev/null
+++ b/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs
@@ -0,0 +1,282 @@
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+using MediaBrowser.Common.Json.Converters;
+
+namespace MediaBrowser.MediaEncoding.Probing
+{
+ /// <summary>
+ /// Represents a stream within the output.
+ /// </summary>
+ public class MediaStreamInfo
+ {
+ /// <summary>
+ /// Gets or sets the index.
+ /// </summary>
+ /// <value>The index.</value>
+ [JsonPropertyName("index")]
+ public int Index { get; set; }
+
+ /// <summary>
+ /// Gets or sets the profile.
+ /// </summary>
+ /// <value>The profile.</value>
+ [JsonPropertyName("profile")]
+ public string Profile { get; set; }
+
+ /// <summary>
+ /// Gets or sets the codec_name.
+ /// </summary>
+ /// <value>The codec_name.</value>
+ [JsonPropertyName("codec_name")]
+ public string CodecName { get; set; }
+
+ /// <summary>
+ /// Gets or sets the codec_long_name.
+ /// </summary>
+ /// <value>The codec_long_name.</value>
+ [JsonPropertyName("codec_long_name")]
+ public string CodecLongName { get; set; }
+
+ /// <summary>
+ /// Gets or sets the codec_type.
+ /// </summary>
+ /// <value>The codec_type.</value>
+ [JsonPropertyName("codec_type")]
+ public string CodecType { get; set; }
+
+ /// <summary>
+ /// Gets or sets the sample_rate.
+ /// </summary>
+ /// <value>The sample_rate.</value>
+ [JsonPropertyName("sample_rate")]
+ public string SampleRate { get; set; }
+
+ /// <summary>
+ /// Gets or sets the channels.
+ /// </summary>
+ /// <value>The channels.</value>
+ [JsonPropertyName("channels")]
+ public int Channels { get; set; }
+
+ /// <summary>
+ /// Gets or sets the channel_layout.
+ /// </summary>
+ /// <value>The channel_layout.</value>
+ [JsonPropertyName("channel_layout")]
+ public string ChannelLayout { get; set; }
+
+ /// <summary>
+ /// Gets or sets the avg_frame_rate.
+ /// </summary>
+ /// <value>The avg_frame_rate.</value>
+ [JsonPropertyName("avg_frame_rate")]
+ public string AverageFrameRate { get; set; }
+
+ /// <summary>
+ /// Gets or sets the duration.
+ /// </summary>
+ /// <value>The duration.</value>
+ [JsonPropertyName("duration")]
+ public string Duration { get; set; }
+
+ /// <summary>
+ /// Gets or sets the bit_rate.
+ /// </summary>
+ /// <value>The bit_rate.</value>
+ [JsonPropertyName("bit_rate")]
+ public string BitRate { get; set; }
+
+ /// <summary>
+ /// Gets or sets the width.
+ /// </summary>
+ /// <value>The width.</value>
+ [JsonPropertyName("width")]
+ public int Width { get; set; }
+
+ /// <summary>
+ /// Gets or sets the refs.
+ /// </summary>
+ /// <value>The refs.</value>
+ [JsonPropertyName("refs")]
+ public int Refs { get; set; }
+
+ /// <summary>
+ /// Gets or sets the height.
+ /// </summary>
+ /// <value>The height.</value>
+ [JsonPropertyName("height")]
+ public int Height { get; set; }
+
+ /// <summary>
+ /// Gets or sets the display_aspect_ratio.
+ /// </summary>
+ /// <value>The display_aspect_ratio.</value>
+ [JsonPropertyName("display_aspect_ratio")]
+ public string DisplayAspectRatio { get; set; }
+
+ /// <summary>
+ /// Gets or sets the tags.
+ /// </summary>
+ /// <value>The tags.</value>
+ [JsonPropertyName("tags")]
+ public IReadOnlyDictionary<string, string> Tags { get; set; }
+
+ /// <summary>
+ /// Gets or sets the bits_per_sample.
+ /// </summary>
+ /// <value>The bits_per_sample.</value>
+ [JsonPropertyName("bits_per_sample")]
+ public int BitsPerSample { get; set; }
+
+ /// <summary>
+ /// Gets or sets the bits_per_raw_sample.
+ /// </summary>
+ /// <value>The bits_per_raw_sample.</value>
+ [JsonPropertyName("bits_per_raw_sample")]
+ [JsonConverter(typeof(JsonInt32Converter))]
+ public int BitsPerRawSample { get; set; }
+
+ /// <summary>
+ /// Gets or sets the r_frame_rate.
+ /// </summary>
+ /// <value>The r_frame_rate.</value>
+ [JsonPropertyName("r_frame_rate")]
+ public string RFrameRate { get; set; }
+
+ /// <summary>
+ /// Gets or sets the has_b_frames.
+ /// </summary>
+ /// <value>The has_b_frames.</value>
+ [JsonPropertyName("has_b_frames")]
+ public int HasBFrames { get; set; }
+
+ /// <summary>
+ /// Gets or sets the sample_aspect_ratio.
+ /// </summary>
+ /// <value>The sample_aspect_ratio.</value>
+ [JsonPropertyName("sample_aspect_ratio")]
+ public string SampleAspectRatio { get; set; }
+
+ /// <summary>
+ /// Gets or sets the pix_fmt.
+ /// </summary>
+ /// <value>The pix_fmt.</value>
+ [JsonPropertyName("pix_fmt")]
+ public string PixelFormat { get; set; }
+
+ /// <summary>
+ /// Gets or sets the level.
+ /// </summary>
+ /// <value>The level.</value>
+ [JsonPropertyName("level")]
+ public int Level { get; set; }
+
+ /// <summary>
+ /// Gets or sets the time_base.
+ /// </summary>
+ /// <value>The time_base.</value>
+ [JsonPropertyName("time_base")]
+ public string TimeBase { get; set; }
+
+ /// <summary>
+ /// Gets or sets the start_time.
+ /// </summary>
+ /// <value>The start_time.</value>
+ [JsonPropertyName("start_time")]
+ public string StartTime { get; set; }
+
+ /// <summary>
+ /// Gets or sets the codec_time_base.
+ /// </summary>
+ /// <value>The codec_time_base.</value>
+ [JsonPropertyName("codec_time_base")]
+ public string CodecTimeBase { get; set; }
+
+ /// <summary>
+ /// Gets or sets the codec_tag.
+ /// </summary>
+ /// <value>The codec_tag.</value>
+ [JsonPropertyName("codec_tag")]
+ public string CodecTag { get; set; }
+
+ /// <summary>
+ /// Gets or sets the codec_tag_string.
+ /// </summary>
+ /// <value>The codec_tag_string.</value>
+ [JsonPropertyName("codec_tag_string")]
+ public string CodecTagString { get; set; }
+
+ /// <summary>
+ /// Gets or sets the sample_fmt.
+ /// </summary>
+ /// <value>The sample_fmt.</value>
+ [JsonPropertyName("sample_fmt")]
+ public string SampleFmt { get; set; }
+
+ /// <summary>
+ /// Gets or sets the dmix_mode.
+ /// </summary>
+ /// <value>The dmix_mode.</value>
+ [JsonPropertyName("dmix_mode")]
+ public string DmixMode { get; set; }
+
+ /// <summary>
+ /// Gets or sets the start_pts.
+ /// </summary>
+ /// <value>The start_pts.</value>
+ [JsonPropertyName("start_pts")]
+ public long StartPts { get; set; }
+
+ /// <summary>
+ /// Gets or sets the is_avc.
+ /// </summary>
+ /// <value>The is_avc.</value>
+ [JsonPropertyName("is_avc")]
+ public string IsAvc { get; set; }
+
+ /// <summary>
+ /// Gets or sets the nal_length_size.
+ /// </summary>
+ /// <value>The nal_length_size.</value>
+ [JsonPropertyName("nal_length_size")]
+ public string NalLengthSize { get; set; }
+
+ /// <summary>
+ /// Gets or sets the ltrt_cmixlev.
+ /// </summary>
+ /// <value>The ltrt_cmixlev.</value>
+ [JsonPropertyName("ltrt_cmixlev")]
+ public string LtrtCmixlev { get; set; }
+
+ /// <summary>
+ /// Gets or sets the ltrt_surmixlev.
+ /// </summary>
+ /// <value>The ltrt_surmixlev.</value>
+ [JsonPropertyName("ltrt_surmixlev")]
+ public string LtrtSurmixlev { get; set; }
+
+ /// <summary>
+ /// Gets or sets the loro_cmixlev.
+ /// </summary>
+ /// <value>The loro_cmixlev.</value>
+ [JsonPropertyName("loro_cmixlev")]
+ public string LoroCmixlev { get; set; }
+
+ /// <summary>
+ /// Gets or sets the loro_surmixlev.
+ /// </summary>
+ /// <value>The loro_surmixlev.</value>
+ [JsonPropertyName("loro_surmixlev")]
+ public string LoroSurmixlev { get; set; }
+
+ [JsonPropertyName("field_order")]
+ public string FieldOrder { get; set; }
+
+ /// <summary>
+ /// Gets or sets the disposition.
+ /// </summary>
+ /// <value>The disposition.</value>
+ [JsonPropertyName("disposition")]
+ public IReadOnlyDictionary<string, int> Disposition { get; set; }
+ }
+}
diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
index 54d02fc9f..f8047af42 100644
--- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
+++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
@@ -8,7 +8,6 @@ using System.Xml;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.MediaInfo;
@@ -41,21 +40,25 @@ namespace MediaBrowser.MediaEncoding.Probing
FFProbeHelpers.NormalizeFFProbeResult(data);
SetSize(data, info);
- var internalStreams = data.streams ?? new MediaStreamInfo[] { };
+ var internalStreams = data.Streams ?? new MediaStreamInfo[] { };
- info.MediaStreams = internalStreams.Select(s => GetMediaStream(isAudio, s, data.format))
+ info.MediaStreams = internalStreams.Select(s => GetMediaStream(isAudio, s, data.Format))
.Where(i => i != null)
// Drop subtitle streams if we don't know the codec because it will just cause failures if we don't know how to handle them
.Where(i => i.Type != MediaStreamType.Subtitle || !string.IsNullOrWhiteSpace(i.Codec))
.ToList();
- if (data.format != null)
+ info.MediaAttachments = internalStreams.Select(s => GetMediaAttachment(s))
+ .Where(i => i != null)
+ .ToList();
+
+ if (data.Format != null)
{
- info.Container = NormalizeFormat(data.format.format_name);
+ info.Container = NormalizeFormat(data.Format.FormatName);
- if (!string.IsNullOrEmpty(data.format.bit_rate))
+ if (!string.IsNullOrEmpty(data.Format.BitRate))
{
- if (int.TryParse(data.format.bit_rate, NumberStyles.Any, _usCulture, out var value))
+ if (int.TryParse(data.Format.BitRate, NumberStyles.Any, _usCulture, out var value))
{
info.Bitrate = value;
}
@@ -65,22 +68,22 @@ namespace MediaBrowser.MediaEncoding.Probing
var tags = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
var tagStreamType = isAudio ? "audio" : "video";
- if (data.streams != null)
+ if (data.Streams != null)
{
- var tagStream = data.streams.FirstOrDefault(i => string.Equals(i.codec_type, tagStreamType, StringComparison.OrdinalIgnoreCase));
+ var tagStream = data.Streams.FirstOrDefault(i => string.Equals(i.CodecType, tagStreamType, StringComparison.OrdinalIgnoreCase));
- if (tagStream != null && tagStream.tags != null)
+ if (tagStream != null && tagStream.Tags != null)
{
- foreach (var pair in tagStream.tags)
+ foreach (var pair in tagStream.Tags)
{
tags[pair.Key] = pair.Value;
}
}
}
- if (data.format != null && data.format.tags != null)
+ if (data.Format != null && data.Format.Tags != null)
{
- foreach (var pair in data.format.tags)
+ foreach (var pair in data.Format.Tags)
{
tags[pair.Key] = pair.Value;
}
@@ -109,6 +112,9 @@ namespace MediaBrowser.MediaEncoding.Probing
info.Name = title;
}
+ info.IndexNumber = FFProbeHelpers.GetDictionaryNumericValue(tags, "episode_sort");
+ info.ParentIndexNumber = FFProbeHelpers.GetDictionaryNumericValue(tags, "season_number");
+ info.ShowName = FFProbeHelpers.GetDictionaryValue(tags, "show_name");
info.ProductionYear = FFProbeHelpers.GetDictionaryNumericValue(tags, "date");
// Several different forms of retaildate
@@ -153,9 +159,9 @@ namespace MediaBrowser.MediaEncoding.Probing
FetchFromItunesInfo(itunesXml, info);
}
- if (data.format != null && !string.IsNullOrEmpty(data.format.duration))
+ if (data.Format != null && !string.IsNullOrEmpty(data.Format.Duration))
{
- info.RunTimeTicks = TimeSpan.FromSeconds(double.Parse(data.format.duration, _usCulture)).Ticks;
+ info.RunTimeTicks = TimeSpan.FromSeconds(double.Parse(data.Format.Duration, _usCulture)).Ticks;
}
FetchWtvInfo(info, data);
@@ -514,6 +520,39 @@ namespace MediaBrowser.MediaEncoding.Probing
}
/// <summary>
+ /// Converts ffprobe stream info to our MediaAttachment class
+ /// </summary>
+ /// <param name="streamInfo">The stream info.</param>
+ /// <returns>MediaAttachments.</returns>
+ private MediaAttachment GetMediaAttachment(MediaStreamInfo streamInfo)
+ {
+ if (!string.Equals(streamInfo.CodecType, "attachment", StringComparison.OrdinalIgnoreCase))
+ {
+ return null;
+ }
+
+ var attachment = new MediaAttachment
+ {
+ Codec = streamInfo.CodecName,
+ Index = streamInfo.Index
+ };
+
+ if (!string.IsNullOrWhiteSpace(streamInfo.CodecTagString))
+ {
+ attachment.CodecTag = streamInfo.CodecTagString;
+ }
+
+ if (streamInfo.Tags != null)
+ {
+ attachment.FileName = GetDictionaryValue(streamInfo.Tags, "filename");
+ attachment.MimeType = GetDictionaryValue(streamInfo.Tags, "mimetype");
+ attachment.Comment = GetDictionaryValue(streamInfo.Tags, "comment");
+ }
+
+ return attachment;
+ }
+
+ /// <summary>
/// Converts ffprobe stream info to our MediaStream class
/// </summary>
/// <param name="isAudio">if set to <c>true</c> [is info].</param>
@@ -523,7 +562,7 @@ namespace MediaBrowser.MediaEncoding.Probing
private MediaStream GetMediaStream(bool isAudio, MediaStreamInfo streamInfo, MediaFormatInfo formatInfo)
{
// These are mp4 chapters
- if (string.Equals(streamInfo.codec_name, "mov_text", StringComparison.OrdinalIgnoreCase))
+ if (string.Equals(streamInfo.CodecName, "mov_text", StringComparison.OrdinalIgnoreCase))
{
// Edit: but these are also sometimes subtitles?
//return null;
@@ -531,71 +570,71 @@ namespace MediaBrowser.MediaEncoding.Probing
var stream = new MediaStream
{
- Codec = streamInfo.codec_name,
- Profile = streamInfo.profile,
- Level = streamInfo.level,
- Index = streamInfo.index,
- PixelFormat = streamInfo.pix_fmt,
- NalLengthSize = streamInfo.nal_length_size,
- TimeBase = streamInfo.time_base,
- CodecTimeBase = streamInfo.codec_time_base
+ Codec = streamInfo.CodecName,
+ Profile = streamInfo.Profile,
+ Level = streamInfo.Level,
+ Index = streamInfo.Index,
+ PixelFormat = streamInfo.PixelFormat,
+ NalLengthSize = streamInfo.NalLengthSize,
+ TimeBase = streamInfo.TimeBase,
+ CodecTimeBase = streamInfo.CodecTimeBase
};
- if (string.Equals(streamInfo.is_avc, "true", StringComparison.OrdinalIgnoreCase) ||
- string.Equals(streamInfo.is_avc, "1", StringComparison.OrdinalIgnoreCase))
+ if (string.Equals(streamInfo.IsAvc, "true", StringComparison.OrdinalIgnoreCase) ||
+ string.Equals(streamInfo.IsAvc, "1", StringComparison.OrdinalIgnoreCase))
{
stream.IsAVC = true;
}
- else if (string.Equals(streamInfo.is_avc, "false", StringComparison.OrdinalIgnoreCase) ||
- string.Equals(streamInfo.is_avc, "0", StringComparison.OrdinalIgnoreCase))
+ else if (string.Equals(streamInfo.IsAvc, "false", StringComparison.OrdinalIgnoreCase) ||
+ string.Equals(streamInfo.IsAvc, "0", StringComparison.OrdinalIgnoreCase))
{
stream.IsAVC = false;
}
- if (!string.IsNullOrWhiteSpace(streamInfo.field_order) && !string.Equals(streamInfo.field_order, "progressive", StringComparison.OrdinalIgnoreCase))
+ if (!string.IsNullOrWhiteSpace(streamInfo.FieldOrder) && !string.Equals(streamInfo.FieldOrder, "progressive", StringComparison.OrdinalIgnoreCase))
{
stream.IsInterlaced = true;
}
// Filter out junk
- if (!string.IsNullOrWhiteSpace(streamInfo.codec_tag_string) && streamInfo.codec_tag_string.IndexOf("[0]", StringComparison.OrdinalIgnoreCase) == -1)
+ if (!string.IsNullOrWhiteSpace(streamInfo.CodecTagString) && streamInfo.CodecTagString.IndexOf("[0]", StringComparison.OrdinalIgnoreCase) == -1)
{
- stream.CodecTag = streamInfo.codec_tag_string;
+ stream.CodecTag = streamInfo.CodecTagString;
}
- if (streamInfo.tags != null)
+ if (streamInfo.Tags != null)
{
- stream.Language = GetDictionaryValue(streamInfo.tags, "language");
- stream.Comment = GetDictionaryValue(streamInfo.tags, "comment");
- stream.Title = GetDictionaryValue(streamInfo.tags, "title");
+ stream.Language = GetDictionaryValue(streamInfo.Tags, "language");
+ stream.Comment = GetDictionaryValue(streamInfo.Tags, "comment");
+ stream.Title = GetDictionaryValue(streamInfo.Tags, "title");
}
- if (string.Equals(streamInfo.codec_type, "audio", StringComparison.OrdinalIgnoreCase))
+ if (string.Equals(streamInfo.CodecType, "audio", StringComparison.OrdinalIgnoreCase))
{
stream.Type = MediaStreamType.Audio;
- stream.Channels = streamInfo.channels;
+ stream.Channels = streamInfo.Channels;
- if (!string.IsNullOrEmpty(streamInfo.sample_rate))
+ if (!string.IsNullOrEmpty(streamInfo.SampleRate))
{
- if (int.TryParse(streamInfo.sample_rate, NumberStyles.Any, _usCulture, out var value))
+ if (int.TryParse(streamInfo.SampleRate, NumberStyles.Any, _usCulture, out var value))
{
stream.SampleRate = value;
}
}
- stream.ChannelLayout = ParseChannelLayout(streamInfo.channel_layout);
+ stream.ChannelLayout = ParseChannelLayout(streamInfo.ChannelLayout);
- if (streamInfo.bits_per_sample > 0)
+ if (streamInfo.BitsPerSample > 0)
{
- stream.BitDepth = streamInfo.bits_per_sample;
+ stream.BitDepth = streamInfo.BitsPerSample;
}
- else if (streamInfo.bits_per_raw_sample > 0)
+ else if (streamInfo.BitsPerRawSample > 0)
{
- stream.BitDepth = streamInfo.bits_per_raw_sample;
+ stream.BitDepth = streamInfo.BitsPerRawSample;
}
}
- else if (string.Equals(streamInfo.codec_type, "subtitle", StringComparison.OrdinalIgnoreCase))
+ else if (string.Equals(streamInfo.CodecType, "subtitle", StringComparison.OrdinalIgnoreCase))
{
stream.Type = MediaStreamType.Subtitle;
stream.Codec = NormalizeSubtitleCodec(stream.Codec);
@@ -603,14 +642,14 @@ namespace MediaBrowser.MediaEncoding.Probing
stream.localizedDefault = _localization.GetLocalizedString("Default");
stream.localizedForced = _localization.GetLocalizedString("Forced");
}
- else if (string.Equals(streamInfo.codec_type, "video", StringComparison.OrdinalIgnoreCase))
+ else if (string.Equals(streamInfo.CodecType, "video", StringComparison.OrdinalIgnoreCase))
{
stream.Type = isAudio || string.Equals(stream.Codec, "mjpeg", StringComparison.OrdinalIgnoreCase) || string.Equals(stream.Codec, "gif", StringComparison.OrdinalIgnoreCase) || string.Equals(stream.Codec, "png", StringComparison.OrdinalIgnoreCase)
? MediaStreamType.EmbeddedImage
: MediaStreamType.Video;
- stream.AverageFrameRate = GetFrameRate(streamInfo.avg_frame_rate);
- stream.RealFrameRate = GetFrameRate(streamInfo.r_frame_rate);
+ stream.AverageFrameRate = GetFrameRate(streamInfo.AverageFrameRate);
+ stream.RealFrameRate = GetFrameRate(streamInfo.RFrameRate);
if (isAudio || string.Equals(stream.Codec, "gif", StringComparison.OrdinalIgnoreCase) ||
string.Equals(stream.Codec, "png", StringComparison.OrdinalIgnoreCase))
@@ -635,17 +674,17 @@ namespace MediaBrowser.MediaEncoding.Probing
stream.Type = MediaStreamType.Video;
}
- stream.Width = streamInfo.width;
- stream.Height = streamInfo.height;
+ stream.Width = streamInfo.Width;
+ stream.Height = streamInfo.Height;
stream.AspectRatio = GetAspectRatio(streamInfo);
- if (streamInfo.bits_per_sample > 0)
+ if (streamInfo.BitsPerSample > 0)
{
- stream.BitDepth = streamInfo.bits_per_sample;
+ stream.BitDepth = streamInfo.BitsPerSample;
}
- else if (streamInfo.bits_per_raw_sample > 0)
+ else if (streamInfo.BitsPerRawSample > 0)
{
- stream.BitDepth = streamInfo.bits_per_raw_sample;
+ stream.BitDepth = streamInfo.BitsPerRawSample;
}
//stream.IsAnamorphic = string.Equals(streamInfo.sample_aspect_ratio, "0:1", StringComparison.OrdinalIgnoreCase) ||
@@ -653,11 +692,11 @@ namespace MediaBrowser.MediaEncoding.Probing
// string.Equals(stream.AspectRatio, "2.40:1", StringComparison.OrdinalIgnoreCase);
// http://stackoverflow.com/questions/17353387/how-to-detect-anamorphic-video-with-ffprobe
- stream.IsAnamorphic = string.Equals(streamInfo.sample_aspect_ratio, "0:1", StringComparison.OrdinalIgnoreCase);
+ stream.IsAnamorphic = string.Equals(streamInfo.SampleAspectRatio, "0:1", StringComparison.OrdinalIgnoreCase);
- if (streamInfo.refs > 0)
+ if (streamInfo.Refs > 0)
{
- stream.RefFrames = streamInfo.refs;
+ stream.RefFrames = streamInfo.Refs;
}
}
else
@@ -668,18 +707,18 @@ namespace MediaBrowser.MediaEncoding.Probing
// Get stream bitrate
var bitrate = 0;
- if (!string.IsNullOrEmpty(streamInfo.bit_rate))
+ if (!string.IsNullOrEmpty(streamInfo.BitRate))
{
- if (int.TryParse(streamInfo.bit_rate, NumberStyles.Any, _usCulture, out var value))
+ if (int.TryParse(streamInfo.BitRate, NumberStyles.Any, _usCulture, out var value))
{
bitrate = value;
}
}
- if (bitrate == 0 && formatInfo != null && !string.IsNullOrEmpty(formatInfo.bit_rate) && stream.Type == MediaStreamType.Video)
+ if (bitrate == 0 && formatInfo != null && !string.IsNullOrEmpty(formatInfo.BitRate) && stream.Type == MediaStreamType.Video)
{
// If the stream info doesn't have a bitrate get the value from the media format info
- if (int.TryParse(formatInfo.bit_rate, NumberStyles.Any, _usCulture, out var value))
+ if (int.TryParse(formatInfo.BitRate, NumberStyles.Any, _usCulture, out var value))
{
bitrate = value;
}
@@ -690,14 +729,18 @@ namespace MediaBrowser.MediaEncoding.Probing
stream.BitRate = bitrate;
}
- if (streamInfo.disposition != null)
+ var disposition = streamInfo.Disposition;
+ if (disposition != null)
{
- var isDefault = GetDictionaryValue(streamInfo.disposition, "default");
- var isForced = GetDictionaryValue(streamInfo.disposition, "forced");
-
- stream.IsDefault = string.Equals(isDefault, "1", StringComparison.OrdinalIgnoreCase);
+ if (disposition.GetValueOrDefault("default") == 1)
+ {
+ stream.IsDefault = true;
+ }
- stream.IsForced = string.Equals(isForced, "1", StringComparison.OrdinalIgnoreCase);
+ if (disposition.GetValueOrDefault("forced") == 1)
+ {
+ stream.IsForced = true;
+ }
}
NormalizeStreamTitle(stream);
@@ -724,7 +767,7 @@ namespace MediaBrowser.MediaEncoding.Probing
/// <param name="tags">The tags.</param>
/// <param name="key">The key.</param>
/// <returns>System.String.</returns>
- private string GetDictionaryValue(Dictionary<string, string> tags, string key)
+ private string GetDictionaryValue(IReadOnlyDictionary<string, string> tags, string key)
{
if (tags == null)
{
@@ -747,7 +790,7 @@ namespace MediaBrowser.MediaEncoding.Probing
private string GetAspectRatio(MediaStreamInfo info)
{
- var original = info.display_aspect_ratio;
+ var original = info.DisplayAspectRatio;
var parts = (original ?? string.Empty).Split(':');
if (!(parts.Length == 2 &&
@@ -756,8 +799,8 @@ namespace MediaBrowser.MediaEncoding.Probing
width > 0 &&
height > 0))
{
- width = info.width;
- height = info.height;
+ width = info.Width;
+ height = info.Height;
}
if (width > 0 && height > 0)
@@ -850,20 +893,20 @@ namespace MediaBrowser.MediaEncoding.Probing
private void SetAudioRuntimeTicks(InternalMediaInfoResult result, MediaInfo data)
{
- if (result.streams != null)
+ if (result.Streams != null)
{
// Get the first info stream
- var stream = result.streams.FirstOrDefault(s => string.Equals(s.codec_type, "audio", StringComparison.OrdinalIgnoreCase));
+ var stream = result.Streams.FirstOrDefault(s => string.Equals(s.CodecType, "audio", StringComparison.OrdinalIgnoreCase));
if (stream != null)
{
// Get duration from stream properties
- var duration = stream.duration;
+ var duration = stream.Duration;
// If it's not there go into format properties
if (string.IsNullOrEmpty(duration))
{
- duration = result.format.duration;
+ duration = result.Format.Duration;
}
// If we got something, parse it
@@ -877,11 +920,11 @@ namespace MediaBrowser.MediaEncoding.Probing
private void SetSize(InternalMediaInfoResult data, MediaInfo info)
{
- if (data.format != null)
+ if (data.Format != null)
{
- if (!string.IsNullOrEmpty(data.format.size))
+ if (!string.IsNullOrEmpty(data.Format.Size))
{
- info.Size = long.Parse(data.format.size, _usCulture);
+ info.Size = long.Parse(data.Format.Size, _usCulture);
}
else
{
@@ -1194,16 +1237,16 @@ namespace MediaBrowser.MediaEncoding.Probing
{
var info = new ChapterInfo();
- if (chapter.tags != null)
+ if (chapter.Tags != null)
{
- if (chapter.tags.TryGetValue("title", out string name))
+ if (chapter.Tags.TryGetValue("title", out string name))
{
info.Name = name;
}
}
// Limit accuracy to milliseconds to match xml saving
- var secondsString = chapter.start_time;
+ var secondsString = chapter.StartTime;
if (double.TryParse(secondsString, NumberStyles.Any, CultureInfo.InvariantCulture, out var seconds))
{
@@ -1218,12 +1261,12 @@ namespace MediaBrowser.MediaEncoding.Probing
private void FetchWtvInfo(MediaInfo video, InternalMediaInfoResult data)
{
- if (data.format == null || data.format.tags == null)
+ if (data.Format == null || data.Format.Tags == null)
{
return;
}
- var genres = FFProbeHelpers.GetDictionaryValue(data.format.tags, "WM/Genre");
+ var genres = FFProbeHelpers.GetDictionaryValue(data.Format.Tags, "WM/Genre");
if (!string.IsNullOrWhiteSpace(genres))
{
@@ -1239,14 +1282,14 @@ namespace MediaBrowser.MediaEncoding.Probing
}
}
- var officialRating = FFProbeHelpers.GetDictionaryValue(data.format.tags, "WM/ParentalRating");
+ var officialRating = FFProbeHelpers.GetDictionaryValue(data.Format.Tags, "WM/ParentalRating");
if (!string.IsNullOrWhiteSpace(officialRating))
{
video.OfficialRating = officialRating;
}
- var people = FFProbeHelpers.GetDictionaryValue(data.format.tags, "WM/MediaCredits");
+ var people = FFProbeHelpers.GetDictionaryValue(data.Format.Tags, "WM/MediaCredits");
if (!string.IsNullOrEmpty(people))
{
@@ -1256,7 +1299,7 @@ namespace MediaBrowser.MediaEncoding.Probing
.ToArray();
}
- var year = FFProbeHelpers.GetDictionaryValue(data.format.tags, "WM/OriginalReleaseTime");
+ var year = FFProbeHelpers.GetDictionaryValue(data.Format.Tags, "WM/OriginalReleaseTime");
if (!string.IsNullOrWhiteSpace(year))
{
if (int.TryParse(year, NumberStyles.Integer, _usCulture, out var val))
@@ -1265,7 +1308,7 @@ namespace MediaBrowser.MediaEncoding.Probing
}
}
- var premiereDateString = FFProbeHelpers.GetDictionaryValue(data.format.tags, "WM/MediaOriginalBroadcastDateTime");
+ var premiereDateString = FFProbeHelpers.GetDictionaryValue(data.Format.Tags, "WM/MediaOriginalBroadcastDateTime");
if (!string.IsNullOrWhiteSpace(premiereDateString))
{
// Credit to MCEBuddy: https://mcebuddy2x.codeplex.com/
@@ -1276,9 +1319,9 @@ namespace MediaBrowser.MediaEncoding.Probing
}
}
- var description = FFProbeHelpers.GetDictionaryValue(data.format.tags, "WM/SubTitleDescription");
+ var description = FFProbeHelpers.GetDictionaryValue(data.Format.Tags, "WM/SubTitleDescription");
- var subTitle = FFProbeHelpers.GetDictionaryValue(data.format.tags, "WM/SubTitle");
+ var subTitle = FFProbeHelpers.GetDictionaryValue(data.Format.Tags, "WM/SubTitle");
// For below code, credit to MCEBuddy: https://mcebuddy2x.codeplex.com/
@@ -1334,24 +1377,25 @@ namespace MediaBrowser.MediaEncoding.Probing
{
video.Timestamp = GetMpegTimestamp(video.Path);
- _logger.LogDebug("Video has {timestamp} timestamp", video.Timestamp);
+ _logger.LogDebug("Video has {Timestamp} timestamp", video.Timestamp);
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error extracting timestamp info from {path}", video.Path);
+ _logger.LogError(ex, "Error extracting timestamp info from {Path}", video.Path);
video.Timestamp = null;
}
}
}
}
+ // REVIEW: find out why the byte array needs to be 197 bytes long and comment the reason
private TransportStreamTimestamp GetMpegTimestamp(string path)
{
- var packetBuffer = new byte['Å'];
+ var packetBuffer = new byte[197];
- using (var fs = _fileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read))
+ using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
{
- fs.Read(packetBuffer, 0, packetBuffer.Length);
+ fs.Read(packetBuffer);
}
if (packetBuffer[0] == 71)
@@ -1359,7 +1403,7 @@ namespace MediaBrowser.MediaEncoding.Probing
return TransportStreamTimestamp.None;
}
- if ((packetBuffer[4] == 71) && (packetBuffer['Ä'] == 71))
+ if ((packetBuffer[4] == 71) && (packetBuffer[196] == 71))
{
if ((packetBuffer[0] == 0) && (packetBuffer[1] == 0) && (packetBuffer[2] == 0) && (packetBuffer[3] == 0))
{
diff --git a/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs b/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs
index 605504418..293cf5ea5 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs
@@ -5,7 +5,6 @@ using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
-using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.MediaInfo;
namespace MediaBrowser.MediaEncoding.Subtitles
diff --git a/MediaBrowser.MediaEncoding/Subtitles/JsonWriter.cs b/MediaBrowser.MediaEncoding/Subtitles/JsonWriter.cs
index 241ebc6df..1b452b0ce 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/JsonWriter.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/JsonWriter.cs
@@ -16,6 +16,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
using (var writer = new Utf8JsonWriter(stream))
{
var trackevents = info.TrackEvents;
+ writer.WriteStartObject();
writer.WriteStartArray("TrackEvents");
for (int i = 0; i < trackevents.Count; i++)
@@ -33,7 +34,10 @@ namespace MediaBrowser.MediaEncoding.Subtitles
writer.WriteEndObject();
}
+ writer.WriteEndArray();
writer.WriteEndObject();
+
+ writer.Flush();
}
}
}
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs
index 0606dbdb2..c98dd1502 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs
@@ -4,7 +4,6 @@ using System.Globalization;
using System.IO;
using System.Text.RegularExpressions;
using System.Threading;
-using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.MediaInfo;
using Microsoft.Extensions.Logging;
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs
index 0d696b906..b94d45165 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs
@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
-using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.MediaInfo;
namespace MediaBrowser.MediaEncoding.Subtitles
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
index 183d7566d..72db56974 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
@@ -123,7 +123,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
throw new ArgumentNullException(nameof(mediaSourceId));
}
- var mediaSources = await _mediaSourceManager.GetPlayackMediaSources(item, null, true, false, cancellationToken).ConfigureAwait(false);
+ var mediaSources = await _mediaSourceManager.GetPlaybackMediaSources(item, null, true, false, cancellationToken).ConfigureAwait(false);
var mediaSource = mediaSources
.First(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase));
@@ -183,11 +183,12 @@ namespace MediaBrowser.MediaEncoding.Subtitles
private async Task<Stream> GetSubtitleStream(string path, MediaProtocol protocol, bool requiresCharset, CancellationToken cancellationToken)
{
- if (requiresCharset)
+ using (var stream = await GetStream(path, protocol, cancellationToken).ConfigureAwait(false))
{
- using (var stream = await GetStream(path, protocol, cancellationToken).ConfigureAwait(false))
+ if (requiresCharset)
{
var result = CharsetDetector.DetectFromStream(stream).Detected;
+ stream.Position = 0;
if (result != null)
{
@@ -199,9 +200,9 @@ namespace MediaBrowser.MediaEncoding.Subtitles
return new MemoryStream(Encoding.UTF8.GetBytes(text));
}
}
- }
- return File.OpenRead(path);
+ return stream;
+ }
}
private async Task<SubtitleInfo> GetReadableFile(
@@ -691,7 +692,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
if (!string.Equals(text, newText))
{
- using (var fileStream = _fileSystem.GetFileStream(file, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
+ using (var fileStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read))
using (var writer = new StreamWriter(fileStream, encoding))
{
writer.Write(newText);
@@ -745,6 +746,8 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
Url = path,
CancellationToken = cancellationToken,
+
+ // Needed for seeking
BufferContent = true
};
diff --git a/MediaBrowser.MediaEncoding/Subtitles/TtmlWriter.cs b/MediaBrowser.MediaEncoding/Subtitles/TtmlWriter.cs
index 4f15bac49..7d3e18578 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/TtmlWriter.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/TtmlWriter.cs
@@ -1,4 +1,3 @@
-using System;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
diff --git a/MediaBrowser.MediaEncoding/packages.config b/MediaBrowser.MediaEncoding/packages.config
deleted file mode 100644
index bbeaf5f00..000000000
--- a/MediaBrowser.MediaEncoding/packages.config
+++ /dev/null
@@ -1,3 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<packages>
-</packages> \ No newline at end of file
diff --git a/MediaBrowser.Model/Activity/ActivityLogEntry.cs b/MediaBrowser.Model/Activity/ActivityLogEntry.cs
index 186fd89ee..80f01b66e 100644
--- a/MediaBrowser.Model/Activity/ActivityLogEntry.cs
+++ b/MediaBrowser.Model/Activity/ActivityLogEntry.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using Microsoft.Extensions.Logging;
diff --git a/MediaBrowser.Model/Activity/IActivityManager.cs b/MediaBrowser.Model/Activity/IActivityManager.cs
index 897d93d79..f336f5272 100644
--- a/MediaBrowser.Model/Activity/IActivityManager.cs
+++ b/MediaBrowser.Model/Activity/IActivityManager.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Querying;
diff --git a/MediaBrowser.Model/Activity/IActivityRepository.cs b/MediaBrowser.Model/Activity/IActivityRepository.cs
index f0e3b902c..66144ec47 100644
--- a/MediaBrowser.Model/Activity/IActivityRepository.cs
+++ b/MediaBrowser.Model/Activity/IActivityRepository.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using MediaBrowser.Model.Querying;
diff --git a/MediaBrowser.Model/ApiClient/ServerDiscoveryInfo.cs b/MediaBrowser.Model/ApiClient/ServerDiscoveryInfo.cs
index d1f3577f7..bb203f895 100644
--- a/MediaBrowser.Model/ApiClient/ServerDiscoveryInfo.cs
+++ b/MediaBrowser.Model/ApiClient/ServerDiscoveryInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.ApiClient
{
public class ServerDiscoveryInfo
@@ -7,16 +9,19 @@ namespace MediaBrowser.Model.ApiClient
/// </summary>
/// <value>The address.</value>
public string Address { get; set; }
+
/// <summary>
/// Gets or sets the server identifier.
/// </summary>
/// <value>The server identifier.</value>
public string Id { get; set; }
+
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
public string Name { get; set; }
+
/// <summary>
/// Gets or sets the endpoint address.
/// </summary>
diff --git a/MediaBrowser.Model/Branding/BrandingOptions.cs b/MediaBrowser.Model/Branding/BrandingOptions.cs
index f83558df5..8ab268a64 100644
--- a/MediaBrowser.Model/Branding/BrandingOptions.cs
+++ b/MediaBrowser.Model/Branding/BrandingOptions.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Branding
{
public class BrandingOptions
@@ -7,6 +9,7 @@ namespace MediaBrowser.Model.Branding
/// </summary>
/// <value>The login disclaimer.</value>
public string LoginDisclaimer { get; set; }
+
/// <summary>
/// Gets or sets the custom CSS.
/// </summary>
diff --git a/MediaBrowser.Model/Channels/ChannelFeatures.cs b/MediaBrowser.Model/Channels/ChannelFeatures.cs
index ee1d11bc0..c4e97ffe5 100644
--- a/MediaBrowser.Model/Channels/ChannelFeatures.cs
+++ b/MediaBrowser.Model/Channels/ChannelFeatures.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace MediaBrowser.Model.Channels
diff --git a/MediaBrowser.Model/Channels/ChannelFolderType.cs b/MediaBrowser.Model/Channels/ChannelFolderType.cs
index 6039eb929..9ead74261 100644
--- a/MediaBrowser.Model/Channels/ChannelFolderType.cs
+++ b/MediaBrowser.Model/Channels/ChannelFolderType.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Channels
{
public enum ChannelFolderType
diff --git a/MediaBrowser.Model/Channels/ChannelInfo.cs b/MediaBrowser.Model/Channels/ChannelInfo.cs
index 9b2d31bf3..bfb34db55 100644
--- a/MediaBrowser.Model/Channels/ChannelInfo.cs
+++ b/MediaBrowser.Model/Channels/ChannelInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Channels
{
public class ChannelInfo
diff --git a/MediaBrowser.Model/Channels/ChannelItemSortField.cs b/MediaBrowser.Model/Channels/ChannelItemSortField.cs
index af75e3edd..2c88e99af 100644
--- a/MediaBrowser.Model/Channels/ChannelItemSortField.cs
+++ b/MediaBrowser.Model/Channels/ChannelItemSortField.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Channels
{
public enum ChannelItemSortField
diff --git a/MediaBrowser.Model/Channels/ChannelMediaContentType.cs b/MediaBrowser.Model/Channels/ChannelMediaContentType.cs
index fc7c21706..61afcbc56 100644
--- a/MediaBrowser.Model/Channels/ChannelMediaContentType.cs
+++ b/MediaBrowser.Model/Channels/ChannelMediaContentType.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Channels
{
public enum ChannelMediaContentType
diff --git a/MediaBrowser.Model/Channels/ChannelMediaType.cs b/MediaBrowser.Model/Channels/ChannelMediaType.cs
index a3fa5cdf9..ba2c06d1b 100644
--- a/MediaBrowser.Model/Channels/ChannelMediaType.cs
+++ b/MediaBrowser.Model/Channels/ChannelMediaType.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Channels
{
public enum ChannelMediaType
diff --git a/MediaBrowser.Model/Channels/ChannelQuery.cs b/MediaBrowser.Model/Channels/ChannelQuery.cs
index 32b368922..88fc94a6f 100644
--- a/MediaBrowser.Model/Channels/ChannelQuery.cs
+++ b/MediaBrowser.Model/Channels/ChannelQuery.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
diff --git a/MediaBrowser.Model/Collections/CollectionCreationResult.cs b/MediaBrowser.Model/Collections/CollectionCreationResult.cs
index 2691f7970..2f1d903a5 100644
--- a/MediaBrowser.Model/Collections/CollectionCreationResult.cs
+++ b/MediaBrowser.Model/Collections/CollectionCreationResult.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace MediaBrowser.Model.Collections
diff --git a/MediaBrowser.Model/Configuration/AccessSchedule.cs b/MediaBrowser.Model/Configuration/AccessSchedule.cs
index d34e27383..120c47dbc 100644
--- a/MediaBrowser.Model/Configuration/AccessSchedule.cs
+++ b/MediaBrowser.Model/Configuration/AccessSchedule.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Configuration
{
public class AccessSchedule
@@ -7,11 +9,13 @@ namespace MediaBrowser.Model.Configuration
/// </summary>
/// <value>The day of week.</value>
public DynamicDayOfWeek DayOfWeek { get; set; }
+
/// <summary>
/// Gets or sets the start hour.
/// </summary>
/// <value>The start hour.</value>
public double StartHour { get; set; }
+
/// <summary>
/// Gets or sets the end hour.
/// </summary>
diff --git a/MediaBrowser.Model/Configuration/BaseApplicationConfiguration.cs b/MediaBrowser.Model/Configuration/BaseApplicationConfiguration.cs
index 6a1a0f090..cc2541f74 100644
--- a/MediaBrowser.Model/Configuration/BaseApplicationConfiguration.cs
+++ b/MediaBrowser.Model/Configuration/BaseApplicationConfiguration.cs
@@ -1,3 +1,6 @@
+using System;
+using System.Xml.Serialization;
+
namespace MediaBrowser.Model.Configuration
{
/// <summary>
@@ -26,6 +29,24 @@ namespace MediaBrowser.Model.Configuration
public string CachePath { get; set; }
/// <summary>
+ /// Last known version that was ran using the configuration.
+ /// </summary>
+ /// <value>The version from previous run.</value>
+ [XmlIgnore]
+ public Version PreviousVersion { get; set; }
+
+ /// <summary>
+ /// Stringified PreviousVersion to be stored/loaded,
+ /// because System.Version itself isn't xml-serializable
+ /// </summary>
+ /// <value>String value of PreviousVersion</value>
+ public string PreviousVersionStr
+ {
+ get => PreviousVersion?.ToString();
+ set => PreviousVersion = Version.Parse(value);
+ }
+
+ /// <summary>
/// Initializes a new instance of the <see cref="BaseApplicationConfiguration" /> class.
/// </summary>
public BaseApplicationConfiguration()
diff --git a/MediaBrowser.Model/Configuration/DynamicDayOfWeek.cs b/MediaBrowser.Model/Configuration/DynamicDayOfWeek.cs
index 73dda5a77..71b16cfba 100644
--- a/MediaBrowser.Model/Configuration/DynamicDayOfWeek.cs
+++ b/MediaBrowser.Model/Configuration/DynamicDayOfWeek.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Configuration
{
public enum DynamicDayOfWeek
diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs
index 9ae10d980..eaf9c4ecb 100644
--- a/MediaBrowser.Model/Configuration/EncodingOptions.cs
+++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Configuration
{
public class EncodingOptions
@@ -8,12 +10,14 @@ namespace MediaBrowser.Model.Configuration
public bool EnableThrottling { get; set; }
public int ThrottleDelaySeconds { get; set; }
public string HardwareAccelerationType { get; set; }
+
/// <summary>
- /// FFmpeg path as set by the user via the UI
+ /// FFmpeg path as set by the user via the UI.
/// </summary>
public string EncoderAppPath { get; set; }
+
/// <summary>
- /// The current FFmpeg path being used by the system and displayed on the transcode page
+ /// The current FFmpeg path being used by the system and displayed on the transcode page.
/// </summary>
public string EncoderAppPathDisplay { get; set; }
public string VaapiDevice { get; set; }
@@ -29,7 +33,7 @@ namespace MediaBrowser.Model.Configuration
public EncodingOptions()
{
DownMixAudioBoost = 2;
- EnableThrottling = true;
+ EnableThrottling = false;
ThrottleDelaySeconds = 180;
EncodingThreadCount = -1;
// This is a DRM device that is almost guaranteed to be there on every intel platform, plus it's the default one in ffmpeg if you don't specify anything
diff --git a/MediaBrowser.Model/Configuration/ImageOption.cs b/MediaBrowser.Model/Configuration/ImageOption.cs
index 3b985bb1b..2b1268c74 100644
--- a/MediaBrowser.Model/Configuration/ImageOption.cs
+++ b/MediaBrowser.Model/Configuration/ImageOption.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Model.Entities;
namespace MediaBrowser.Model.Configuration
@@ -9,6 +11,7 @@ namespace MediaBrowser.Model.Configuration
/// </summary>
/// <value>The type.</value>
public ImageType Type { get; set; }
+
/// <summary>
/// Gets or sets the limit.
/// </summary>
diff --git a/MediaBrowser.Model/Configuration/ImageSavingConvention.cs b/MediaBrowser.Model/Configuration/ImageSavingConvention.cs
index 7206fa5fc..485a4d2f8 100644
--- a/MediaBrowser.Model/Configuration/ImageSavingConvention.cs
+++ b/MediaBrowser.Model/Configuration/ImageSavingConvention.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Configuration
{
public enum ImageSavingConvention
diff --git a/MediaBrowser.Model/Configuration/LibraryOptions.cs b/MediaBrowser.Model/Configuration/LibraryOptions.cs
index ba33bee87..4342ccd8a 100644
--- a/MediaBrowser.Model/Configuration/LibraryOptions.cs
+++ b/MediaBrowser.Model/Configuration/LibraryOptions.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using MediaBrowser.Model.Entities;
@@ -18,6 +20,7 @@ namespace MediaBrowser.Model.Configuration
public bool ImportMissingEpisodes { get; set; }
public bool EnableAutomaticSeriesGrouping { get; set; }
public bool EnableEmbeddedTitles { get; set; }
+ public bool EnableEmbeddedEpisodeInfos { get; set; }
public int AutomaticRefreshIntervalDays { get; set; }
diff --git a/MediaBrowser.Model/Configuration/MetadataConfiguration.cs b/MediaBrowser.Model/Configuration/MetadataConfiguration.cs
index 87e02d054..706831bdd 100644
--- a/MediaBrowser.Model/Configuration/MetadataConfiguration.cs
+++ b/MediaBrowser.Model/Configuration/MetadataConfiguration.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Configuration
{
public class MetadataConfiguration
diff --git a/MediaBrowser.Model/Configuration/MetadataOptions.cs b/MediaBrowser.Model/Configuration/MetadataOptions.cs
index c095b8cdd..625054b9e 100644
--- a/MediaBrowser.Model/Configuration/MetadataOptions.cs
+++ b/MediaBrowser.Model/Configuration/MetadataOptions.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace MediaBrowser.Model.Configuration
diff --git a/MediaBrowser.Model/Configuration/MetadataPlugin.cs b/MediaBrowser.Model/Configuration/MetadataPlugin.cs
index d6f863e55..c2b47eb9b 100644
--- a/MediaBrowser.Model/Configuration/MetadataPlugin.cs
+++ b/MediaBrowser.Model/Configuration/MetadataPlugin.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Configuration
{
public class MetadataPlugin
diff --git a/MediaBrowser.Model/Configuration/MetadataPluginSummary.cs b/MediaBrowser.Model/Configuration/MetadataPluginSummary.cs
index 0bd20f837..53063810b 100644
--- a/MediaBrowser.Model/Configuration/MetadataPluginSummary.cs
+++ b/MediaBrowser.Model/Configuration/MetadataPluginSummary.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using MediaBrowser.Model.Entities;
diff --git a/MediaBrowser.Model/Configuration/MetadataPluginType.cs b/MediaBrowser.Model/Configuration/MetadataPluginType.cs
index 985107ac9..bff12799f 100644
--- a/MediaBrowser.Model/Configuration/MetadataPluginType.cs
+++ b/MediaBrowser.Model/Configuration/MetadataPluginType.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Configuration
{
/// <summary>
diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
index cf6d9c2f6..dfb563180 100644
--- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs
+++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using MediaBrowser.Model.Dto;
@@ -159,10 +161,10 @@ namespace MediaBrowser.Model.Configuration
public MetadataOptions[] MetadataOptions { get; set; }
- public bool EnableAutomaticRestart { get; set; }
public bool SkipDeserializationForBasicTypes { get; set; }
public string ServerName { get; set; }
+
public string BaseUrl
{
get => _baseUrl;
@@ -235,7 +237,7 @@ namespace MediaBrowser.Model.Configuration
CodecsUsed = Array.Empty<string>();
PathSubstitutions = Array.Empty<PathSubstitution>();
IgnoreVirtualInterfaces = false;
- EnableSimpleArtistDetection = true;
+ EnableSimpleArtistDetection = false;
DisplaySpecialsWithinSeasons = true;
EnableExternalContentInSuggestions = true;
@@ -245,11 +247,10 @@ namespace MediaBrowser.Model.Configuration
PublicHttpsPort = DefaultHttpsPort;
HttpServerPortNumber = DefaultHttpPort;
HttpsPortNumber = DefaultHttpsPort;
- EnableHttps = true;
+ EnableHttps = false;
EnableDashboardResponseCaching = true;
EnableCaseSensitiveItemIds = true;
- EnableAutomaticRestart = true;
AutoRunWebApp = true;
EnableRemoteAccess = true;
diff --git a/MediaBrowser.Model/Configuration/SubtitlePlaybackMode.cs b/MediaBrowser.Model/Configuration/SubtitlePlaybackMode.cs
index fc429934f..f0aa2b98c 100644
--- a/MediaBrowser.Model/Configuration/SubtitlePlaybackMode.cs
+++ b/MediaBrowser.Model/Configuration/SubtitlePlaybackMode.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Configuration
{
public enum SubtitlePlaybackMode
diff --git a/MediaBrowser.Model/Configuration/UnratedItem.cs b/MediaBrowser.Model/Configuration/UnratedItem.cs
index 107b4e520..e1d1a363d 100644
--- a/MediaBrowser.Model/Configuration/UnratedItem.cs
+++ b/MediaBrowser.Model/Configuration/UnratedItem.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Configuration
{
public enum UnratedItem
diff --git a/MediaBrowser.Model/Configuration/UserConfiguration.cs b/MediaBrowser.Model/Configuration/UserConfiguration.cs
index 689459eba..a475c9910 100644
--- a/MediaBrowser.Model/Configuration/UserConfiguration.cs
+++ b/MediaBrowser.Model/Configuration/UserConfiguration.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace MediaBrowser.Model.Configuration
diff --git a/MediaBrowser.Model/Configuration/XbmcMetadataOptions.cs b/MediaBrowser.Model/Configuration/XbmcMetadataOptions.cs
index 19e6be166..d6c1295f4 100644
--- a/MediaBrowser.Model/Configuration/XbmcMetadataOptions.cs
+++ b/MediaBrowser.Model/Configuration/XbmcMetadataOptions.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Configuration
{
public class XbmcMetadataOptions
diff --git a/MediaBrowser.Model/Cryptography/ICryptoProvider.cs b/MediaBrowser.Model/Cryptography/ICryptoProvider.cs
index 2d75c9b3e..656c04f46 100644
--- a/MediaBrowser.Model/Cryptography/ICryptoProvider.cs
+++ b/MediaBrowser.Model/Cryptography/ICryptoProvider.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
namespace MediaBrowser.Model.Cryptography
diff --git a/MediaBrowser.Model/Devices/ContentUploadHistory.cs b/MediaBrowser.Model/Devices/ContentUploadHistory.cs
index 5dd9bf2d0..c493760d5 100644
--- a/MediaBrowser.Model/Devices/ContentUploadHistory.cs
+++ b/MediaBrowser.Model/Devices/ContentUploadHistory.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Devices
{
public class ContentUploadHistory
diff --git a/MediaBrowser.Model/Devices/DeviceInfo.cs b/MediaBrowser.Model/Devices/DeviceInfo.cs
index 214c49e5e..d2563d1d0 100644
--- a/MediaBrowser.Model/Devices/DeviceInfo.cs
+++ b/MediaBrowser.Model/Devices/DeviceInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using MediaBrowser.Model.Session;
@@ -5,6 +7,11 @@ namespace MediaBrowser.Model.Devices
{
public class DeviceInfo
{
+ public DeviceInfo()
+ {
+ Capabilities = new ClientCapabilities();
+ }
+
public string Name { get; set; }
/// <summary>
@@ -12,42 +19,43 @@ namespace MediaBrowser.Model.Devices
/// </summary>
/// <value>The identifier.</value>
public string Id { get; set; }
+
/// <summary>
/// Gets or sets the last name of the user.
/// </summary>
/// <value>The last name of the user.</value>
public string LastUserName { get; set; }
+
/// <summary>
/// Gets or sets the name of the application.
/// </summary>
/// <value>The name of the application.</value>
public string AppName { get; set; }
+
/// <summary>
/// Gets or sets the application version.
/// </summary>
/// <value>The application version.</value>
public string AppVersion { get; set; }
+
/// <summary>
/// Gets or sets the last user identifier.
/// </summary>
/// <value>The last user identifier.</value>
public Guid LastUserId { get; set; }
+
/// <summary>
/// Gets or sets the date last modified.
/// </summary>
/// <value>The date last modified.</value>
public DateTime DateLastActivity { get; set; }
+
/// <summary>
/// Gets or sets the capabilities.
/// </summary>
/// <value>The capabilities.</value>
public ClientCapabilities Capabilities { get; set; }
- public DeviceInfo()
- {
- Capabilities = new ClientCapabilities();
- }
-
public string IconUrl { get; set; }
}
}
diff --git a/MediaBrowser.Model/Devices/DeviceQuery.cs b/MediaBrowser.Model/Devices/DeviceQuery.cs
index 48d7ec142..64e366a56 100644
--- a/MediaBrowser.Model/Devices/DeviceQuery.cs
+++ b/MediaBrowser.Model/Devices/DeviceQuery.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace MediaBrowser.Model.Devices
@@ -9,6 +11,7 @@ namespace MediaBrowser.Model.Devices
/// </summary>
/// <value><c>null</c> if [supports synchronize] contains no value, <c>true</c> if [supports synchronize]; otherwise, <c>false</c>.</value>
public bool? SupportsSync { get; set; }
+
/// <summary>
/// Gets or sets the user identifier.
/// </summary>
diff --git a/MediaBrowser.Model/Devices/DevicesOptions.cs b/MediaBrowser.Model/Devices/DevicesOptions.cs
index 5bbd33b73..02570650e 100644
--- a/MediaBrowser.Model/Devices/DevicesOptions.cs
+++ b/MediaBrowser.Model/Devices/DevicesOptions.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace MediaBrowser.Model.Devices
diff --git a/MediaBrowser.Model/Devices/LocalFileInfo.cs b/MediaBrowser.Model/Devices/LocalFileInfo.cs
index cc5c9250b..63a8dc2aa 100644
--- a/MediaBrowser.Model/Devices/LocalFileInfo.cs
+++ b/MediaBrowser.Model/Devices/LocalFileInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Devices
{
public class LocalFileInfo
diff --git a/MediaBrowser.Model/Diagnostics/IProcess.cs b/MediaBrowser.Model/Diagnostics/IProcess.cs
index cade631c9..514d1e737 100644
--- a/MediaBrowser.Model/Diagnostics/IProcess.cs
+++ b/MediaBrowser.Model/Diagnostics/IProcess.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.IO;
using System.Threading.Tasks;
diff --git a/MediaBrowser.Model/Diagnostics/IProcessFactory.cs b/MediaBrowser.Model/Diagnostics/IProcessFactory.cs
index a11be8f4e..57082acc5 100644
--- a/MediaBrowser.Model/Diagnostics/IProcessFactory.cs
+++ b/MediaBrowser.Model/Diagnostics/IProcessFactory.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Diagnostics
{
public interface IProcessFactory
diff --git a/MediaBrowser.Model/Dlna/AudioOptions.cs b/MediaBrowser.Model/Dlna/AudioOptions.cs
index 6dfe8093e..40081b282 100644
--- a/MediaBrowser.Model/Dlna/AudioOptions.cs
+++ b/MediaBrowser.Model/Dlna/AudioOptions.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using MediaBrowser.Model.Dto;
diff --git a/MediaBrowser.Model/Dlna/CodecProfile.cs b/MediaBrowser.Model/Dlna/CodecProfile.cs
index 9ea248908..756e500dd 100644
--- a/MediaBrowser.Model/Dlna/CodecProfile.cs
+++ b/MediaBrowser.Model/Dlna/CodecProfile.cs
@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+
+using System;
using System.Xml.Serialization;
using MediaBrowser.Model.Extensions;
@@ -20,8 +23,8 @@ namespace MediaBrowser.Model.Dlna
public CodecProfile()
{
- Conditions = new ProfileCondition[] { };
- ApplyConditions = new ProfileCondition[] { };
+ Conditions = Array.Empty<ProfileCondition>();
+ ApplyConditions = Array.Empty<ProfileCondition>();
}
public string[] GetCodecs()
diff --git a/MediaBrowser.Model/Dlna/CodecType.cs b/MediaBrowser.Model/Dlna/CodecType.cs
index d777be4c2..c9f090e4c 100644
--- a/MediaBrowser.Model/Dlna/CodecType.cs
+++ b/MediaBrowser.Model/Dlna/CodecType.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Dlna
{
public enum CodecType
diff --git a/MediaBrowser.Model/Dlna/ConditionProcessor.cs b/MediaBrowser.Model/Dlna/ConditionProcessor.cs
index 3629d1547..7423efaf6 100644
--- a/MediaBrowser.Model/Dlna/ConditionProcessor.cs
+++ b/MediaBrowser.Model/Dlna/ConditionProcessor.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Globalization;
using MediaBrowser.Model.Extensions;
@@ -169,9 +171,9 @@ namespace MediaBrowser.Model.Dlna
return ListHelper.ContainsIgnoreCase(expected.Split('|'), currentValue);
}
case ProfileConditionType.Equals:
- return StringHelper.EqualsIgnoreCase(currentValue, expected);
+ return string.Equals(currentValue, expected, StringComparison.OrdinalIgnoreCase);
case ProfileConditionType.NotEquals:
- return !StringHelper.EqualsIgnoreCase(currentValue, expected);
+ return !string.Equals(currentValue, expected, StringComparison.OrdinalIgnoreCase);
default:
throw new InvalidOperationException("Unexpected ProfileConditionType: " + condition.Condition);
}
diff --git a/MediaBrowser.Model/Dlna/ContainerProfile.cs b/MediaBrowser.Model/Dlna/ContainerProfile.cs
index 073324c26..e6691c513 100644
--- a/MediaBrowser.Model/Dlna/ContainerProfile.cs
+++ b/MediaBrowser.Model/Dlna/ContainerProfile.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Xml.Serialization;
using MediaBrowser.Model.Extensions;
diff --git a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs
index 2333fa7a0..a20f11503 100644
--- a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs
+++ b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Globalization;
diff --git a/MediaBrowser.Model/Dlna/DeviceIdentification.cs b/MediaBrowser.Model/Dlna/DeviceIdentification.cs
index 84573521a..f1699d930 100644
--- a/MediaBrowser.Model/Dlna/DeviceIdentification.cs
+++ b/MediaBrowser.Model/Dlna/DeviceIdentification.cs
@@ -1,3 +1,7 @@
+#pragma warning disable CS1591
+
+using System;
+
namespace MediaBrowser.Model.Dlna
{
public class DeviceIdentification
@@ -7,46 +11,55 @@ namespace MediaBrowser.Model.Dlna
/// </summary>
/// <value>The name of the friendly.</value>
public string FriendlyName { get; set; }
+
/// <summary>
/// Gets or sets the model number.
/// </summary>
/// <value>The model number.</value>
public string ModelNumber { get; set; }
+
/// <summary>
/// Gets or sets the serial number.
/// </summary>
/// <value>The serial number.</value>
public string SerialNumber { get; set; }
+
/// <summary>
/// Gets or sets the name of the model.
/// </summary>
/// <value>The name of the model.</value>
public string ModelName { get; set; }
+
/// <summary>
/// Gets or sets the model description.
/// </summary>
/// <value>The model description.</value>
public string ModelDescription { get; set; }
+
/// <summary>
/// Gets or sets the device description.
/// </summary>
/// <value>The device description.</value>
public string DeviceDescription { get; set; }
+
/// <summary>
/// Gets or sets the model URL.
/// </summary>
/// <value>The model URL.</value>
public string ModelUrl { get; set; }
+
/// <summary>
/// Gets or sets the manufacturer.
/// </summary>
/// <value>The manufacturer.</value>
public string Manufacturer { get; set; }
+
/// <summary>
/// Gets or sets the manufacturer URL.
/// </summary>
/// <value>The manufacturer URL.</value>
public string ManufacturerUrl { get; set; }
+
/// <summary>
/// Gets or sets the headers.
/// </summary>
@@ -55,7 +68,7 @@ namespace MediaBrowser.Model.Dlna
public DeviceIdentification()
{
- Headers = new HttpHeaderInfo[] { };
+ Headers = Array.Empty<HttpHeaderInfo>();
}
}
}
diff --git a/MediaBrowser.Model/Dlna/DeviceProfile.cs b/MediaBrowser.Model/Dlna/DeviceProfile.cs
index 8d8fe9eb5..0cefbbe01 100644
--- a/MediaBrowser.Model/Dlna/DeviceProfile.cs
+++ b/MediaBrowser.Model/Dlna/DeviceProfile.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Xml.Serialization;
using MediaBrowser.Model.Extensions;
@@ -122,7 +124,7 @@ namespace MediaBrowser.Model.Dlna
continue;
}
- if (!StringHelper.EqualsIgnoreCase(container, i.Container))
+ if (!string.Equals(container, i.Container, StringComparison.OrdinalIgnoreCase))
{
continue;
}
@@ -148,7 +150,7 @@ namespace MediaBrowser.Model.Dlna
continue;
}
- if (!StringHelper.EqualsIgnoreCase(container, i.Container))
+ if (!string.Equals(container, i.Container, StringComparison.OrdinalIgnoreCase))
{
continue;
}
@@ -158,7 +160,7 @@ namespace MediaBrowser.Model.Dlna
continue;
}
- if (!StringHelper.EqualsIgnoreCase(videoCodec, i.VideoCodec ?? string.Empty))
+ if (!string.Equals(videoCodec, i.VideoCodec ?? string.Empty, StringComparison.OrdinalIgnoreCase))
{
continue;
}
diff --git a/MediaBrowser.Model/Dlna/DeviceProfileInfo.cs b/MediaBrowser.Model/Dlna/DeviceProfileInfo.cs
index c78f0d9b2..347583965 100644
--- a/MediaBrowser.Model/Dlna/DeviceProfileInfo.cs
+++ b/MediaBrowser.Model/Dlna/DeviceProfileInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Dlna
{
public class DeviceProfileInfo
diff --git a/MediaBrowser.Model/Dlna/DeviceProfileType.cs b/MediaBrowser.Model/Dlna/DeviceProfileType.cs
index 2449fa434..46062abd0 100644
--- a/MediaBrowser.Model/Dlna/DeviceProfileType.cs
+++ b/MediaBrowser.Model/Dlna/DeviceProfileType.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Dlna
{
public enum DeviceProfileType
diff --git a/MediaBrowser.Model/Dlna/DirectPlayProfile.cs b/MediaBrowser.Model/Dlna/DirectPlayProfile.cs
index 5a54847d7..a5947bbf4 100644
--- a/MediaBrowser.Model/Dlna/DirectPlayProfile.cs
+++ b/MediaBrowser.Model/Dlna/DirectPlayProfile.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Xml.Serialization;
namespace MediaBrowser.Model.Dlna
diff --git a/MediaBrowser.Model/Dlna/DlnaFlags.cs b/MediaBrowser.Model/Dlna/DlnaFlags.cs
index d076e73ec..02d9ea9c5 100644
--- a/MediaBrowser.Model/Dlna/DlnaFlags.cs
+++ b/MediaBrowser.Model/Dlna/DlnaFlags.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace MediaBrowser.Model.Dlna
diff --git a/MediaBrowser.Model/Dlna/DlnaMaps.cs b/MediaBrowser.Model/Dlna/DlnaMaps.cs
index 880d05724..052b4b78b 100644
--- a/MediaBrowser.Model/Dlna/DlnaMaps.cs
+++ b/MediaBrowser.Model/Dlna/DlnaMaps.cs
@@ -1,6 +1,8 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Dlna
{
- public class DlnaMaps
+ public static class DlnaMaps
{
private static readonly string DefaultStreaming =
FlagsToString(DlnaFlags.StreamingTransferMode |
diff --git a/MediaBrowser.Model/Dlna/DlnaProfileType.cs b/MediaBrowser.Model/Dlna/DlnaProfileType.cs
index 6a23bbb04..e30ed0f3c 100644
--- a/MediaBrowser.Model/Dlna/DlnaProfileType.cs
+++ b/MediaBrowser.Model/Dlna/DlnaProfileType.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Dlna
{
public enum DlnaProfileType
diff --git a/MediaBrowser.Model/Dlna/EncodingContext.cs b/MediaBrowser.Model/Dlna/EncodingContext.cs
index 7352bdd19..79ca6366d 100644
--- a/MediaBrowser.Model/Dlna/EncodingContext.cs
+++ b/MediaBrowser.Model/Dlna/EncodingContext.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Dlna
{
public enum EncodingContext
diff --git a/MediaBrowser.Model/Dlna/HeaderMatchType.cs b/MediaBrowser.Model/Dlna/HeaderMatchType.cs
index b0a1438f6..2a9abb20e 100644
--- a/MediaBrowser.Model/Dlna/HeaderMatchType.cs
+++ b/MediaBrowser.Model/Dlna/HeaderMatchType.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Dlna
{
public enum HeaderMatchType
diff --git a/MediaBrowser.Model/Dlna/HttpHeaderInfo.cs b/MediaBrowser.Model/Dlna/HttpHeaderInfo.cs
index d15727504..f23a24084 100644
--- a/MediaBrowser.Model/Dlna/HttpHeaderInfo.cs
+++ b/MediaBrowser.Model/Dlna/HttpHeaderInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Xml.Serialization;
namespace MediaBrowser.Model.Dlna
diff --git a/MediaBrowser.Model/Dlna/IDeviceDiscovery.cs b/MediaBrowser.Model/Dlna/IDeviceDiscovery.cs
index 3de3fe761..76c9a4b04 100644
--- a/MediaBrowser.Model/Dlna/IDeviceDiscovery.cs
+++ b/MediaBrowser.Model/Dlna/IDeviceDiscovery.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using MediaBrowser.Model.Events;
diff --git a/MediaBrowser.Model/Dlna/ITranscoderSupport.cs b/MediaBrowser.Model/Dlna/ITranscoderSupport.cs
index c0ff54c3f..7e35cc85b 100644
--- a/MediaBrowser.Model/Dlna/ITranscoderSupport.cs
+++ b/MediaBrowser.Model/Dlna/ITranscoderSupport.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Dlna
{
public interface ITranscoderSupport
diff --git a/MediaBrowser.Model/Dlna/MediaFormatProfile.cs b/MediaBrowser.Model/Dlna/MediaFormatProfile.cs
index 7c56fc5f4..20e05b8a9 100644
--- a/MediaBrowser.Model/Dlna/MediaFormatProfile.cs
+++ b/MediaBrowser.Model/Dlna/MediaFormatProfile.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Dlna
{
public enum MediaFormatProfile
diff --git a/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs b/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs
index 672784589..4cd318abb 100644
--- a/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs
+++ b/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs
@@ -1,7 +1,8 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Linq;
-using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.MediaInfo;
namespace MediaBrowser.Model.Dlna
@@ -17,53 +18,53 @@ namespace MediaBrowser.Model.Dlna
private MediaFormatProfile[] ResolveVideoFormatInternal(string container, string videoCodec, string audioCodec, int? width, int? height, TransportStreamTimestamp timestampType)
{
- if (StringHelper.EqualsIgnoreCase(container, "asf"))
+ if (string.Equals(container, "asf", StringComparison.OrdinalIgnoreCase))
{
MediaFormatProfile? val = ResolveVideoASFFormat(videoCodec, audioCodec, width, height);
return val.HasValue ? new MediaFormatProfile[] { val.Value } : new MediaFormatProfile[] { };
}
- if (StringHelper.EqualsIgnoreCase(container, "mp4"))
+ if (string.Equals(container, "mp4", StringComparison.OrdinalIgnoreCase))
{
MediaFormatProfile? val = ResolveVideoMP4Format(videoCodec, audioCodec, width, height);
return val.HasValue ? new MediaFormatProfile[] { val.Value } : new MediaFormatProfile[] { };
}
- if (StringHelper.EqualsIgnoreCase(container, "avi"))
+ if (string.Equals(container, "avi", StringComparison.OrdinalIgnoreCase))
return new MediaFormatProfile[] { MediaFormatProfile.AVI };
- if (StringHelper.EqualsIgnoreCase(container, "mkv"))
+ if (string.Equals(container, "mkv", StringComparison.OrdinalIgnoreCase))
return new MediaFormatProfile[] { MediaFormatProfile.MATROSKA };
- if (StringHelper.EqualsIgnoreCase(container, "mpeg2ps") ||
- StringHelper.EqualsIgnoreCase(container, "ts"))
+ if (string.Equals(container, "mpeg2ps", StringComparison.OrdinalIgnoreCase) ||
+ string.Equals(container, "ts", StringComparison.OrdinalIgnoreCase))
return new MediaFormatProfile[] { MediaFormatProfile.MPEG_PS_NTSC, MediaFormatProfile.MPEG_PS_PAL };
- if (StringHelper.EqualsIgnoreCase(container, "mpeg1video"))
+ if (string.Equals(container, "mpeg1video", StringComparison.OrdinalIgnoreCase))
return new MediaFormatProfile[] { MediaFormatProfile.MPEG1 };
- if (StringHelper.EqualsIgnoreCase(container, "mpeg2ts") ||
- StringHelper.EqualsIgnoreCase(container, "mpegts") ||
- StringHelper.EqualsIgnoreCase(container, "m2ts"))
+ if (string.Equals(container, "mpeg2ts", StringComparison.OrdinalIgnoreCase) ||
+ string.Equals(container, "mpegts", StringComparison.OrdinalIgnoreCase) ||
+ string.Equals(container, "m2ts", StringComparison.OrdinalIgnoreCase))
{
return ResolveVideoMPEG2TSFormat(videoCodec, audioCodec, width, height, timestampType);
}
- if (StringHelper.EqualsIgnoreCase(container, "flv"))
+ if (string.Equals(container, "flv", StringComparison.OrdinalIgnoreCase))
return new MediaFormatProfile[] { MediaFormatProfile.FLV };
- if (StringHelper.EqualsIgnoreCase(container, "wtv"))
+ if (string.Equals(container, "wtv", StringComparison.OrdinalIgnoreCase))
return new MediaFormatProfile[] { MediaFormatProfile.WTV };
- if (StringHelper.EqualsIgnoreCase(container, "3gp"))
+ if (string.Equals(container, "3gp", StringComparison.OrdinalIgnoreCase))
{
MediaFormatProfile? val = ResolveVideo3GPFormat(videoCodec, audioCodec);
return val.HasValue ? new MediaFormatProfile[] { val.Value } : new MediaFormatProfile[] { };
}
- if (StringHelper.EqualsIgnoreCase(container, "ogv") || StringHelper.EqualsIgnoreCase(container, "ogg"))
+ if (string.Equals(container, "ogv", StringComparison.OrdinalIgnoreCase) || string.Equals(container, "ogg", StringComparison.OrdinalIgnoreCase))
return new MediaFormatProfile[] { MediaFormatProfile.OGV };
return new MediaFormatProfile[] { };
@@ -89,7 +90,7 @@ namespace MediaBrowser.Model.Dlna
resolution = "H";
}
- if (StringHelper.EqualsIgnoreCase(videoCodec, "mpeg2video"))
+ if (string.Equals(videoCodec, "mpeg2video", StringComparison.OrdinalIgnoreCase))
{
var list = new List<MediaFormatProfile>();
@@ -97,18 +98,18 @@ namespace MediaBrowser.Model.Dlna
list.Add(ValueOf("MPEG_TS_SD_EU" + suffix));
list.Add(ValueOf("MPEG_TS_SD_KO" + suffix));
- if ((timestampType == TransportStreamTimestamp.Valid) && StringHelper.EqualsIgnoreCase(audioCodec, "aac"))
+ if ((timestampType == TransportStreamTimestamp.Valid) && string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase))
{
list.Add(MediaFormatProfile.MPEG_TS_JP_T);
}
return list.ToArray();
}
- if (StringHelper.EqualsIgnoreCase(videoCodec, "h264"))
+ if (string.Equals(videoCodec, "h264", StringComparison.OrdinalIgnoreCase))
{
- if (StringHelper.EqualsIgnoreCase(audioCodec, "lpcm"))
+ if (string.Equals(audioCodec, "lpcm", StringComparison.OrdinalIgnoreCase))
return new MediaFormatProfile[] { MediaFormatProfile.AVC_TS_HD_50_LPCM_T };
- if (StringHelper.EqualsIgnoreCase(audioCodec, "dts"))
+ if (string.Equals(audioCodec, "dts", StringComparison.OrdinalIgnoreCase))
{
if (timestampType == TransportStreamTimestamp.None)
{
@@ -117,7 +118,7 @@ namespace MediaBrowser.Model.Dlna
return new MediaFormatProfile[] { MediaFormatProfile.AVC_TS_HD_DTS_T };
}
- if (StringHelper.EqualsIgnoreCase(audioCodec, "mp2"))
+ if (string.Equals(audioCodec, "mp2", StringComparison.OrdinalIgnoreCase))
{
if (timestampType == TransportStreamTimestamp.None)
{
@@ -127,19 +128,19 @@ namespace MediaBrowser.Model.Dlna
return new MediaFormatProfile[] { ValueOf(string.Format("AVC_TS_HP_{0}D_MPEG1_L2_T", resolution)) };
}
- if (StringHelper.EqualsIgnoreCase(audioCodec, "aac"))
+ if (string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase))
return new MediaFormatProfile[] { ValueOf(string.Format("AVC_TS_MP_{0}D_AAC_MULT5{1}", resolution, suffix)) };
- if (StringHelper.EqualsIgnoreCase(audioCodec, "mp3"))
+ if (string.Equals(audioCodec, "mp3", StringComparison.OrdinalIgnoreCase))
return new MediaFormatProfile[] { ValueOf(string.Format("AVC_TS_MP_{0}D_MPEG1_L3{1}", resolution, suffix)) };
if (string.IsNullOrEmpty(audioCodec) ||
- StringHelper.EqualsIgnoreCase(audioCodec, "ac3"))
+ string.Equals(audioCodec, "ac3", StringComparison.OrdinalIgnoreCase))
return new MediaFormatProfile[] { ValueOf(string.Format("AVC_TS_MP_{0}D_AC3{1}", resolution, suffix)) };
}
- else if (StringHelper.EqualsIgnoreCase(videoCodec, "vc1"))
+ else if (string.Equals(videoCodec, "vc1", StringComparison.OrdinalIgnoreCase))
{
- if (string.IsNullOrEmpty(audioCodec) || StringHelper.EqualsIgnoreCase(audioCodec, "ac3"))
+ if (string.IsNullOrEmpty(audioCodec) || string.Equals(audioCodec, "ac3", StringComparison.OrdinalIgnoreCase))
{
if ((width.HasValue && width.Value > 720) || (height.HasValue && height.Value > 576))
{
@@ -147,23 +148,23 @@ namespace MediaBrowser.Model.Dlna
}
return new MediaFormatProfile[] { MediaFormatProfile.VC1_TS_AP_L1_AC3_ISO };
}
- if (StringHelper.EqualsIgnoreCase(audioCodec, "dts"))
+ if (string.Equals(audioCodec, "dts", StringComparison.OrdinalIgnoreCase))
{
- suffix = StringHelper.EqualsIgnoreCase(suffix, "_ISO") ? suffix : "_T";
+ suffix = string.Equals(suffix, "_ISO", StringComparison.OrdinalIgnoreCase) ? suffix : "_T";
return new MediaFormatProfile[] { ValueOf(string.Format("VC1_TS_HD_DTS{0}", suffix)) };
}
}
- else if (StringHelper.EqualsIgnoreCase(videoCodec, "mpeg4") || StringHelper.EqualsIgnoreCase(videoCodec, "msmpeg4"))
+ else if (string.Equals(videoCodec, "mpeg4", StringComparison.OrdinalIgnoreCase) || string.Equals(videoCodec, "msmpeg4", StringComparison.OrdinalIgnoreCase))
{
- if (StringHelper.EqualsIgnoreCase(audioCodec, "aac"))
+ if (string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase))
return new MediaFormatProfile[] { ValueOf(string.Format("MPEG4_P2_TS_ASP_AAC{0}", suffix)) };
- if (StringHelper.EqualsIgnoreCase(audioCodec, "mp3"))
+ if (string.Equals(audioCodec, "mp3", StringComparison.OrdinalIgnoreCase))
return new MediaFormatProfile[] { ValueOf(string.Format("MPEG4_P2_TS_ASP_MPEG1_L3{0}", suffix)) };
- if (StringHelper.EqualsIgnoreCase(audioCodec, "mp2"))
+ if (string.Equals(audioCodec, "mp2", StringComparison.OrdinalIgnoreCase))
return new MediaFormatProfile[] { ValueOf(string.Format("MPEG4_P2_TS_ASP_MPEG2_L2{0}", suffix)) };
- if (StringHelper.EqualsIgnoreCase(audioCodec, "ac3"))
+ if (string.Equals(audioCodec, "ac3", StringComparison.OrdinalIgnoreCase))
return new MediaFormatProfile[] { ValueOf(string.Format("MPEG4_P2_TS_ASP_AC3{0}", suffix)) };
}
@@ -177,16 +178,16 @@ namespace MediaBrowser.Model.Dlna
private MediaFormatProfile? ResolveVideoMP4Format(string videoCodec, string audioCodec, int? width, int? height)
{
- if (StringHelper.EqualsIgnoreCase(videoCodec, "h264"))
+ if (string.Equals(videoCodec, "h264", StringComparison.OrdinalIgnoreCase))
{
- if (StringHelper.EqualsIgnoreCase(audioCodec, "lpcm"))
+ if (string.Equals(audioCodec, "lpcm", StringComparison.OrdinalIgnoreCase))
return MediaFormatProfile.AVC_MP4_LPCM;
if (string.IsNullOrEmpty(audioCodec) ||
- StringHelper.EqualsIgnoreCase(audioCodec, "ac3"))
+ string.Equals(audioCodec, "ac3", StringComparison.OrdinalIgnoreCase))
{
return MediaFormatProfile.AVC_MP4_MP_SD_AC3;
}
- if (StringHelper.EqualsIgnoreCase(audioCodec, "mp3"))
+ if (string.Equals(audioCodec, "mp3", StringComparison.OrdinalIgnoreCase))
{
return MediaFormatProfile.AVC_MP4_MP_SD_MPEG1_L3;
}
@@ -194,41 +195,41 @@ namespace MediaBrowser.Model.Dlna
{
if ((width.Value <= 720) && (height.Value <= 576))
{
- if (StringHelper.EqualsIgnoreCase(audioCodec, "aac"))
+ if (string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase))
return MediaFormatProfile.AVC_MP4_MP_SD_AAC_MULT5;
}
else if ((width.Value <= 1280) && (height.Value <= 720))
{
- if (StringHelper.EqualsIgnoreCase(audioCodec, "aac"))
+ if (string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase))
return MediaFormatProfile.AVC_MP4_MP_HD_720p_AAC;
}
else if ((width.Value <= 1920) && (height.Value <= 1080))
{
- if (StringHelper.EqualsIgnoreCase(audioCodec, "aac"))
+ if (string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase))
{
return MediaFormatProfile.AVC_MP4_MP_HD_1080i_AAC;
}
}
}
}
- else if (StringHelper.EqualsIgnoreCase(videoCodec, "mpeg4") ||
- StringHelper.EqualsIgnoreCase(videoCodec, "msmpeg4"))
+ else if (string.Equals(videoCodec, "mpeg4", StringComparison.OrdinalIgnoreCase) ||
+ string.Equals(videoCodec, "msmpeg4", StringComparison.OrdinalIgnoreCase))
{
if (width.HasValue && height.HasValue && width.Value <= 720 && height.Value <= 576)
{
- if (string.IsNullOrEmpty(audioCodec) || StringHelper.EqualsIgnoreCase(audioCodec, "aac"))
+ if (string.IsNullOrEmpty(audioCodec) || string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase))
return MediaFormatProfile.MPEG4_P2_MP4_ASP_AAC;
- if (StringHelper.EqualsIgnoreCase(audioCodec, "ac3") || StringHelper.EqualsIgnoreCase(audioCodec, "mp3"))
+ if (string.Equals(audioCodec, "ac3", StringComparison.OrdinalIgnoreCase) || string.Equals(audioCodec, "mp3", StringComparison.OrdinalIgnoreCase))
{
return MediaFormatProfile.MPEG4_P2_MP4_NDSD;
}
}
- else if (string.IsNullOrEmpty(audioCodec) || StringHelper.EqualsIgnoreCase(audioCodec, "aac"))
+ else if (string.IsNullOrEmpty(audioCodec) || string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase))
{
return MediaFormatProfile.MPEG4_P2_MP4_SP_L6_AAC;
}
}
- else if (StringHelper.EqualsIgnoreCase(videoCodec, "h263") && StringHelper.EqualsIgnoreCase(audioCodec, "aac"))
+ else if (string.Equals(videoCodec, "h263", StringComparison.OrdinalIgnoreCase) && string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase))
{
return MediaFormatProfile.MPEG4_H263_MP4_P0_L10_AAC;
}
@@ -238,20 +239,20 @@ namespace MediaBrowser.Model.Dlna
private MediaFormatProfile? ResolveVideo3GPFormat(string videoCodec, string audioCodec)
{
- if (StringHelper.EqualsIgnoreCase(videoCodec, "h264"))
+ if (string.Equals(videoCodec, "h264", StringComparison.OrdinalIgnoreCase))
{
- if (string.IsNullOrEmpty(audioCodec) || StringHelper.EqualsIgnoreCase(audioCodec, "aac"))
+ if (string.IsNullOrEmpty(audioCodec) || string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase))
return MediaFormatProfile.AVC_3GPP_BL_QCIF15_AAC;
}
- else if (StringHelper.EqualsIgnoreCase(videoCodec, "mpeg4") ||
- StringHelper.EqualsIgnoreCase(videoCodec, "msmpeg4"))
+ else if (string.Equals(videoCodec, "mpeg4", StringComparison.OrdinalIgnoreCase) ||
+ string.Equals(videoCodec, "msmpeg4", StringComparison.OrdinalIgnoreCase))
{
- if (string.IsNullOrEmpty(audioCodec) || StringHelper.EqualsIgnoreCase(audioCodec, "wma"))
+ if (string.IsNullOrEmpty(audioCodec) || string.Equals(audioCodec, "wma", StringComparison.OrdinalIgnoreCase))
return MediaFormatProfile.MPEG4_P2_3GPP_SP_L0B_AAC;
- if (StringHelper.EqualsIgnoreCase(audioCodec, "amrnb"))
+ if (string.Equals(audioCodec, "amrnb", StringComparison.OrdinalIgnoreCase))
return MediaFormatProfile.MPEG4_P2_3GPP_SP_L0B_AMR;
}
- else if (StringHelper.EqualsIgnoreCase(videoCodec, "h263") && StringHelper.EqualsIgnoreCase(audioCodec, "amrnb"))
+ else if (string.Equals(videoCodec, "h263", StringComparison.OrdinalIgnoreCase) && string.Equals(audioCodec, "amrnb", StringComparison.OrdinalIgnoreCase))
{
return MediaFormatProfile.MPEG4_H263_3GPP_P0_L10_AMR;
}
@@ -261,15 +262,15 @@ namespace MediaBrowser.Model.Dlna
private MediaFormatProfile? ResolveVideoASFFormat(string videoCodec, string audioCodec, int? width, int? height)
{
- if (StringHelper.EqualsIgnoreCase(videoCodec, "wmv") &&
- (string.IsNullOrEmpty(audioCodec) || StringHelper.EqualsIgnoreCase(audioCodec, "wma") || StringHelper.EqualsIgnoreCase(videoCodec, "wmapro")))
+ if (string.Equals(videoCodec, "wmv", StringComparison.OrdinalIgnoreCase) &&
+ (string.IsNullOrEmpty(audioCodec) || string.Equals(audioCodec, "wma", StringComparison.OrdinalIgnoreCase) || string.Equals(videoCodec, "wmapro", StringComparison.OrdinalIgnoreCase)))
{
if (width.HasValue && height.HasValue)
{
if ((width.Value <= 720) && (height.Value <= 576))
{
- if (string.IsNullOrEmpty(audioCodec) || StringHelper.EqualsIgnoreCase(audioCodec, "wma"))
+ if (string.IsNullOrEmpty(audioCodec) || string.Equals(audioCodec, "wma", StringComparison.OrdinalIgnoreCase))
{
return MediaFormatProfile.WMVMED_FULL;
}
@@ -277,14 +278,14 @@ namespace MediaBrowser.Model.Dlna
}
}
- if (string.IsNullOrEmpty(audioCodec) || StringHelper.EqualsIgnoreCase(audioCodec, "wma"))
+ if (string.IsNullOrEmpty(audioCodec) || string.Equals(audioCodec, "wma", StringComparison.OrdinalIgnoreCase))
{
return MediaFormatProfile.WMVHIGH_FULL;
}
return MediaFormatProfile.WMVHIGH_PRO;
}
- if (StringHelper.EqualsIgnoreCase(videoCodec, "vc1"))
+ if (string.Equals(videoCodec, "vc1", StringComparison.OrdinalIgnoreCase))
{
if (width.HasValue && height.HasValue)
{
@@ -296,7 +297,7 @@ namespace MediaBrowser.Model.Dlna
return MediaFormatProfile.VC1_ASF_AP_L3_WMA;
}
}
- else if (StringHelper.EqualsIgnoreCase(videoCodec, "mpeg2video"))
+ else if (string.Equals(videoCodec, "mpeg2video", StringComparison.OrdinalIgnoreCase))
{
return MediaFormatProfile.DVR_MS;
}
@@ -306,27 +307,27 @@ namespace MediaBrowser.Model.Dlna
public MediaFormatProfile? ResolveAudioFormat(string container, int? bitrate, int? frequency, int? channels)
{
- if (StringHelper.EqualsIgnoreCase(container, "asf"))
+ if (string.Equals(container, "asf", StringComparison.OrdinalIgnoreCase))
return ResolveAudioASFFormat(bitrate);
- if (StringHelper.EqualsIgnoreCase(container, "mp3"))
+ if (string.Equals(container, "mp3", StringComparison.OrdinalIgnoreCase))
return MediaFormatProfile.MP3;
- if (StringHelper.EqualsIgnoreCase(container, "lpcm"))
+ if (string.Equals(container, "lpcm", StringComparison.OrdinalIgnoreCase))
return ResolveAudioLPCMFormat(frequency, channels);
- if (StringHelper.EqualsIgnoreCase(container, "mp4") ||
- StringHelper.EqualsIgnoreCase(container, "aac"))
+ if (string.Equals(container, "mp4", StringComparison.OrdinalIgnoreCase) ||
+ string.Equals(container, "aac", StringComparison.OrdinalIgnoreCase))
return ResolveAudioMP4Format(bitrate);
- if (StringHelper.EqualsIgnoreCase(container, "adts"))
+ if (string.Equals(container, "adts", StringComparison.OrdinalIgnoreCase))
return ResolveAudioADTSFormat(bitrate);
- if (StringHelper.EqualsIgnoreCase(container, "flac"))
+ if (string.Equals(container, "flac", StringComparison.OrdinalIgnoreCase))
return MediaFormatProfile.FLAC;
- if (StringHelper.EqualsIgnoreCase(container, "oga") ||
- StringHelper.EqualsIgnoreCase(container, "ogg"))
+ if (string.Equals(container, "oga", StringComparison.OrdinalIgnoreCase) ||
+ string.Equals(container, "ogg", StringComparison.OrdinalIgnoreCase))
return MediaFormatProfile.OGG;
return null;
@@ -388,17 +389,17 @@ namespace MediaBrowser.Model.Dlna
public MediaFormatProfile? ResolveImageFormat(string container, int? width, int? height)
{
- if (StringHelper.EqualsIgnoreCase(container, "jpeg") ||
- StringHelper.EqualsIgnoreCase(container, "jpg"))
+ if (string.Equals(container, "jpeg", StringComparison.OrdinalIgnoreCase) ||
+ string.Equals(container, "jpg", StringComparison.OrdinalIgnoreCase))
return ResolveImageJPGFormat(width, height);
- if (StringHelper.EqualsIgnoreCase(container, "png"))
+ if (string.Equals(container, "png", StringComparison.OrdinalIgnoreCase))
return ResolveImagePNGFormat(width, height);
- if (StringHelper.EqualsIgnoreCase(container, "gif"))
+ if (string.Equals(container, "gif", StringComparison.OrdinalIgnoreCase))
return MediaFormatProfile.GIF_LRG;
- if (StringHelper.EqualsIgnoreCase(container, "raw"))
+ if (string.Equals(container, "raw", StringComparison.OrdinalIgnoreCase))
return MediaFormatProfile.RAW;
return null;
diff --git a/MediaBrowser.Model/Dlna/PlaybackErrorCode.cs b/MediaBrowser.Model/Dlna/PlaybackErrorCode.cs
index 5080bc286..300fab5c5 100644
--- a/MediaBrowser.Model/Dlna/PlaybackErrorCode.cs
+++ b/MediaBrowser.Model/Dlna/PlaybackErrorCode.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Dlna
{
public enum PlaybackErrorCode
diff --git a/MediaBrowser.Model/Dlna/ProfileCondition.cs b/MediaBrowser.Model/Dlna/ProfileCondition.cs
index b83566f6e..2021038d8 100644
--- a/MediaBrowser.Model/Dlna/ProfileCondition.cs
+++ b/MediaBrowser.Model/Dlna/ProfileCondition.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Xml.Serialization;
namespace MediaBrowser.Model.Dlna
diff --git a/MediaBrowser.Model/Dlna/ProfileConditionType.cs b/MediaBrowser.Model/Dlna/ProfileConditionType.cs
index 262841262..fbf133857 100644
--- a/MediaBrowser.Model/Dlna/ProfileConditionType.cs
+++ b/MediaBrowser.Model/Dlna/ProfileConditionType.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Dlna
{
public enum ProfileConditionType
diff --git a/MediaBrowser.Model/Dlna/ProfileConditionValue.cs b/MediaBrowser.Model/Dlna/ProfileConditionValue.cs
index bae46bdcf..eb81fde75 100644
--- a/MediaBrowser.Model/Dlna/ProfileConditionValue.cs
+++ b/MediaBrowser.Model/Dlna/ProfileConditionValue.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Dlna
{
public enum ProfileConditionValue
diff --git a/MediaBrowser.Model/Dlna/ResolutionConfiguration.cs b/MediaBrowser.Model/Dlna/ResolutionConfiguration.cs
index 6b1f85440..c26eeec77 100644
--- a/MediaBrowser.Model/Dlna/ResolutionConfiguration.cs
+++ b/MediaBrowser.Model/Dlna/ResolutionConfiguration.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Dlna
{
public class ResolutionConfiguration
diff --git a/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs b/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs
index cf92633c3..6a58b4adc 100644
--- a/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs
+++ b/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using MediaBrowser.Model.Extensions;
@@ -76,9 +78,9 @@ namespace MediaBrowser.Model.Dlna
private static double GetVideoBitrateScaleFactor(string codec)
{
- if (StringHelper.EqualsIgnoreCase(codec, "h265") ||
- StringHelper.EqualsIgnoreCase(codec, "hevc") ||
- StringHelper.EqualsIgnoreCase(codec, "vp9"))
+ if (string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase) ||
+ string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase) ||
+ string.Equals(codec, "vp9", StringComparison.OrdinalIgnoreCase))
{
return .5;
}
diff --git a/MediaBrowser.Model/Dlna/ResolutionOptions.cs b/MediaBrowser.Model/Dlna/ResolutionOptions.cs
index 30c078b55..5ea0252cb 100644
--- a/MediaBrowser.Model/Dlna/ResolutionOptions.cs
+++ b/MediaBrowser.Model/Dlna/ResolutionOptions.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Dlna
{
public class ResolutionOptions
diff --git a/MediaBrowser.Model/Dlna/ResponseProfile.cs b/MediaBrowser.Model/Dlna/ResponseProfile.cs
index 8c6b0806f..c264cb936 100644
--- a/MediaBrowser.Model/Dlna/ResponseProfile.cs
+++ b/MediaBrowser.Model/Dlna/ResponseProfile.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Xml.Serialization;
namespace MediaBrowser.Model.Dlna
diff --git a/MediaBrowser.Model/Dlna/SearchCriteria.cs b/MediaBrowser.Model/Dlna/SearchCriteria.cs
index 4f47c2821..dc6d201ae 100644
--- a/MediaBrowser.Model/Dlna/SearchCriteria.cs
+++ b/MediaBrowser.Model/Dlna/SearchCriteria.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Text.RegularExpressions;
using MediaBrowser.Model.Extensions;
@@ -48,22 +50,22 @@ namespace MediaBrowser.Model.Dlna
if (subFactors.Length == 3)
{
- if (StringHelper.EqualsIgnoreCase("upnp:class", subFactors[0]) &&
- (StringHelper.EqualsIgnoreCase("=", subFactors[1]) || StringHelper.EqualsIgnoreCase("derivedfrom", subFactors[1])))
+ if (string.Equals("upnp:class", subFactors[0], StringComparison.OrdinalIgnoreCase) &&
+ (string.Equals("=", subFactors[1]) || string.Equals("derivedfrom", subFactors[1], StringComparison.OrdinalIgnoreCase)))
{
- if (StringHelper.EqualsIgnoreCase("\"object.item.imageItem\"", subFactors[2]) || StringHelper.EqualsIgnoreCase("\"object.item.imageItem.photo\"", subFactors[2]))
+ if (string.Equals("\"object.item.imageItem\"", subFactors[2]) || string.Equals("\"object.item.imageItem.photo\"", subFactors[2], StringComparison.OrdinalIgnoreCase))
{
SearchType = SearchType.Image;
}
- else if (StringHelper.EqualsIgnoreCase("\"object.item.videoItem\"", subFactors[2]))
+ else if (string.Equals("\"object.item.videoItem\"", subFactors[2], StringComparison.OrdinalIgnoreCase))
{
SearchType = SearchType.Video;
}
- else if (StringHelper.EqualsIgnoreCase("\"object.container.playlistContainer\"", subFactors[2]))
+ else if (string.Equals("\"object.container.playlistContainer\"", subFactors[2], StringComparison.OrdinalIgnoreCase))
{
SearchType = SearchType.Playlist;
}
- else if (StringHelper.EqualsIgnoreCase("\"object.container.album.musicAlbum\"", subFactors[2]))
+ else if (string.Equals("\"object.container.album.musicAlbum\"", subFactors[2], StringComparison.OrdinalIgnoreCase))
{
SearchType = SearchType.MusicAlbum;
}
diff --git a/MediaBrowser.Model/Dlna/SearchType.cs b/MediaBrowser.Model/Dlna/SearchType.cs
index 05c59f5de..8bc7c5249 100644
--- a/MediaBrowser.Model/Dlna/SearchType.cs
+++ b/MediaBrowser.Model/Dlna/SearchType.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Dlna
{
public enum SearchType
diff --git a/MediaBrowser.Model/Dlna/SortCriteria.cs b/MediaBrowser.Model/Dlna/SortCriteria.cs
index b5c1ac408..3f8985fdc 100644
--- a/MediaBrowser.Model/Dlna/SortCriteria.cs
+++ b/MediaBrowser.Model/Dlna/SortCriteria.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Model.Entities;
namespace MediaBrowser.Model.Dlna
diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs
index 586e322e4..58755b171 100644
--- a/MediaBrowser.Model/Dlna/StreamBuilder.cs
+++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs
@@ -1,10 +1,11 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Session;
using Microsoft.Extensions.Logging;
@@ -22,7 +23,7 @@ namespace MediaBrowser.Model.Dlna
_logger = logger;
}
- public StreamBuilder(ILogger logger)
+ public StreamBuilder(ILogger<StreamBuilder> logger)
: this(new FullTranscoderSupport(), logger)
{
}
@@ -35,7 +36,7 @@ namespace MediaBrowser.Model.Dlna
foreach (MediaSourceInfo i in options.MediaSources)
{
if (string.IsNullOrEmpty(options.MediaSourceId) ||
- StringHelper.EqualsIgnoreCase(i.Id, options.MediaSourceId))
+ string.Equals(i.Id, options.MediaSourceId, StringComparison.OrdinalIgnoreCase))
{
mediaSources.Add(i);
}
@@ -68,7 +69,7 @@ namespace MediaBrowser.Model.Dlna
foreach (MediaSourceInfo i in options.MediaSources)
{
if (string.IsNullOrEmpty(options.MediaSourceId) ||
- StringHelper.EqualsIgnoreCase(i.Id, options.MediaSourceId))
+ string.Equals(i.Id, options.MediaSourceId, StringComparison.OrdinalIgnoreCase))
{
mediaSources.Add(i);
}
@@ -582,7 +583,7 @@ namespace MediaBrowser.Model.Dlna
{
foreach (var profile in subtitleProfiles)
{
- if (profile.Method == SubtitleDeliveryMethod.External && StringHelper.EqualsIgnoreCase(profile.Format, stream.Codec))
+ if (profile.Method == SubtitleDeliveryMethod.External && string.Equals(profile.Format, stream.Codec, StringComparison.OrdinalIgnoreCase))
{
return stream.Index;
}
@@ -1198,7 +1199,7 @@ namespace MediaBrowser.Model.Dlna
continue;
}
- if (subtitleStream.IsTextSubtitleStream == MediaStream.IsTextFormat(profile.Format) && StringHelper.EqualsIgnoreCase(profile.Format, subtitleStream.Codec))
+ if (subtitleStream.IsTextSubtitleStream == MediaStream.IsTextFormat(profile.Format) && string.Equals(profile.Format, subtitleStream.Codec, StringComparison.OrdinalIgnoreCase))
{
return profile;
}
@@ -1292,7 +1293,7 @@ namespace MediaBrowser.Model.Dlna
if ((profile.Method == SubtitleDeliveryMethod.External && subtitleStream.IsTextSubtitleStream == MediaStream.IsTextFormat(profile.Format)) ||
(profile.Method == SubtitleDeliveryMethod.Hls && subtitleStream.IsTextSubtitleStream))
{
- bool requiresConversion = !StringHelper.EqualsIgnoreCase(subtitleStream.Codec, profile.Format);
+ bool requiresConversion = !string.Equals(subtitleStream.Codec, profile.Format, StringComparison.OrdinalIgnoreCase);
if (!requiresConversion)
{
diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs
index 10efb9b38..c9fe679e1 100644
--- a/MediaBrowser.Model/Dlna/StreamInfo.cs
+++ b/MediaBrowser.Model/Dlna/StreamInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -5,7 +7,6 @@ using System.Linq;
using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Session;
@@ -153,18 +154,18 @@ namespace MediaBrowser.Model.Dlna
}
// Try to keep the url clean by omitting defaults
- if (StringHelper.EqualsIgnoreCase(pair.Name, "StartTimeTicks") &&
- StringHelper.EqualsIgnoreCase(pair.Value, "0"))
+ if (string.Equals(pair.Name, "StartTimeTicks", StringComparison.OrdinalIgnoreCase) &&
+ string.Equals(pair.Value, "0", StringComparison.OrdinalIgnoreCase))
{
continue;
}
- if (StringHelper.EqualsIgnoreCase(pair.Name, "SubtitleStreamIndex") &&
- StringHelper.EqualsIgnoreCase(pair.Value, "-1"))
+ if (string.Equals(pair.Name, "SubtitleStreamIndex", StringComparison.OrdinalIgnoreCase) &&
+ string.Equals(pair.Value, "-1", StringComparison.OrdinalIgnoreCase))
{
continue;
}
- if (StringHelper.EqualsIgnoreCase(pair.Name, "Static") &&
- StringHelper.EqualsIgnoreCase(pair.Value, "false"))
+ if (string.Equals(pair.Name, "Static", StringComparison.OrdinalIgnoreCase) &&
+ string.Equals(pair.Value, "false", StringComparison.OrdinalIgnoreCase))
{
continue;
}
@@ -192,7 +193,7 @@ namespace MediaBrowser.Model.Dlna
if (MediaType == DlnaProfileType.Audio)
{
- if (StringHelper.EqualsIgnoreCase(SubProtocol, "hls"))
+ if (string.Equals(SubProtocol, "hls", StringComparison.OrdinalIgnoreCase))
{
return string.Format("{0}/audio/{1}/master.m3u8?{2}", baseUrl, ItemId, queryString);
}
@@ -200,7 +201,7 @@ namespace MediaBrowser.Model.Dlna
return string.Format("{0}/audio/{1}/stream{2}?{3}", baseUrl, ItemId, extension, queryString);
}
- if (StringHelper.EqualsIgnoreCase(SubProtocol, "hls"))
+ if (string.Equals(SubProtocol, "hls", StringComparison.OrdinalIgnoreCase))
{
return string.Format("{0}/videos/{1}/master.m3u8?{2}", baseUrl, ItemId, queryString);
}
@@ -237,7 +238,7 @@ namespace MediaBrowser.Model.Dlna
long startPositionTicks = item.StartPositionTicks;
- var isHls = StringHelper.EqualsIgnoreCase(item.SubProtocol, "hls");
+ var isHls = string.Equals(item.SubProtocol, "hls", StringComparison.OrdinalIgnoreCase);
if (isHls)
{
@@ -370,7 +371,7 @@ namespace MediaBrowser.Model.Dlna
var list = new List<SubtitleStreamInfo>();
// HLS will preserve timestamps so we can just grab the full subtitle stream
- long startPositionTicks = StringHelper.EqualsIgnoreCase(SubProtocol, "hls")
+ long startPositionTicks = string.Equals(SubProtocol, "hls", StringComparison.OrdinalIgnoreCase)
? 0
: (PlayMethod == PlayMethod.Transcode && !CopyTimestamps ? StartPositionTicks : 0);
@@ -435,7 +436,7 @@ namespace MediaBrowser.Model.Dlna
if (info.DeliveryMethod == SubtitleDeliveryMethod.External)
{
- if (MediaSource.Protocol == MediaProtocol.File || !StringHelper.EqualsIgnoreCase(stream.Codec, subtitleProfile.Format) || !stream.IsExternal)
+ 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}",
baseUrl,
@@ -802,7 +803,7 @@ namespace MediaBrowser.Model.Dlna
foreach (string codec in AudioCodecs)
{
- if (StringHelper.EqualsIgnoreCase(codec, inputCodec))
+ if (string.Equals(codec, inputCodec, StringComparison.OrdinalIgnoreCase))
{
return string.IsNullOrEmpty(codec) ? new string[] { } : new[] { codec };
}
@@ -827,7 +828,7 @@ namespace MediaBrowser.Model.Dlna
foreach (string codec in VideoCodecs)
{
- if (StringHelper.EqualsIgnoreCase(codec, inputCodec))
+ if (string.Equals(codec, inputCodec, StringComparison.OrdinalIgnoreCase))
{
return string.IsNullOrEmpty(codec) ? new string[] { } : new[] { codec };
}
@@ -884,7 +885,7 @@ namespace MediaBrowser.Model.Dlna
{
get
{
- var defaultValue = StringHelper.EqualsIgnoreCase(Container, "m2ts")
+ var defaultValue = string.Equals(Container, "m2ts", StringComparison.OrdinalIgnoreCase)
? TransportStreamTimestamp.Valid
: TransportStreamTimestamp.None;
diff --git a/MediaBrowser.Model/Dlna/SubtitleDeliveryMethod.cs b/MediaBrowser.Model/Dlna/SubtitleDeliveryMethod.cs
index 925c1f9fc..7b0204590 100644
--- a/MediaBrowser.Model/Dlna/SubtitleDeliveryMethod.cs
+++ b/MediaBrowser.Model/Dlna/SubtitleDeliveryMethod.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Dlna
{
public enum SubtitleDeliveryMethod
@@ -6,14 +8,17 @@ namespace MediaBrowser.Model.Dlna
/// The encode
/// </summary>
Encode = 0,
+
/// <summary>
/// The embed
/// </summary>
Embed = 1,
+
/// <summary>
/// The external
/// </summary>
External = 2,
+
/// <summary>
/// The HLS
/// </summary>
diff --git a/MediaBrowser.Model/Dlna/SubtitleProfile.cs b/MediaBrowser.Model/Dlna/SubtitleProfile.cs
index f950b6cb8..6a8f655ac 100644
--- a/MediaBrowser.Model/Dlna/SubtitleProfile.cs
+++ b/MediaBrowser.Model/Dlna/SubtitleProfile.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Xml.Serialization;
using MediaBrowser.Model.Extensions;
diff --git a/MediaBrowser.Model/Dlna/SubtitleStreamInfo.cs b/MediaBrowser.Model/Dlna/SubtitleStreamInfo.cs
index e81c26e69..02b3a198c 100644
--- a/MediaBrowser.Model/Dlna/SubtitleStreamInfo.cs
+++ b/MediaBrowser.Model/Dlna/SubtitleStreamInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Dlna
{
public class SubtitleStreamInfo
diff --git a/MediaBrowser.Model/Dlna/TranscodeSeekInfo.cs b/MediaBrowser.Model/Dlna/TranscodeSeekInfo.cs
index eac5d4b36..cc0c6069b 100644
--- a/MediaBrowser.Model/Dlna/TranscodeSeekInfo.cs
+++ b/MediaBrowser.Model/Dlna/TranscodeSeekInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Dlna
{
public enum TranscodeSeekInfo
diff --git a/MediaBrowser.Model/Dlna/TranscodingProfile.cs b/MediaBrowser.Model/Dlna/TranscodingProfile.cs
index dc2f0c90d..570ee7baa 100644
--- a/MediaBrowser.Model/Dlna/TranscodingProfile.cs
+++ b/MediaBrowser.Model/Dlna/TranscodingProfile.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Xml.Serialization;
namespace MediaBrowser.Model.Dlna
diff --git a/MediaBrowser.Model/Dlna/UpnpDeviceInfo.cs b/MediaBrowser.Model/Dlna/UpnpDeviceInfo.cs
index c443a8ad1..3dc1fca36 100644
--- a/MediaBrowser.Model/Dlna/UpnpDeviceInfo.cs
+++ b/MediaBrowser.Model/Dlna/UpnpDeviceInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Net;
diff --git a/MediaBrowser.Model/Dlna/VideoOptions.cs b/MediaBrowser.Model/Dlna/VideoOptions.cs
index 9c4a38292..5b12fff1c 100644
--- a/MediaBrowser.Model/Dlna/VideoOptions.cs
+++ b/MediaBrowser.Model/Dlna/VideoOptions.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Dlna
{
/// <summary>
diff --git a/MediaBrowser.Model/Dlna/XmlAttribute.cs b/MediaBrowser.Model/Dlna/XmlAttribute.cs
index aa64177a8..31603a754 100644
--- a/MediaBrowser.Model/Dlna/XmlAttribute.cs
+++ b/MediaBrowser.Model/Dlna/XmlAttribute.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Xml.Serialization;
namespace MediaBrowser.Model.Dlna
diff --git a/MediaBrowser.Model/Drawing/DrawingUtils.cs b/MediaBrowser.Model/Drawing/DrawingUtils.cs
index 9fe85512f..0be30b0ba 100644
--- a/MediaBrowser.Model/Drawing/DrawingUtils.cs
+++ b/MediaBrowser.Model/Drawing/DrawingUtils.cs
@@ -3,19 +3,19 @@ using System;
namespace MediaBrowser.Model.Drawing
{
/// <summary>
- /// Class DrawingUtils
+ /// Class DrawingUtils.
/// </summary>
public static class DrawingUtils
{
/// <summary>
- /// Resizes a set of dimensions
+ /// Resizes a set of dimensions.
/// </summary>
- /// <param name="size">The original size object</param>
- /// <param name="width">A new fixed width, if desired</param>
- /// <param name="height">A new fixed height, if desired</param>
- /// <param name="maxWidth">A max fixed width, if desired</param>
- /// <param name="maxHeight">A max fixed height, if desired</param>
- /// <returns>A new size object</returns>
+ /// <param name="size">The original size object.</param>
+ /// <param name="width">A new fixed width, if desired.</param>
+ /// <param name="height">A new fixed height, if desired.</param>
+ /// <param name="maxWidth">A max fixed width, if desired.</param>
+ /// <param name="maxHeight">A max fixed height, if desired.</param>
+ /// <returns>A new size object.</returns>
public static ImageDimensions Resize(ImageDimensions size,
int width,
int height,
diff --git a/MediaBrowser.Model/Drawing/ImageDimensions.cs b/MediaBrowser.Model/Drawing/ImageDimensions.cs
index e7805ac49..f84fe6830 100644
--- a/MediaBrowser.Model/Drawing/ImageDimensions.cs
+++ b/MediaBrowser.Model/Drawing/ImageDimensions.cs
@@ -1,36 +1,45 @@
+#pragma warning disable CS1591
+
+using System.Globalization;
+
namespace MediaBrowser.Model.Drawing
{
/// <summary>
- /// Struct ImageDimensions
+ /// Struct ImageDimensions.
/// </summary>
- public struct ImageDimensions
+ public readonly struct ImageDimensions
{
+ public ImageDimensions(int width, int height)
+ {
+ Width = width;
+ Height = height;
+ }
+
/// <summary>
- /// Gets or sets the height.
+ /// Gets the height.
/// </summary>
/// <value>The height.</value>
- public int Height { get; set; }
+ public int Height { get; }
/// <summary>
- /// Gets or sets the width.
+ /// Gets the width.
/// </summary>
/// <value>The width.</value>
- public int Width { get; set; }
+ public int Width { get; }
public bool Equals(ImageDimensions size)
{
return Width.Equals(size.Width) && Height.Equals(size.Height);
}
+ /// <inheritdoc />
public override string ToString()
{
- return string.Format("{0}-{1}", Width, Height);
- }
-
- public ImageDimensions(int width, int height)
- {
- Width = width;
- Height = height;
+ return string.Format(
+ CultureInfo.InvariantCulture,
+ "{0}-{1}",
+ Width,
+ Height);
}
}
}
diff --git a/MediaBrowser.Model/Drawing/ImageFormat.cs b/MediaBrowser.Model/Drawing/ImageFormat.cs
index 3639c1594..511c16a4e 100644
--- a/MediaBrowser.Model/Drawing/ImageFormat.cs
+++ b/MediaBrowser.Model/Drawing/ImageFormat.cs
@@ -1,28 +1,32 @@
namespace MediaBrowser.Model.Drawing
{
/// <summary>
- /// Enum ImageOutputFormat
+ /// Enum ImageOutputFormat.
/// </summary>
public enum ImageFormat
{
/// <summary>
- /// The BMP
+ /// The BMP.
/// </summary>
Bmp,
+
/// <summary>
- /// The GIF
+ /// The GIF.
/// </summary>
Gif,
+
/// <summary>
- /// The JPG
+ /// The JPG.
/// </summary>
Jpg,
+
/// <summary>
- /// The PNG
+ /// The PNG.
/// </summary>
Png,
+
/// <summary>
- /// The webp
+ /// The webp.
/// </summary>
Webp
}
diff --git a/MediaBrowser.Model/Drawing/ImageOrientation.cs b/MediaBrowser.Model/Drawing/ImageOrientation.cs
index 0fce8c3dc..5c78aea12 100644
--- a/MediaBrowser.Model/Drawing/ImageOrientation.cs
+++ b/MediaBrowser.Model/Drawing/ImageOrientation.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Drawing
{
public enum ImageOrientation
diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs
index 4da5508b4..607355d8d 100644
--- a/MediaBrowser.Model/Dto/BaseItemDto.cs
+++ b/MediaBrowser.Model/Dto/BaseItemDto.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using MediaBrowser.Model.Drawing;
diff --git a/MediaBrowser.Model/Dto/BaseItemPerson.cs b/MediaBrowser.Model/Dto/BaseItemPerson.cs
index 270a4683a..5b7eefd70 100644
--- a/MediaBrowser.Model/Dto/BaseItemPerson.cs
+++ b/MediaBrowser.Model/Dto/BaseItemPerson.cs
@@ -3,7 +3,7 @@ using System.Text.Json.Serialization;
namespace MediaBrowser.Model.Dto
{
/// <summary>
- /// This is used by the api to get information about a Person within a BaseItem
+ /// This is used by the api to get information about a Person within a BaseItem.
/// </summary>
public class BaseItemPerson
{
diff --git a/MediaBrowser.Model/Dto/IHasServerId.cs b/MediaBrowser.Model/Dto/IHasServerId.cs
index 2cce5df62..8c9798c5c 100644
--- a/MediaBrowser.Model/Dto/IHasServerId.cs
+++ b/MediaBrowser.Model/Dto/IHasServerId.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Dto
{
public interface IHasServerId
diff --git a/MediaBrowser.Model/Dto/IItemDto.cs b/MediaBrowser.Model/Dto/IItemDto.cs
index 0130adb6f..cb8808fb6 100644
--- a/MediaBrowser.Model/Dto/IItemDto.cs
+++ b/MediaBrowser.Model/Dto/IItemDto.cs
@@ -1,7 +1,7 @@
namespace MediaBrowser.Model.Dto
{
/// <summary>
- /// Interface IItemDto
+ /// Interface IItemDto.
/// </summary>
public interface IItemDto
{
diff --git a/MediaBrowser.Model/Dto/ImageByNameInfo.cs b/MediaBrowser.Model/Dto/ImageByNameInfo.cs
index 2bda8bf20..d2e43634d 100644
--- a/MediaBrowser.Model/Dto/ImageByNameInfo.cs
+++ b/MediaBrowser.Model/Dto/ImageByNameInfo.cs
@@ -1,3 +1,4 @@
+#pragma warning disable CS1591
namespace MediaBrowser.Model.Dto
{
@@ -8,21 +9,25 @@ namespace MediaBrowser.Model.Dto
/// </summary>
/// <value>The name.</value>
public string Name { get; set; }
+
/// <summary>
/// Gets or sets the theme.
/// </summary>
/// <value>The theme.</value>
public string Theme { get; set; }
+
/// <summary>
/// Gets or sets the context.
/// </summary>
/// <value>The context.</value>
public string Context { get; set; }
+
/// <summary>
/// Gets or sets the length of the file.
/// </summary>
/// <value>The length of the file.</value>
public long FileLength { get; set; }
+
/// <summary>
/// Gets or sets the format.
/// </summary>
diff --git a/MediaBrowser.Model/Dto/ImageInfo.cs b/MediaBrowser.Model/Dto/ImageInfo.cs
index 792eaff03..57942ac23 100644
--- a/MediaBrowser.Model/Dto/ImageInfo.cs
+++ b/MediaBrowser.Model/Dto/ImageInfo.cs
@@ -3,7 +3,7 @@ using MediaBrowser.Model.Entities;
namespace MediaBrowser.Model.Dto
{
/// <summary>
- /// Class ImageInfo
+ /// Class ImageInfo.
/// </summary>
public class ImageInfo
{
diff --git a/MediaBrowser.Model/Dto/ImageOptions.cs b/MediaBrowser.Model/Dto/ImageOptions.cs
index 1fd4a5383..4e672a007 100644
--- a/MediaBrowser.Model/Dto/ImageOptions.cs
+++ b/MediaBrowser.Model/Dto/ImageOptions.cs
@@ -4,7 +4,7 @@ using MediaBrowser.Model.Entities;
namespace MediaBrowser.Model.Dto
{
/// <summary>
- /// Class ImageOptions
+ /// Class ImageOptions.
/// </summary>
public class ImageOptions
{
diff --git a/MediaBrowser.Model/Dto/ItemCounts.cs b/MediaBrowser.Model/Dto/ItemCounts.cs
index ec5adab85..95f4a3d77 100644
--- a/MediaBrowser.Model/Dto/ItemCounts.cs
+++ b/MediaBrowser.Model/Dto/ItemCounts.cs
@@ -1,7 +1,7 @@
namespace MediaBrowser.Model.Dto
{
/// <summary>
- /// Class LibrarySummary
+ /// Class LibrarySummary.
/// </summary>
public class ItemCounts
{
@@ -10,48 +10,71 @@ namespace MediaBrowser.Model.Dto
/// </summary>
/// <value>The movie count.</value>
public int MovieCount { get; set; }
+
/// <summary>
/// Gets or sets the series count.
/// </summary>
/// <value>The series count.</value>
public int SeriesCount { get; set; }
+
/// <summary>
/// Gets or sets the episode count.
/// </summary>
/// <value>The episode count.</value>
public int EpisodeCount { get; set; }
+
+ /// <summary>
+ /// Gets or sets the artist count.
+ /// </summary>
+ /// <value>The artist count.</value>
public int ArtistCount { get; set; }
+
+ /// <summary>
+ /// Gets or sets the program count.
+ /// </summary>
+ /// <value>The program count.</value>
public int ProgramCount { get; set; }
+
/// <summary>
/// Gets or sets the trailer count.
/// </summary>
/// <value>The trailer count.</value>
public int TrailerCount { get; set; }
+
/// <summary>
/// Gets or sets the song count.
/// </summary>
/// <value>The song count.</value>
public int SongCount { get; set; }
+
/// <summary>
/// Gets or sets the album count.
/// </summary>
/// <value>The album count.</value>
public int AlbumCount { get; set; }
+
/// <summary>
/// Gets or sets the music video count.
/// </summary>
/// <value>The music video count.</value>
public int MusicVideoCount { get; set; }
+
/// <summary>
/// Gets or sets the box set count.
/// </summary>
/// <value>The box set count.</value>
public int BoxSetCount { get; set; }
+
/// <summary>
/// Gets or sets the book count.
/// </summary>
/// <value>The book count.</value>
public int BookCount { get; set; }
+
+ /// <summary>
+ /// Gets or sets the item count.
+ /// </summary>
+ /// <value>The item count.</value>
public int ItemCount { get; set; }
}
}
diff --git a/MediaBrowser.Model/Dto/ItemIndex.cs b/MediaBrowser.Model/Dto/ItemIndex.cs
index 21e14c73e..525576d61 100644
--- a/MediaBrowser.Model/Dto/ItemIndex.cs
+++ b/MediaBrowser.Model/Dto/ItemIndex.cs
@@ -1,7 +1,7 @@
namespace MediaBrowser.Model.Dto
{
/// <summary>
- /// Class ItemIndex
+ /// Class ItemIndex.
/// </summary>
public class ItemIndex
{
diff --git a/MediaBrowser.Model/Dto/MediaSourceInfo.cs b/MediaBrowser.Model/Dto/MediaSourceInfo.cs
index 5bdc4809a..29613adbf 100644
--- a/MediaBrowser.Model/Dto/MediaSourceInfo.cs
+++ b/MediaBrowser.Model/Dto/MediaSourceInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
@@ -57,6 +59,8 @@ namespace MediaBrowser.Model.Dto
public List<MediaStream> MediaStreams { get; set; }
+ public IReadOnlyList<MediaAttachment> MediaAttachments { get; set; }
+
public string[] Formats { get; set; }
public int? Bitrate { get; set; }
@@ -74,6 +78,7 @@ namespace MediaBrowser.Model.Dto
{
Formats = Array.Empty<string>();
MediaStreams = new List<MediaStream>();
+ MediaAttachments = Array.Empty<MediaAttachment>();
RequiredHttpHeaders = new Dictionary<string, string>();
SupportsTranscoding = true;
SupportsDirectStream = true;
@@ -207,10 +212,7 @@ namespace MediaBrowser.Model.Dto
{
if (currentStream.Type == MediaStreamType.Audio && currentStream.IsDefault)
{
- if (currentStream.Index != stream.Index)
- {
- return true;
- }
+ return currentStream.Index != stream.Index;
}
}
diff --git a/MediaBrowser.Model/Dto/MediaSourceType.cs b/MediaBrowser.Model/Dto/MediaSourceType.cs
index b643cad9a..42314d519 100644
--- a/MediaBrowser.Model/Dto/MediaSourceType.cs
+++ b/MediaBrowser.Model/Dto/MediaSourceType.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Dto
{
public enum MediaSourceType
diff --git a/MediaBrowser.Model/Dto/MetadataEditorInfo.cs b/MediaBrowser.Model/Dto/MetadataEditorInfo.cs
index 46bcb62f4..21d8a31f2 100644
--- a/MediaBrowser.Model/Dto/MetadataEditorInfo.cs
+++ b/MediaBrowser.Model/Dto/MetadataEditorInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Globalization;
diff --git a/MediaBrowser.Model/Dto/NameIdPair.cs b/MediaBrowser.Model/Dto/NameIdPair.cs
index ccd42f17f..1b4800863 100644
--- a/MediaBrowser.Model/Dto/NameIdPair.cs
+++ b/MediaBrowser.Model/Dto/NameIdPair.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace MediaBrowser.Model.Dto
@@ -9,6 +11,7 @@ namespace MediaBrowser.Model.Dto
/// </summary>
/// <value>The name.</value>
public string Name { get; set; }
+
/// <summary>
/// Gets or sets the identifier.
/// </summary>
diff --git a/MediaBrowser.Model/Dto/NameValuePair.cs b/MediaBrowser.Model/Dto/NameValuePair.cs
index 624763211..74040c2cb 100644
--- a/MediaBrowser.Model/Dto/NameValuePair.cs
+++ b/MediaBrowser.Model/Dto/NameValuePair.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Dto
{
public class NameValuePair
@@ -18,6 +20,7 @@ namespace MediaBrowser.Model.Dto
/// </summary>
/// <value>The name.</value>
public string Name { get; set; }
+
/// <summary>
/// Gets or sets the value.
/// </summary>
diff --git a/MediaBrowser.Model/Dto/RatingType.cs b/MediaBrowser.Model/Dto/RatingType.cs
index fc1f7ea0f..033776f9c 100644
--- a/MediaBrowser.Model/Dto/RatingType.cs
+++ b/MediaBrowser.Model/Dto/RatingType.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Dto
{
public enum RatingType
diff --git a/MediaBrowser.Model/Dto/RecommendationDto.cs b/MediaBrowser.Model/Dto/RecommendationDto.cs
index acfb85e9b..bc97dd6f1 100644
--- a/MediaBrowser.Model/Dto/RecommendationDto.cs
+++ b/MediaBrowser.Model/Dto/RecommendationDto.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
diff --git a/MediaBrowser.Model/Dto/RecommendationType.cs b/MediaBrowser.Model/Dto/RecommendationType.cs
index 55a0a8091..384f20c32 100644
--- a/MediaBrowser.Model/Dto/RecommendationType.cs
+++ b/MediaBrowser.Model/Dto/RecommendationType.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Dto
{
public enum RecommendationType
diff --git a/MediaBrowser.Model/Dto/UserDto.cs b/MediaBrowser.Model/Dto/UserDto.cs
index 13da018a6..d36706c38 100644
--- a/MediaBrowser.Model/Dto/UserDto.cs
+++ b/MediaBrowser.Model/Dto/UserDto.cs
@@ -5,7 +5,7 @@ using MediaBrowser.Model.Users;
namespace MediaBrowser.Model.Dto
{
/// <summary>
- /// Class UserDto
+ /// Class UserDto.
/// </summary>
public class UserDto : IItemDto, IHasServerId
{
@@ -58,6 +58,9 @@ namespace MediaBrowser.Model.Dto
/// <value><c>true</c> if this instance has configured easy password; otherwise, <c>false</c>.</value>
public bool HasConfiguredEasyPassword { get; set; }
+ /// <summary>
+ /// Gets or sets whether async login is enabled or not.
+ /// </summary>
public bool? EnableAutoLogin { get; set; }
/// <summary>
@@ -99,6 +102,7 @@ namespace MediaBrowser.Model.Dto
Policy = new UserPolicy();
}
+ /// <inheritdoc />
public override string ToString()
{
return Name ?? base.ToString();
diff --git a/MediaBrowser.Model/Dto/UserItemDataDto.cs b/MediaBrowser.Model/Dto/UserItemDataDto.cs
index fa512e94c..92f06c973 100644
--- a/MediaBrowser.Model/Dto/UserItemDataDto.cs
+++ b/MediaBrowser.Model/Dto/UserItemDataDto.cs
@@ -3,7 +3,7 @@ using System;
namespace MediaBrowser.Model.Dto
{
/// <summary>
- /// Class UserItemDataDto
+ /// Class UserItemDataDto.
/// </summary>
public class UserItemDataDto
{
diff --git a/MediaBrowser.Model/Entities/ChapterInfo.cs b/MediaBrowser.Model/Entities/ChapterInfo.cs
index dfd6fdf4a..2903ef61b 100644
--- a/MediaBrowser.Model/Entities/ChapterInfo.cs
+++ b/MediaBrowser.Model/Entities/ChapterInfo.cs
@@ -1,9 +1,11 @@
+#pragma warning disable CS1591
+
using System;
namespace MediaBrowser.Model.Entities
{
/// <summary>
- /// Class ChapterInfo
+ /// Class ChapterInfo.
/// </summary>
public class ChapterInfo
{
diff --git a/MediaBrowser.Model/Entities/CollectionType.cs b/MediaBrowser.Model/Entities/CollectionType.cs
index e26d1b8c3..354038712 100644
--- a/MediaBrowser.Model/Entities/CollectionType.cs
+++ b/MediaBrowser.Model/Entities/CollectionType.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Entities
{
public static class CollectionType
diff --git a/MediaBrowser.Model/Entities/DisplayPreferences.cs b/MediaBrowser.Model/Entities/DisplayPreferences.cs
index f9b3ac7b3..499baa058 100644
--- a/MediaBrowser.Model/Entities/DisplayPreferences.cs
+++ b/MediaBrowser.Model/Entities/DisplayPreferences.cs
@@ -3,12 +3,12 @@ using System.Collections.Generic;
namespace MediaBrowser.Model.Entities
{
/// <summary>
- /// Defines the display preferences for any item that supports them (usually Folders)
+ /// Defines the display preferences for any item that supports them (usually Folders).
/// </summary>
public class DisplayPreferences
{
/// <summary>
- /// The image scale
+ /// The image scale.
/// </summary>
private const double ImageScale = .9;
@@ -29,66 +29,79 @@ namespace MediaBrowser.Model.Entities
/// </summary>
/// <value>The user id.</value>
public string Id { get; set; }
+
/// <summary>
/// Gets or sets the type of the view.
/// </summary>
/// <value>The type of the view.</value>
public string ViewType { get; set; }
+
/// <summary>
/// Gets or sets the sort by.
/// </summary>
/// <value>The sort by.</value>
public string SortBy { get; set; }
+
/// <summary>
/// Gets or sets the index by.
/// </summary>
/// <value>The index by.</value>
public string IndexBy { get; set; }
+
/// <summary>
/// Gets or sets a value indicating whether [remember indexing].
/// </summary>
/// <value><c>true</c> if [remember indexing]; otherwise, <c>false</c>.</value>
public bool RememberIndexing { get; set; }
+
/// <summary>
/// Gets or sets the height of the primary image.
/// </summary>
/// <value>The height of the primary image.</value>
public int PrimaryImageHeight { get; set; }
+
/// <summary>
/// Gets or sets the width of the primary image.
/// </summary>
/// <value>The width of the primary image.</value>
public int PrimaryImageWidth { get; set; }
+
/// <summary>
/// Gets or sets the custom prefs.
/// </summary>
/// <value>The custom prefs.</value>
public Dictionary<string, string> CustomPrefs { get; set; }
+
/// <summary>
/// Gets or sets the scroll direction.
/// </summary>
/// <value>The scroll direction.</value>
public ScrollDirection ScrollDirection { get; set; }
+
/// <summary>
/// Gets or sets a value indicating whether to show backdrops on this item.
/// </summary>
/// <value><c>true</c> if showing backdrops; otherwise, <c>false</c>.</value>
public bool ShowBackdrop { get; set; }
+
/// <summary>
/// Gets or sets a value indicating whether [remember sorting].
/// </summary>
/// <value><c>true</c> if [remember sorting]; otherwise, <c>false</c>.</value>
public bool RememberSorting { get; set; }
+
/// <summary>
/// Gets or sets the sort order.
/// </summary>
/// <value>The sort order.</value>
public SortOrder SortOrder { get; set; }
+
/// <summary>
/// Gets or sets a value indicating whether [show sidebar].
/// </summary>
/// <value><c>true</c> if [show sidebar]; otherwise, <c>false</c>.</value>
public bool ShowSidebar { get; set; }
+
/// <summary>
/// Gets or sets the client
/// </summary>
diff --git a/MediaBrowser.Model/Entities/EmptyRequestResult.cs b/MediaBrowser.Model/Entities/EmptyRequestResult.cs
deleted file mode 100644
index 5d29218e3..000000000
--- a/MediaBrowser.Model/Entities/EmptyRequestResult.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace MediaBrowser.Model.Entities
-{
- public class EmptyRequestResult
- {
- }
-}
diff --git a/MediaBrowser.Model/Entities/ExtraType.cs b/MediaBrowser.Model/Entities/ExtraType.cs
index 97350b955..857e92adb 100644
--- a/MediaBrowser.Model/Entities/ExtraType.cs
+++ b/MediaBrowser.Model/Entities/ExtraType.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Entities
{
public enum ExtraType
diff --git a/MediaBrowser.Model/Entities/IHasProviderIds.cs b/MediaBrowser.Model/Entities/IHasProviderIds.cs
index 3b8d74cb4..c117efde9 100644
--- a/MediaBrowser.Model/Entities/IHasProviderIds.cs
+++ b/MediaBrowser.Model/Entities/IHasProviderIds.cs
@@ -3,7 +3,7 @@ using System.Collections.Generic;
namespace MediaBrowser.Model.Entities
{
/// <summary>
- /// Since BaseItem and DTOBaseItem both have ProviderIds, this interface helps avoid code repition by using extension methods
+ /// Since BaseItem and DTOBaseItem both have ProviderIds, this interface helps avoid code repition by using extension methods.
/// </summary>
public interface IHasProviderIds
{
diff --git a/MediaBrowser.Model/Entities/ImageType.cs b/MediaBrowser.Model/Entities/ImageType.cs
index ce3560e78..d89a4b3ad 100644
--- a/MediaBrowser.Model/Entities/ImageType.cs
+++ b/MediaBrowser.Model/Entities/ImageType.cs
@@ -1,56 +1,67 @@
namespace MediaBrowser.Model.Entities
{
/// <summary>
- /// Enum ImageType
+ /// Enum ImageType.
/// </summary>
public enum ImageType
{
/// <summary>
- /// The primary
+ /// The primary.
/// </summary>
Primary = 0,
+
/// <summary>
- /// The art
+ /// The art.
/// </summary>
Art = 1,
+
/// <summary>
- /// The backdrop
+ /// The backdrop.
/// </summary>
Backdrop = 2,
+
/// <summary>
- /// The banner
+ /// The banner.
/// </summary>
Banner = 3,
+
/// <summary>
- /// The logo
+ /// The logo.
/// </summary>
Logo = 4,
+
/// <summary>
- /// The thumb
+ /// The thumb.
/// </summary>
Thumb = 5,
+
/// <summary>
- /// The disc
+ /// The disc.
/// </summary>
Disc = 6,
+
/// <summary>
- /// The box
+ /// The box.
/// </summary>
Box = 7,
+
/// <summary>
- /// The screenshot
+ /// The screenshot.
/// </summary>
Screenshot = 8,
+
/// <summary>
- /// The menu
+ /// The menu.
/// </summary>
Menu = 9,
+
/// <summary>
- /// The chapter image
+ /// The chapter image.
/// </summary>
Chapter = 10,
+
/// <summary>
- /// The box rear
+ /// The box rear.
/// </summary>
BoxRear = 11
}
diff --git a/MediaBrowser.Model/Entities/IsoType.cs b/MediaBrowser.Model/Entities/IsoType.cs
index 8e4f0d63a..1aa29f31d 100644
--- a/MediaBrowser.Model/Entities/IsoType.cs
+++ b/MediaBrowser.Model/Entities/IsoType.cs
@@ -1,16 +1,17 @@
namespace MediaBrowser.Model.Entities
{
/// <summary>
- /// Enum IsoType
+ /// Enum IsoType.
/// </summary>
public enum IsoType
{
/// <summary>
- /// The DVD
+ /// The DVD.
/// </summary>
Dvd,
+
/// <summary>
- /// The blu ray
+ /// The blu ray.
/// </summary>
BluRay
}
diff --git a/MediaBrowser.Model/Entities/LibraryUpdateInfo.cs b/MediaBrowser.Model/Entities/LibraryUpdateInfo.cs
index b83df87e2..b98c00240 100644
--- a/MediaBrowser.Model/Entities/LibraryUpdateInfo.cs
+++ b/MediaBrowser.Model/Entities/LibraryUpdateInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace MediaBrowser.Model.Entities
diff --git a/MediaBrowser.Model/Entities/LocationType.cs b/MediaBrowser.Model/Entities/LocationType.cs
index 52c08b28b..879b31c5c 100644
--- a/MediaBrowser.Model/Entities/LocationType.cs
+++ b/MediaBrowser.Model/Entities/LocationType.cs
@@ -1,24 +1,27 @@
namespace MediaBrowser.Model.Entities
{
/// <summary>
- /// Enum LocationType
+ /// Enum LocationType.
/// </summary>
public enum LocationType
{
/// <summary>
- /// The file system
+ /// The file system.
/// </summary>
FileSystem = 0,
+
/// <summary>
- /// The remote
+ /// The remote.
/// </summary>
Remote = 1,
+
/// <summary>
- /// The virtual
+ /// The virtual.
/// </summary>
Virtual = 2,
+
/// <summary>
- /// The offline
+ /// The offline.
/// </summary>
Offline = 3
}
diff --git a/MediaBrowser.Model/Entities/MediaAttachment.cs b/MediaBrowser.Model/Entities/MediaAttachment.cs
new file mode 100644
index 000000000..167be18c9
--- /dev/null
+++ b/MediaBrowser.Model/Entities/MediaAttachment.cs
@@ -0,0 +1,50 @@
+namespace MediaBrowser.Model.Entities
+{
+ /// <summary>
+ /// Class MediaAttachment.
+ /// </summary>
+ public class MediaAttachment
+ {
+ /// <summary>
+ /// Gets or sets the codec.
+ /// </summary>
+ /// <value>The codec.</value>
+ public string Codec { get; set; }
+
+ /// <summary>
+ /// Gets or sets the codec tag.
+ /// </summary>
+ /// <value>The codec tag.</value>
+ public string CodecTag { get; set; }
+
+ /// <summary>
+ /// Gets or sets the comment.
+ /// </summary>
+ /// <value>The comment.</value>
+ public string Comment { get; set; }
+
+ /// <summary>
+ /// Gets or sets the index.
+ /// </summary>
+ /// <value>The index.</value>
+ public int Index { get; set; }
+
+ /// <summary>
+ /// Gets or sets the filename.
+ /// </summary>
+ /// <value>The filename.</value>
+ public string FileName { get; set; }
+
+ /// <summary>
+ /// Gets or sets the MIME type.
+ /// </summary>
+ /// <value>The MIME type.</value>
+ public string MimeType { get; set; }
+
+ /// <summary>
+ /// Gets or sets the delivery URL.
+ /// </summary>
+ /// <value>The delivery URL.</value>
+ public string DeliveryUrl { get; set; }
+ }
+}
diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs
index 5652962da..37f9d7c1a 100644
--- a/MediaBrowser.Model/Entities/MediaStream.cs
+++ b/MediaBrowser.Model/Entities/MediaStream.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -88,11 +90,11 @@ namespace MediaBrowser.Model.Entities
{
attributes.Add(StringHelper.FirstToUpper(Language));
}
- if (!string.IsNullOrEmpty(Codec) && !StringHelper.EqualsIgnoreCase(Codec, "dca"))
+ if (!string.IsNullOrEmpty(Codec) && !string.Equals(Codec, "dca", StringComparison.OrdinalIgnoreCase))
{
attributes.Add(AudioCodec.GetFriendlyName(Codec));
}
- else if (!string.IsNullOrEmpty(Profile) && !StringHelper.EqualsIgnoreCase(Profile, "lc"))
+ else if (!string.IsNullOrEmpty(Profile) && !string.Equals(Profile, "lc", StringComparison.OrdinalIgnoreCase))
{
attributes.Add(Profile);
}
@@ -394,8 +396,8 @@ namespace MediaBrowser.Model.Entities
return codec.IndexOf("pgs", StringComparison.OrdinalIgnoreCase) == -1 &&
codec.IndexOf("dvd", StringComparison.OrdinalIgnoreCase) == -1 &&
codec.IndexOf("dvbsub", StringComparison.OrdinalIgnoreCase) == -1 &&
- !StringHelper.EqualsIgnoreCase(codec, "sub") &&
- !StringHelper.EqualsIgnoreCase(codec, "dvb_subtitle");
+ !string.Equals(codec, "sub", StringComparison.OrdinalIgnoreCase) &&
+ !string.Equals(codec, "dvb_subtitle", StringComparison.OrdinalIgnoreCase);
}
public bool SupportsSubtitleConversionTo(string toCodec)
@@ -408,21 +410,21 @@ namespace MediaBrowser.Model.Entities
var fromCodec = Codec;
// Can't convert from this
- if (StringHelper.EqualsIgnoreCase(fromCodec, "ass"))
+ if (string.Equals(fromCodec, "ass", StringComparison.OrdinalIgnoreCase))
{
return false;
}
- if (StringHelper.EqualsIgnoreCase(fromCodec, "ssa"))
+ if (string.Equals(fromCodec, "ssa", StringComparison.OrdinalIgnoreCase))
{
return false;
}
// Can't convert to this
- if (StringHelper.EqualsIgnoreCase(toCodec, "ass"))
+ if (string.Equals(toCodec, "ass", StringComparison.OrdinalIgnoreCase))
{
return false;
}
- if (StringHelper.EqualsIgnoreCase(toCodec, "ssa"))
+ if (string.Equals(toCodec, "ssa", StringComparison.OrdinalIgnoreCase))
{
return false;
}
diff --git a/MediaBrowser.Model/Entities/MediaStreamType.cs b/MediaBrowser.Model/Entities/MediaStreamType.cs
index 4fc1e5372..e09aaf6d0 100644
--- a/MediaBrowser.Model/Entities/MediaStreamType.cs
+++ b/MediaBrowser.Model/Entities/MediaStreamType.cs
@@ -1,24 +1,27 @@
namespace MediaBrowser.Model.Entities
{
/// <summary>
- /// Enum MediaStreamType
+ /// Enum MediaStreamType.
/// </summary>
public enum MediaStreamType
{
/// <summary>
- /// The audio
+ /// The audio.
/// </summary>
Audio,
+
/// <summary>
- /// The video
+ /// The video.
/// </summary>
Video,
+
/// <summary>
- /// The subtitle
+ /// The subtitle.
/// </summary>
Subtitle,
+
/// <summary>
- /// The embedded image
+ /// The embedded image.
/// </summary>
EmbeddedImage
}
diff --git a/MediaBrowser.Model/Entities/MediaType.cs b/MediaBrowser.Model/Entities/MediaType.cs
index c56c8f8f2..dd2ae810b 100644
--- a/MediaBrowser.Model/Entities/MediaType.cs
+++ b/MediaBrowser.Model/Entities/MediaType.cs
@@ -1,24 +1,27 @@
namespace MediaBrowser.Model.Entities
{
/// <summary>
- /// Class MediaType
+ /// Class MediaType.
/// </summary>
- public class MediaType
+ public static class MediaType
{
/// <summary>
- /// The video
+ /// The video.
/// </summary>
public const string Video = "Video";
+
/// <summary>
- /// The audio
+ /// The audio.
/// </summary>
public const string Audio = "Audio";
+
/// <summary>
- /// The photo
+ /// The photo.
/// </summary>
public const string Photo = "Photo";
+
/// <summary>
- /// The book
+ /// The book.
/// </summary>
public const string Book = "Book";
}
diff --git a/MediaBrowser.Model/Entities/MediaUrl.cs b/MediaBrowser.Model/Entities/MediaUrl.cs
index 428c895b6..e44143755 100644
--- a/MediaBrowser.Model/Entities/MediaUrl.cs
+++ b/MediaBrowser.Model/Entities/MediaUrl.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Entities
{
public class MediaUrl
diff --git a/MediaBrowser.Model/Entities/MetadataFields.cs b/MediaBrowser.Model/Entities/MetadataFields.cs
index a7947a933..d64d4f4da 100644
--- a/MediaBrowser.Model/Entities/MetadataFields.cs
+++ b/MediaBrowser.Model/Entities/MetadataFields.cs
@@ -1,44 +1,52 @@
namespace MediaBrowser.Model.Entities
{
/// <summary>
- /// Enum MetadataFields
+ /// Enum MetadataFields.
/// </summary>
public enum MetadataFields
{
/// <summary>
- /// The cast
+ /// The cast.
/// </summary>
Cast,
+
/// <summary>
- /// The genres
+ /// The genres.
/// </summary>
Genres,
+
/// <summary>
- /// The production locations
+ /// The production locations.
/// </summary>
ProductionLocations,
+
/// <summary>
- /// The studios
+ /// The studios.
/// </summary>
Studios,
+
/// <summary>
- /// The tags
+ /// The tags.
/// </summary>
Tags,
+
/// <summary>
- /// The name
+ /// The name.
/// </summary>
Name,
+
/// <summary>
- /// The overview
+ /// The overview.
/// </summary>
Overview,
+
/// <summary>
- /// The runtime
+ /// The runtime.
/// </summary>
Runtime,
+
/// <summary>
- /// The official rating
+ /// The official rating.
/// </summary>
OfficialRating
}
diff --git a/MediaBrowser.Model/Entities/MetadataProviders.cs b/MediaBrowser.Model/Entities/MetadataProviders.cs
index e9802cf46..1a44a1661 100644
--- a/MediaBrowser.Model/Entities/MetadataProviders.cs
+++ b/MediaBrowser.Model/Entities/MetadataProviders.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Entities
{
/// <summary>
diff --git a/MediaBrowser.Model/Entities/PackageReviewInfo.cs b/MediaBrowser.Model/Entities/PackageReviewInfo.cs
index b73ba8dd0..a034de8ba 100644
--- a/MediaBrowser.Model/Entities/PackageReviewInfo.cs
+++ b/MediaBrowser.Model/Entities/PackageReviewInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace MediaBrowser.Model.Entities
diff --git a/MediaBrowser.Model/Entities/ParentalRating.cs b/MediaBrowser.Model/Entities/ParentalRating.cs
index a22e119fa..4b37bd64a 100644
--- a/MediaBrowser.Model/Entities/ParentalRating.cs
+++ b/MediaBrowser.Model/Entities/ParentalRating.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Entities
{
/// <summary>
diff --git a/MediaBrowser.Model/Entities/PersonType.cs b/MediaBrowser.Model/Entities/PersonType.cs
index 72e3538fc..81db9c613 100644
--- a/MediaBrowser.Model/Entities/PersonType.cs
+++ b/MediaBrowser.Model/Entities/PersonType.cs
@@ -1,40 +1,47 @@
namespace MediaBrowser.Model.Entities
{
/// <summary>
- /// Struct PersonType
+ /// Struct PersonType.
/// </summary>
public class PersonType
{
/// <summary>
- /// The actor
+ /// The actor.
/// </summary>
public const string Actor = "Actor";
+
/// <summary>
- /// The director
+ /// The director.
/// </summary>
public const string Director = "Director";
+
/// <summary>
- /// The composer
+ /// The composer.
/// </summary>
public const string Composer = "Composer";
+
/// <summary>
- /// The writer
+ /// The writer.
/// </summary>
public const string Writer = "Writer";
+
/// <summary>
- /// The guest star
+ /// The guest star.
/// </summary>
public const string GuestStar = "GuestStar";
+
/// <summary>
- /// The producer
+ /// The producer.
/// </summary>
public const string Producer = "Producer";
+
/// <summary>
- /// The conductor
+ /// The conductor.
/// </summary>
public const string Conductor = "Conductor";
+
/// <summary>
- /// The lyricist
+ /// The lyricist.
/// </summary>
public const string Lyricist = "Lyricist";
}
diff --git a/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs b/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs
index a151bb3bb..cd387bd54 100644
--- a/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs
+++ b/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs
@@ -4,7 +4,7 @@ using System.Collections.Generic;
namespace MediaBrowser.Model.Entities
{
/// <summary>
- /// Class ProviderIdsExtensions
+ /// Class ProviderIdsExtensions.
/// </summary>
public static class ProviderIdsExtensions
{
diff --git a/MediaBrowser.Model/Entities/ScrollDirection.cs b/MediaBrowser.Model/Entities/ScrollDirection.cs
index bc66364f7..a1de0edcb 100644
--- a/MediaBrowser.Model/Entities/ScrollDirection.cs
+++ b/MediaBrowser.Model/Entities/ScrollDirection.cs
@@ -1,16 +1,17 @@
namespace MediaBrowser.Model.Entities
{
/// <summary>
- /// Enum ScrollDirection
+ /// Enum ScrollDirection.
/// </summary>
public enum ScrollDirection
{
/// <summary>
- /// The horizontal
+ /// The horizontal.
/// </summary>
Horizontal,
+
/// <summary>
- /// The vertical
+ /// The vertical.
/// </summary>
Vertical
}
diff --git a/MediaBrowser.Model/Entities/SeriesStatus.cs b/MediaBrowser.Model/Entities/SeriesStatus.cs
index cab6a83e8..51351c135 100644
--- a/MediaBrowser.Model/Entities/SeriesStatus.cs
+++ b/MediaBrowser.Model/Entities/SeriesStatus.cs
@@ -1,16 +1,17 @@
namespace MediaBrowser.Model.Entities
{
/// <summary>
- /// Enum SeriesStatus
+ /// Enum SeriesStatus.
/// </summary>
public enum SeriesStatus
{
/// <summary>
- /// The continuing
+ /// The continuing.
/// </summary>
Continuing,
+
/// <summary>
- /// The ended
+ /// The ended.
/// </summary>
Ended
}
diff --git a/MediaBrowser.Model/Entities/SortOrder.cs b/MediaBrowser.Model/Entities/SortOrder.cs
index 558ebeac2..e6cb6fd09 100644
--- a/MediaBrowser.Model/Entities/SortOrder.cs
+++ b/MediaBrowser.Model/Entities/SortOrder.cs
@@ -1,16 +1,17 @@
namespace MediaBrowser.Model.Entities
{
/// <summary>
- /// Enum SortOrder
+ /// Enum SortOrder.
/// </summary>
public enum SortOrder
{
/// <summary>
- /// The ascending
+ /// The ascending.
/// </summary>
Ascending,
+
/// <summary>
- /// The descending
+ /// The descending.
/// </summary>
Descending
}
diff --git a/MediaBrowser.Model/Entities/TrailerType.cs b/MediaBrowser.Model/Entities/TrailerType.cs
index 73be5d7ca..68d992b0d 100644
--- a/MediaBrowser.Model/Entities/TrailerType.cs
+++ b/MediaBrowser.Model/Entities/TrailerType.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Entities
{
public enum TrailerType
diff --git a/MediaBrowser.Model/Entities/UserDataSaveReason.cs b/MediaBrowser.Model/Entities/UserDataSaveReason.cs
index bd7471682..20404e6f4 100644
--- a/MediaBrowser.Model/Entities/UserDataSaveReason.cs
+++ b/MediaBrowser.Model/Entities/UserDataSaveReason.cs
@@ -1,32 +1,37 @@
namespace MediaBrowser.Model.Entities
{
/// <summary>
- /// Enum UserDataSaveReason
+ /// Enum UserDataSaveReason.
/// </summary>
public enum UserDataSaveReason
{
/// <summary>
- /// The playback start
+ /// The playback start.
/// </summary>
PlaybackStart = 1,
+
/// <summary>
- /// The playback progress
+ /// The playback progress.
/// </summary>
PlaybackProgress = 2,
+
/// <summary>
- /// The playback finished
+ /// The playback finished.
/// </summary>
PlaybackFinished = 3,
+
/// <summary>
- /// The toggle played
+ /// The toggle played.
/// </summary>
TogglePlayed = 4,
+
/// <summary>
- /// The update user rating
+ /// The update user rating.
/// </summary>
UpdateUserRating = 5,
+
/// <summary>
- /// The import
+ /// The import.
/// </summary>
Import = 6
}
diff --git a/MediaBrowser.Model/Entities/Video3DFormat.cs b/MediaBrowser.Model/Entities/Video3DFormat.cs
index 89923ae52..a4f62e18b 100644
--- a/MediaBrowser.Model/Entities/Video3DFormat.cs
+++ b/MediaBrowser.Model/Entities/Video3DFormat.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Entities
{
public enum Video3DFormat
diff --git a/MediaBrowser.Model/Entities/VideoType.cs b/MediaBrowser.Model/Entities/VideoType.cs
index 95d69fb7b..172796301 100644
--- a/MediaBrowser.Model/Entities/VideoType.cs
+++ b/MediaBrowser.Model/Entities/VideoType.cs
@@ -1,24 +1,27 @@
namespace MediaBrowser.Model.Entities
{
/// <summary>
- /// Enum VideoType
+ /// Enum VideoType.
/// </summary>
public enum VideoType
{
/// <summary>
- /// The video file
+ /// The video file.
/// </summary>
VideoFile,
+
/// <summary>
- /// The iso
+ /// The iso.
/// </summary>
Iso,
+
/// <summary>
- /// The DVD
+ /// The DVD.
/// </summary>
Dvd,
+
/// <summary>
- /// The blu ray
+ /// The blu ray.
/// </summary>
BluRay
}
diff --git a/MediaBrowser.Model/Entities/VirtualFolderInfo.cs b/MediaBrowser.Model/Entities/VirtualFolderInfo.cs
index 6bdbdb489..dd30c9c84 100644
--- a/MediaBrowser.Model/Entities/VirtualFolderInfo.cs
+++ b/MediaBrowser.Model/Entities/VirtualFolderInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using MediaBrowser.Model.Configuration;
diff --git a/MediaBrowser.Model/Events/GenericEventArgs.cs b/MediaBrowser.Model/Events/GenericEventArgs.cs
index fc8bc620f..1ef0b25c9 100644
--- a/MediaBrowser.Model/Events/GenericEventArgs.cs
+++ b/MediaBrowser.Model/Events/GenericEventArgs.cs
@@ -3,7 +3,7 @@ using System;
namespace MediaBrowser.Model.Events
{
/// <summary>
- /// Provides a generic EventArgs subclass that can hold any kind of object
+ /// Provides a generic EventArgs subclass that can hold any kind of object.
/// </summary>
/// <typeparam name="T"></typeparam>
public class GenericEventArgs<T> : EventArgs
diff --git a/MediaBrowser.Model/Extensions/ListHelper.cs b/MediaBrowser.Model/Extensions/ListHelper.cs
index b5bd07702..90ce6f2e5 100644
--- a/MediaBrowser.Model/Extensions/ListHelper.cs
+++ b/MediaBrowser.Model/Extensions/ListHelper.cs
@@ -1,7 +1,10 @@
+#pragma warning disable CS1591
+
using System;
namespace MediaBrowser.Model.Extensions
{
+ // TODO: @bond remove
public static class ListHelper
{
public static bool ContainsIgnoreCase(string[] list, string value)
diff --git a/MediaBrowser.Model/Extensions/StringHelper.cs b/MediaBrowser.Model/Extensions/StringHelper.cs
index 75ba12a17..f97a07096 100644
--- a/MediaBrowser.Model/Extensions/StringHelper.cs
+++ b/MediaBrowser.Model/Extensions/StringHelper.cs
@@ -1,57 +1,38 @@
-using System;
-using System.Text;
-
namespace MediaBrowser.Model.Extensions
{
/// <summary>
- /// Isolating these helpers allow this entire project to be easily converted to Java
+ /// Helper methods for manipulating strings.
/// </summary>
public static class StringHelper
{
/// <summary>
- /// Equalses the ignore case.
+ /// Returns the string with the first character as uppercase.
/// </summary>
- /// <param name="str1">The STR1.</param>
- /// <param name="str2">The STR2.</param>
- /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
- public static bool EqualsIgnoreCase(string str1, string str2)
+ /// <param name="str">The input string.</param>
+ /// <returns>The string with the first character as uppercase.</returns>
+ public static string FirstToUpper(string str)
{
- return string.Equals(str1, str2, StringComparison.OrdinalIgnoreCase);
- }
-
- /// <summary>
- /// Replaces the specified STR.
- /// </summary>
- /// <param name="str">The STR.</param>
- /// <param name="oldValue">The old value.</param>
- /// <param name="newValue">The new value.</param>
- /// <param name="comparison">The comparison.</param>
- /// <returns>System.String.</returns>
- public static string Replace(this string str, string oldValue, string newValue, StringComparison comparison)
- {
- var sb = new StringBuilder();
-
- var previousIndex = 0;
- var index = str.IndexOf(oldValue, comparison);
-
- while (index != -1)
+ if (string.IsNullOrEmpty(str))
{
- sb.Append(str.Substring(previousIndex, index - previousIndex));
- sb.Append(newValue);
- index += oldValue.Length;
-
- previousIndex = index;
- index = str.IndexOf(oldValue, index, comparison);
+ return string.Empty;
}
- sb.Append(str.Substring(previousIndex));
-
- return sb.ToString();
- }
+ if (char.IsUpper(str[0]))
+ {
+ return str;
+ }
- public static string FirstToUpper(this string str)
- {
- return string.IsNullOrEmpty(str) ? string.Empty : str.Substring(0, 1).ToUpperInvariant() + str.Substring(1);
+ return string.Create(
+ str.Length,
+ str,
+ (chars, buf) =>
+ {
+ chars[0] = char.ToUpperInvariant(buf[0]);
+ for (int i = 1; i < chars.Length; i++)
+ {
+ chars[i] = buf[i];
+ }
+ });
}
}
}
diff --git a/MediaBrowser.Model/Globalization/CountryInfo.cs b/MediaBrowser.Model/Globalization/CountryInfo.cs
index 3ae59494f..72362f4f3 100644
--- a/MediaBrowser.Model/Globalization/CountryInfo.cs
+++ b/MediaBrowser.Model/Globalization/CountryInfo.cs
@@ -1,7 +1,7 @@
namespace MediaBrowser.Model.Globalization
{
/// <summary>
- /// Class CountryInfo
+ /// Class CountryInfo.
/// </summary>
public class CountryInfo
{
diff --git a/MediaBrowser.Model/Globalization/CultureDto.cs b/MediaBrowser.Model/Globalization/CultureDto.cs
index a213d4147..f415840b0 100644
--- a/MediaBrowser.Model/Globalization/CultureDto.cs
+++ b/MediaBrowser.Model/Globalization/CultureDto.cs
@@ -1,9 +1,11 @@
-using global::System;
+#pragma warning disable CS1591
+
+using System;
namespace MediaBrowser.Model.Globalization
{
/// <summary>
- /// Class CultureDto
+ /// Class CultureDto.
/// </summary>
public class CultureDto
{
diff --git a/MediaBrowser.Model/Globalization/ILocalizationManager.cs b/MediaBrowser.Model/Globalization/ILocalizationManager.cs
index 0b6cfe1b7..613bfca69 100644
--- a/MediaBrowser.Model/Globalization/ILocalizationManager.cs
+++ b/MediaBrowser.Model/Globalization/ILocalizationManager.cs
@@ -5,7 +5,7 @@ using MediaBrowser.Model.Entities;
namespace MediaBrowser.Model.Globalization
{
/// <summary>
- /// Interface ILocalizationManager
+ /// Interface ILocalizationManager.
/// </summary>
public interface ILocalizationManager
{
diff --git a/MediaBrowser.Model/Globalization/LocalizationOption.cs b/MediaBrowser.Model/Globalization/LocalizationOption.cs
index c4c9a8919..00caf5e11 100644
--- a/MediaBrowser.Model/Globalization/LocalizationOption.cs
+++ b/MediaBrowser.Model/Globalization/LocalizationOption.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Globalization
{
public class LocalizationOption
diff --git a/MediaBrowser.Model/IO/FileSystemEntryType.cs b/MediaBrowser.Model/IO/FileSystemEntryType.cs
index a4ed2334d..7780d6fcc 100644
--- a/MediaBrowser.Model/IO/FileSystemEntryType.cs
+++ b/MediaBrowser.Model/IO/FileSystemEntryType.cs
@@ -1,24 +1,27 @@
namespace MediaBrowser.Model.IO
{
/// <summary>
- /// Enum FileSystemEntryType
+ /// Enum FileSystemEntryType.
/// </summary>
public enum FileSystemEntryType
{
/// <summary>
- /// The file
+ /// The file.
/// </summary>
File,
+
/// <summary>
- /// The directory
+ /// The directory.
/// </summary>
Directory,
+
/// <summary>
- /// The network computer
+ /// The network computer.
/// </summary>
NetworkComputer,
+
/// <summary>
- /// The network share
+ /// The network share.
/// </summary>
NetworkShare
}
diff --git a/MediaBrowser.Model/IO/FileSystemMetadata.cs b/MediaBrowser.Model/IO/FileSystemMetadata.cs
index 2a6d13959..4b9102392 100644
--- a/MediaBrowser.Model/IO/FileSystemMetadata.cs
+++ b/MediaBrowser.Model/IO/FileSystemMetadata.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace MediaBrowser.Model.IO
@@ -9,26 +11,31 @@ namespace MediaBrowser.Model.IO
/// </summary>
/// <value><c>true</c> if exists; otherwise, <c>false</c>.</value>
public bool Exists { get; set; }
+
/// <summary>
/// Gets or sets the full name.
/// </summary>
/// <value>The full name.</value>
public string FullName { get; set; }
+
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
public string Name { get; set; }
+
/// <summary>
/// Gets or sets the extension.
/// </summary>
/// <value>The extension.</value>
public string Extension { get; set; }
+
/// <summary>
/// Gets or sets the length.
/// </summary>
/// <value>The length.</value>
public long Length { get; set; }
+
/// <summary>
/// Gets or sets the name of the directory.
/// </summary>
@@ -40,11 +47,13 @@ namespace MediaBrowser.Model.IO
/// </summary>
/// <value>The last write time UTC.</value>
public DateTime LastWriteTimeUtc { get; set; }
+
/// <summary>
/// Gets or sets the creation time UTC.
/// </summary>
/// <value>The creation time UTC.</value>
public DateTime CreationTimeUtc { get; set; }
+
/// <summary>
/// Gets a value indicating whether this instance is directory.
/// </summary>
diff --git a/MediaBrowser.Model/IO/IFileSystem.cs b/MediaBrowser.Model/IO/IFileSystem.cs
index ca99b28ca..53f23a8e0 100644
--- a/MediaBrowser.Model/IO/IFileSystem.cs
+++ b/MediaBrowser.Model/IO/IFileSystem.cs
@@ -1,11 +1,12 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
-using System.IO;
namespace MediaBrowser.Model.IO
{
/// <summary>
- /// Interface IFileSystem
+ /// Interface IFileSystem.
/// </summary>
public interface IFileSystem
{
@@ -99,20 +100,6 @@ namespace MediaBrowser.Model.IO
DateTime GetLastWriteTimeUtc(string path);
/// <summary>
- /// Gets the file stream.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <param name="mode">The mode.</param>
- /// <param name="access">The access.</param>
- /// <param name="share">The share.</param>
- /// <param name="isAsync">if set to <c>true</c> [is asynchronous].</param>
- /// <returns>FileStream.</returns>
- Stream GetFileStream(string path, FileOpenMode mode, FileAccessMode access, FileShareMode share, bool isAsync = false);
-
- Stream GetFileStream(string path, FileOpenMode mode, FileAccessMode access, FileShareMode share,
- FileOpenOptions fileOpenOptions);
-
- /// <summary>
/// Swaps the files.
/// </summary>
/// <param name="file1">The file1.</param>
@@ -218,128 +205,4 @@ namespace MediaBrowser.Model.IO
List<FileSystemMetadata> GetDrives();
void SetExecutable(string path);
}
-
- //TODO Investigate if can be replaced by the one from System.IO ?
- public enum FileOpenMode
- {
- //
- // Summary:
- // Specifies that the operating system should create a new file. This requires System.Security.Permissions.FileIOPermissionAccess.Write
- // permission. If the file already exists, an System.IO.IOException exception is
- // thrown.
- CreateNew = 1,
- //
- // Summary:
- // Specifies that the operating system should create a new file. If the file already
- // exists, it will be overwritten. This requires System.Security.Permissions.FileIOPermissionAccess.Write
- // permission. FileMode.Create is equivalent to requesting that if the file does
- // not exist, use System.IO.FileMode.CreateNew; otherwise, use System.IO.FileMode.Truncate.
- // If the file already exists but is a hidden file, an System.UnauthorizedAccessException
- // exception is thrown.
- Create = 2,
- //
- // Summary:
- // Specifies that the operating system should open an existing file. The ability
- // to open the file is dependent on the value specified by the System.IO.FileAccess
- // enumeration. A System.IO.FileNotFoundException exception is thrown if the file
- // does not exist.
- Open = 3,
- //
- // Summary:
- // Specifies that the operating system should open a file if it exists; otherwise,
- // a new file should be created. If the file is opened with FileAccess.Read, System.Security.Permissions.FileIOPermissionAccess.Read
- // permission is required. If the file access is FileAccess.Write, System.Security.Permissions.FileIOPermissionAccess.Write
- // permission is required. If the file is opened with FileAccess.ReadWrite, both
- // System.Security.Permissions.FileIOPermissionAccess.Read and System.Security.Permissions.FileIOPermissionAccess.Write
- // permissions are required.
- OpenOrCreate = 4
- }
-
- public enum FileAccessMode
- {
- //
- // Summary:
- // Read access to the file. Data can be read from the file. Combine with Write for
- // read/write access.
- Read = 1,
- //
- // Summary:
- // Write access to the file. Data can be written to the file. Combine with Read
- // for read/write access.
- Write = 2
- }
-
- public enum FileShareMode
- {
- //
- // Summary:
- // Declines sharing of the current file. Any request to open the file (by this process
- // or another process) will fail until the file is closed.
- None = 0,
- //
- // Summary:
- // Allows subsequent opening of the file for reading. If this flag is not specified,
- // any request to open the file for reading (by this process or another process)
- // will fail until the file is closed. However, even if this flag is specified,
- // additional permissions might still be needed to access the file.
- Read = 1,
- //
- // Summary:
- // Allows subsequent opening of the file for writing. If this flag is not specified,
- // any request to open the file for writing (by this process or another process)
- // will fail until the file is closed. However, even if this flag is specified,
- // additional permissions might still be needed to access the file.
- Write = 2,
- //
- // Summary:
- // Allows subsequent opening of the file for reading or writing. If this flag is
- // not specified, any request to open the file for reading or writing (by this process
- // or another process) will fail until the file is closed. However, even if this
- // flag is specified, additional permissions might still be needed to access the
- // file.
- ReadWrite = 3
- }
-
- //
- // Summary:
- // Represents advanced options for creating a System.IO.FileStream object.
- [Flags]
- public enum FileOpenOptions
- {
- //
- // Summary:
- // Indicates that the system should write through any intermediate cache and go
- // directly to disk.
- WriteThrough = int.MinValue,
- //
- // Summary:
- // Indicates that no additional options should be used when creating a System.IO.FileStream
- // object.
- None = 0,
- //
- // Summary:
- // Indicates that a file is encrypted and can be decrypted only by using the same
- // user account used for encryption.
- Encrypted = 16384,
- //
- // Summary:
- // Indicates that a file is automatically deleted when it is no longer in use.
- DeleteOnClose = 67108864,
- //
- // Summary:
- // Indicates that the file is to be accessed sequentially from beginning to end.
- // The system can use this as a hint to optimize file caching. If an application
- // moves the file pointer for random access, optimum caching may not occur; however,
- // correct operation is still guaranteed.
- SequentialScan = 134217728,
- //
- // Summary:
- // Indicates that the file is accessed randomly. The system can use this as a hint
- // to optimize file caching.
- RandomAccess = 268435456,
- //
- // Summary:
- // Indicates that a file can be used for asynchronous reading and writing.
- Asynchronous = 1073741824
- }
}
diff --git a/MediaBrowser.Model/IO/IIsoManager.cs b/MediaBrowser.Model/IO/IIsoManager.cs
index eb0cb4bfb..8b6af019d 100644
--- a/MediaBrowser.Model/IO/IIsoManager.cs
+++ b/MediaBrowser.Model/IO/IIsoManager.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.IO;
diff --git a/MediaBrowser.Model/IO/IIsoMount.cs b/MediaBrowser.Model/IO/IIsoMount.cs
index 825c0c243..72ec673ee 100644
--- a/MediaBrowser.Model/IO/IIsoMount.cs
+++ b/MediaBrowser.Model/IO/IIsoMount.cs
@@ -3,7 +3,7 @@ using System;
namespace MediaBrowser.Model.IO
{
/// <summary>
- /// Interface IIsoMount
+ /// Interface IIsoMount.
/// </summary>
public interface IIsoMount : IDisposable
{
diff --git a/MediaBrowser.Model/IO/IIsoMounter.cs b/MediaBrowser.Model/IO/IIsoMounter.cs
index 766a9e4e6..83fdb5fd6 100644
--- a/MediaBrowser.Model/IO/IIsoMounter.cs
+++ b/MediaBrowser.Model/IO/IIsoMounter.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.IO;
using System.Threading;
diff --git a/MediaBrowser.Model/IO/StreamDefaults.cs b/MediaBrowser.Model/IO/IODefaults.cs
index 4b55ce1f3..f392dbcce 100644
--- a/MediaBrowser.Model/IO/StreamDefaults.cs
+++ b/MediaBrowser.Model/IO/IODefaults.cs
@@ -1,18 +1,18 @@
namespace MediaBrowser.Model.IO
{
/// <summary>
- /// Class StreamDefaults.
+ /// Class IODefaults.
/// </summary>
- public static class StreamDefaults
+ public static class IODefaults
{
/// <summary>
/// The default copy to buffer size.
/// </summary>
- public const int DefaultCopyToBufferSize = 81920;
+ public const int CopyToBufferSize = 81920;
/// <summary>
/// The default file stream buffer size.
/// </summary>
- public const int DefaultFileStreamBufferSize = 4096;
+ public const int FileStreamBufferSize = 4096;
}
}
diff --git a/MediaBrowser.Model/IO/IShortcutHandler.cs b/MediaBrowser.Model/IO/IShortcutHandler.cs
index 2cc18274b..5c663aa0d 100644
--- a/MediaBrowser.Model/IO/IShortcutHandler.cs
+++ b/MediaBrowser.Model/IO/IShortcutHandler.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.IO
{
public interface IShortcutHandler
@@ -7,12 +9,14 @@ namespace MediaBrowser.Model.IO
/// </summary>
/// <value>The extension.</value>
string Extension { get; }
+
/// <summary>
/// Resolves the specified shortcut path.
/// </summary>
/// <param name="shortcutPath">The shortcut path.</param>
/// <returns>System.String.</returns>
string Resolve(string shortcutPath);
+
/// <summary>
/// Creates the specified shortcut path.
/// </summary>
diff --git a/MediaBrowser.Model/IO/IStreamHelper.cs b/MediaBrowser.Model/IO/IStreamHelper.cs
index 97d985df6..e348cd725 100644
--- a/MediaBrowser.Model/IO/IStreamHelper.cs
+++ b/MediaBrowser.Model/IO/IStreamHelper.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.IO;
using System.Threading;
diff --git a/MediaBrowser.Model/IO/IZipClient.cs b/MediaBrowser.Model/IO/IZipClient.cs
index eaddd6df3..83e8a018d 100644
--- a/MediaBrowser.Model/IO/IZipClient.cs
+++ b/MediaBrowser.Model/IO/IZipClient.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.IO;
namespace MediaBrowser.Model.IO
diff --git a/MediaBrowser.Model/Library/PlayAccess.cs b/MediaBrowser.Model/Library/PlayAccess.cs
index 2fd754f5e..a2f263ce5 100644
--- a/MediaBrowser.Model/Library/PlayAccess.cs
+++ b/MediaBrowser.Model/Library/PlayAccess.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Library
{
public enum PlayAccess
diff --git a/MediaBrowser.Model/Library/UserViewQuery.cs b/MediaBrowser.Model/Library/UserViewQuery.cs
index c2e189603..a538efd25 100644
--- a/MediaBrowser.Model/Library/UserViewQuery.cs
+++ b/MediaBrowser.Model/Library/UserViewQuery.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace MediaBrowser.Model.Library
diff --git a/MediaBrowser.Model/LiveTv/BaseTimerInfoDto.cs b/MediaBrowser.Model/LiveTv/BaseTimerInfoDto.cs
index 311b5b0c5..064ce6520 100644
--- a/MediaBrowser.Model/LiveTv/BaseTimerInfoDto.cs
+++ b/MediaBrowser.Model/LiveTv/BaseTimerInfoDto.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using MediaBrowser.Model.Dto;
diff --git a/MediaBrowser.Model/LiveTv/ChannelType.cs b/MediaBrowser.Model/LiveTv/ChannelType.cs
index 8808aa6d2..b6974cb08 100644
--- a/MediaBrowser.Model/LiveTv/ChannelType.cs
+++ b/MediaBrowser.Model/LiveTv/ChannelType.cs
@@ -1,7 +1,7 @@
namespace MediaBrowser.Model.LiveTv
{
/// <summary>
- /// Enum ChannelType
+ /// Enum ChannelType.
/// </summary>
public enum ChannelType
{
@@ -11,7 +11,7 @@ namespace MediaBrowser.Model.LiveTv
TV,
/// <summary>
- /// The radio
+ /// The radio.
/// </summary>
Radio
}
diff --git a/MediaBrowser.Model/LiveTv/DayPattern.cs b/MediaBrowser.Model/LiveTv/DayPattern.cs
index 73b15507b..17efe3908 100644
--- a/MediaBrowser.Model/LiveTv/DayPattern.cs
+++ b/MediaBrowser.Model/LiveTv/DayPattern.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.LiveTv
{
public enum DayPattern
diff --git a/MediaBrowser.Model/LiveTv/GuideInfo.cs b/MediaBrowser.Model/LiveTv/GuideInfo.cs
index 1303c278e..a224d73b7 100644
--- a/MediaBrowser.Model/LiveTv/GuideInfo.cs
+++ b/MediaBrowser.Model/LiveTv/GuideInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace MediaBrowser.Model.LiveTv
diff --git a/MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs b/MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs
index eedf89db0..8154fbd0e 100644
--- a/MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs
+++ b/MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using MediaBrowser.Model.Entities;
diff --git a/MediaBrowser.Model/LiveTv/LiveTvInfo.cs b/MediaBrowser.Model/LiveTv/LiveTvInfo.cs
index 60cb27331..85b77af24 100644
--- a/MediaBrowser.Model/LiveTv/LiveTvInfo.cs
+++ b/MediaBrowser.Model/LiveTv/LiveTvInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace MediaBrowser.Model.LiveTv
diff --git a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs
index 36fe0c3d2..dc8e0f91b 100644
--- a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs
+++ b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using MediaBrowser.Model.Dto;
diff --git a/MediaBrowser.Model/LiveTv/LiveTvServiceInfo.cs b/MediaBrowser.Model/LiveTv/LiveTvServiceInfo.cs
index 9ad13391a..09e900643 100644
--- a/MediaBrowser.Model/LiveTv/LiveTvServiceInfo.cs
+++ b/MediaBrowser.Model/LiveTv/LiveTvServiceInfo.cs
@@ -1,9 +1,11 @@
+#pragma warning disable CS1591
+
using System;
namespace MediaBrowser.Model.LiveTv
{
/// <summary>
- /// Class ServiceInfo
+ /// Class ServiceInfo.
/// </summary>
public class LiveTvServiceInfo
{
@@ -42,6 +44,7 @@ namespace MediaBrowser.Model.LiveTv
/// </summary>
/// <value><c>true</c> if this instance has update available; otherwise, <c>false</c>.</value>
public bool HasUpdateAvailable { get; set; }
+
/// <summary>
/// Gets or sets a value indicating whether this instance is visible.
/// </summary>
diff --git a/MediaBrowser.Model/LiveTv/LiveTvServiceStatus.cs b/MediaBrowser.Model/LiveTv/LiveTvServiceStatus.cs
index 7578f329a..72a0e2d7b 100644
--- a/MediaBrowser.Model/LiveTv/LiveTvServiceStatus.cs
+++ b/MediaBrowser.Model/LiveTv/LiveTvServiceStatus.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.LiveTv
{
public enum LiveTvServiceStatus
diff --git a/MediaBrowser.Model/LiveTv/LiveTvTunerStatus.cs b/MediaBrowser.Model/LiveTv/LiveTvTunerStatus.cs
index f7f521e43..80a646195 100644
--- a/MediaBrowser.Model/LiveTv/LiveTvTunerStatus.cs
+++ b/MediaBrowser.Model/LiveTv/LiveTvTunerStatus.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.LiveTv
{
public enum LiveTvTunerStatus
diff --git a/MediaBrowser.Model/LiveTv/ProgramAudio.cs b/MediaBrowser.Model/LiveTv/ProgramAudio.cs
index 158d67eb9..727d34695 100644
--- a/MediaBrowser.Model/LiveTv/ProgramAudio.cs
+++ b/MediaBrowser.Model/LiveTv/ProgramAudio.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.LiveTv
{
public enum ProgramAudio
diff --git a/MediaBrowser.Model/LiveTv/RecordingQuery.cs b/MediaBrowser.Model/LiveTv/RecordingQuery.cs
index f98d7fe86..c75092b79 100644
--- a/MediaBrowser.Model/LiveTv/RecordingQuery.cs
+++ b/MediaBrowser.Model/LiveTv/RecordingQuery.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
diff --git a/MediaBrowser.Model/LiveTv/RecordingStatus.cs b/MediaBrowser.Model/LiveTv/RecordingStatus.cs
index d3270c4d3..b0ba42d43 100644
--- a/MediaBrowser.Model/LiveTv/RecordingStatus.cs
+++ b/MediaBrowser.Model/LiveTv/RecordingStatus.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.LiveTv
{
public enum RecordingStatus
diff --git a/MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs b/MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs
index 72c7a0c90..e30dd84dc 100644
--- a/MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs
+++ b/MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using MediaBrowser.Model.Entities;
diff --git a/MediaBrowser.Model/LiveTv/SeriesTimerQuery.cs b/MediaBrowser.Model/LiveTv/SeriesTimerQuery.cs
index a15ba7a12..bb553a576 100644
--- a/MediaBrowser.Model/LiveTv/SeriesTimerQuery.cs
+++ b/MediaBrowser.Model/LiveTv/SeriesTimerQuery.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Model.Entities;
namespace MediaBrowser.Model.LiveTv
diff --git a/MediaBrowser.Model/LiveTv/TimerInfoDto.cs b/MediaBrowser.Model/LiveTv/TimerInfoDto.cs
index 208f731c5..a1fbc5177 100644
--- a/MediaBrowser.Model/LiveTv/TimerInfoDto.cs
+++ b/MediaBrowser.Model/LiveTv/TimerInfoDto.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Model.Dto;
namespace MediaBrowser.Model.LiveTv
diff --git a/MediaBrowser.Model/LiveTv/TimerQuery.cs b/MediaBrowser.Model/LiveTv/TimerQuery.cs
index 1478cc148..1ef6dd67e 100644
--- a/MediaBrowser.Model/LiveTv/TimerQuery.cs
+++ b/MediaBrowser.Model/LiveTv/TimerQuery.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.LiveTv
{
public class TimerQuery
diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj
index 53cd08fbd..657665766 100644
--- a/MediaBrowser.Model/MediaBrowser.Model.csproj
+++ b/MediaBrowser.Model/MediaBrowser.Model.csproj
@@ -8,20 +8,33 @@
</PropertyGroup>
<PropertyGroup>
- <TargetFramework>netstandard2.0</TargetFramework>
+ <TargetFramework>netstandard2.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
+ <TreatWarningsAsErrors Condition=" '$(Configuration)' == 'Release' " >true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" />
- <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="3.0.0" />
+ <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="3.1.1" />
<PackageReference Include="System.Globalization" Version="4.3.0" />
- <PackageReference Include="System.Text.Json" Version="4.6.0" />
+ <PackageReference Include="System.Text.Json" Version="4.7.0" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\SharedVersion.cs" />
</ItemGroup>
+ <!-- Code Analyzers-->
+ <ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
+ <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8" PrivateAssets="All" />
+ <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
+ <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" />
+ <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
+ </ItemGroup>
+
+ <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
+ <CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
+ </PropertyGroup>
+
</Project>
diff --git a/MediaBrowser.Model/MediaInfo/AudioCodec.cs b/MediaBrowser.Model/MediaInfo/AudioCodec.cs
index 5ebdb99cb..dcb6fa270 100644
--- a/MediaBrowser.Model/MediaInfo/AudioCodec.cs
+++ b/MediaBrowser.Model/MediaInfo/AudioCodec.cs
@@ -1,6 +1,8 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.MediaInfo
{
- public class AudioCodec
+ public static class AudioCodec
{
public const string AAC = "aac";
public const string MP3 = "mp3";
diff --git a/MediaBrowser.Model/MediaInfo/BlurayDiscInfo.cs b/MediaBrowser.Model/MediaInfo/BlurayDiscInfo.cs
index e728ecdfd..29ba10dbb 100644
--- a/MediaBrowser.Model/MediaInfo/BlurayDiscInfo.cs
+++ b/MediaBrowser.Model/MediaInfo/BlurayDiscInfo.cs
@@ -1,9 +1,11 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Model.Entities;
namespace MediaBrowser.Model.MediaInfo
{
/// <summary>
- /// Represents the result of BDInfo output
+ /// Represents the result of BDInfo output.
/// </summary>
public class BlurayDiscInfo
{
diff --git a/MediaBrowser.Model/MediaInfo/Container.cs b/MediaBrowser.Model/MediaInfo/Container.cs
deleted file mode 100644
index f8d56702d..000000000
--- a/MediaBrowser.Model/MediaInfo/Container.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace MediaBrowser.Model.MediaInfo
-{
- public class Container
- {
- public const string MP4 = "mp4";
- public const string MKV = "mkv";
- }
-}
diff --git a/MediaBrowser.Model/MediaInfo/IBlurayExaminer.cs b/MediaBrowser.Model/MediaInfo/IBlurayExaminer.cs
index 27137ab26..5b7d1d03c 100644
--- a/MediaBrowser.Model/MediaInfo/IBlurayExaminer.cs
+++ b/MediaBrowser.Model/MediaInfo/IBlurayExaminer.cs
@@ -1,7 +1,7 @@
namespace MediaBrowser.Model.MediaInfo
{
/// <summary>
- /// Interface IBlurayExaminer
+ /// Interface IBlurayExaminer.
/// </summary>
public interface IBlurayExaminer
{
diff --git a/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs b/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs
index a5ae7c7a5..52348f802 100644
--- a/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs
+++ b/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using MediaBrowser.Model.Dlna;
diff --git a/MediaBrowser.Model/MediaInfo/LiveStreamResponse.cs b/MediaBrowser.Model/MediaInfo/LiveStreamResponse.cs
index dd4b69469..45b8fcce9 100644
--- a/MediaBrowser.Model/MediaInfo/LiveStreamResponse.cs
+++ b/MediaBrowser.Model/MediaInfo/LiveStreamResponse.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Model.Dto;
namespace MediaBrowser.Model.MediaInfo
diff --git a/MediaBrowser.Model/MediaInfo/MediaInfo.cs b/MediaBrowser.Model/MediaInfo/MediaInfo.cs
index 9d45a2af1..ad174f15d 100644
--- a/MediaBrowser.Model/MediaInfo/MediaInfo.cs
+++ b/MediaBrowser.Model/MediaInfo/MediaInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using MediaBrowser.Model.Dto;
@@ -14,38 +16,45 @@ namespace MediaBrowser.Model.MediaInfo
/// </summary>
/// <value>The album.</value>
public string Album { get; set; }
+
/// <summary>
/// Gets or sets the artists.
/// </summary>
/// <value>The artists.</value>
public string[] Artists { get; set; }
+
/// <summary>
/// Gets or sets the album artists.
/// </summary>
/// <value>The album artists.</value>
public string[] AlbumArtists { get; set; }
+
/// <summary>
/// Gets or sets the studios.
/// </summary>
/// <value>The studios.</value>
public string[] Studios { get; set; }
public string[] Genres { get; set; }
+ public string ShowName { get; set; }
public int? IndexNumber { get; set; }
public int? ParentIndexNumber { get; set; }
public int? ProductionYear { get; set; }
public DateTime? PremiereDate { get; set; }
public BaseItemPerson[] People { get; set; }
public Dictionary<string, string> ProviderIds { get; set; }
+
/// <summary>
/// Gets or sets the official rating.
/// </summary>
/// <value>The official rating.</value>
public string OfficialRating { get; set; }
+
/// <summary>
/// Gets or sets the official rating description.
/// </summary>
/// <value>The official rating description.</value>
public string OfficialRatingDescription { get; set; }
+
/// <summary>
/// Gets or sets the overview.
/// </summary>
diff --git a/MediaBrowser.Model/MediaInfo/MediaProtocol.cs b/MediaBrowser.Model/MediaInfo/MediaProtocol.cs
index a993f6075..b9df01f27 100644
--- a/MediaBrowser.Model/MediaInfo/MediaProtocol.cs
+++ b/MediaBrowser.Model/MediaInfo/MediaProtocol.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.MediaInfo
{
public enum MediaProtocol
diff --git a/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs b/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs
index e5fad4e11..a2f163422 100644
--- a/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs
+++ b/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using MediaBrowser.Model.Dlna;
diff --git a/MediaBrowser.Model/MediaInfo/PlaybackInfoResponse.cs b/MediaBrowser.Model/MediaInfo/PlaybackInfoResponse.cs
index 38638af42..440818c3e 100644
--- a/MediaBrowser.Model/MediaInfo/PlaybackInfoResponse.cs
+++ b/MediaBrowser.Model/MediaInfo/PlaybackInfoResponse.cs
@@ -1,15 +1,20 @@
+using System;
+using System.Collections.Generic;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto;
namespace MediaBrowser.Model.MediaInfo
{
+ /// <summary>
+ /// Class PlaybackInfoResponse.
+ /// </summary>
public class PlaybackInfoResponse
{
/// <summary>
/// Gets or sets the media sources.
/// </summary>
/// <value>The media sources.</value>
- public MediaSourceInfo[] MediaSources { get; set; }
+ public IReadOnlyList<MediaSourceInfo> MediaSources { get; set; }
/// <summary>
/// Gets or sets the play session identifier.
@@ -23,9 +28,12 @@ namespace MediaBrowser.Model.MediaInfo
/// <value>The error code.</value>
public PlaybackErrorCode? ErrorCode { get; set; }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PlaybackInfoResponse" /> class.
+ /// </summary>
public PlaybackInfoResponse()
{
- MediaSources = new MediaSourceInfo[] { };
+ MediaSources = Array.Empty<MediaSourceInfo>();
}
}
}
diff --git a/MediaBrowser.Model/MediaInfo/SubtitleFormat.cs b/MediaBrowser.Model/MediaInfo/SubtitleFormat.cs
index 208e9bab9..2bd45695a 100644
--- a/MediaBrowser.Model/MediaInfo/SubtitleFormat.cs
+++ b/MediaBrowser.Model/MediaInfo/SubtitleFormat.cs
@@ -1,6 +1,8 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.MediaInfo
{
- public class SubtitleFormat
+ public static class SubtitleFormat
{
public const string SRT = "srt";
public const string SSA = "ssa";
diff --git a/MediaBrowser.Model/MediaInfo/SubtitleTrackEvent.cs b/MediaBrowser.Model/MediaInfo/SubtitleTrackEvent.cs
index 4eb000e58..5b0ccb28a 100644
--- a/MediaBrowser.Model/MediaInfo/SubtitleTrackEvent.cs
+++ b/MediaBrowser.Model/MediaInfo/SubtitleTrackEvent.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.MediaInfo
{
public class SubtitleTrackEvent
diff --git a/MediaBrowser.Model/MediaInfo/SubtitleTrackInfo.cs b/MediaBrowser.Model/MediaInfo/SubtitleTrackInfo.cs
index c382b20c9..37f5c55da 100644
--- a/MediaBrowser.Model/MediaInfo/SubtitleTrackInfo.cs
+++ b/MediaBrowser.Model/MediaInfo/SubtitleTrackInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
diff --git a/MediaBrowser.Model/MediaInfo/TransportStreamTimestamp.cs b/MediaBrowser.Model/MediaInfo/TransportStreamTimestamp.cs
index 46ce2302e..b7ee5747a 100644
--- a/MediaBrowser.Model/MediaInfo/TransportStreamTimestamp.cs
+++ b/MediaBrowser.Model/MediaInfo/TransportStreamTimestamp.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.MediaInfo
{
public enum TransportStreamTimestamp
diff --git a/MediaBrowser.Model/MediaInfo/VideoCodec.cs b/MediaBrowser.Model/MediaInfo/VideoCodec.cs
deleted file mode 100644
index a26ce1b70..000000000
--- a/MediaBrowser.Model/MediaInfo/VideoCodec.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-namespace MediaBrowser.Model.MediaInfo
-{
- public class VideoCodec
- {
- public const string H263 = "h263";
- public const string H264 = "h264";
- public const string H265 = "h265";
- public const string MPEG4 = "mpeg4";
- public const string MPEG1 = "mpeg1video";
- public const string MPEG2 = "mpeg2video";
- public const string MSMPEG4 = "msmpeg4";
- public const string VC1 = "vc1";
- }
-}
diff --git a/MediaBrowser.Model/Net/EndPointInfo.cs b/MediaBrowser.Model/Net/EndPointInfo.cs
index b73799ea8..f5ac3d169 100644
--- a/MediaBrowser.Model/Net/EndPointInfo.cs
+++ b/MediaBrowser.Model/Net/EndPointInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Net
{
public class EndPointInfo
diff --git a/MediaBrowser.Model/Net/HttpException.cs b/MediaBrowser.Model/Net/HttpException.cs
index 16253ed6c..4b15e30f0 100644
--- a/MediaBrowser.Model/Net/HttpException.cs
+++ b/MediaBrowser.Model/Net/HttpException.cs
@@ -4,7 +4,7 @@ using System.Net;
namespace MediaBrowser.Model.Net
{
/// <summary>
- /// Class HttpException
+ /// Class HttpException.
/// </summary>
public class HttpException : Exception
{
diff --git a/MediaBrowser.Model/Net/ISocket.cs b/MediaBrowser.Model/Net/ISocket.cs
index f80de5524..2bfbfcb20 100644
--- a/MediaBrowser.Model/Net/ISocket.cs
+++ b/MediaBrowser.Model/Net/ISocket.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Net;
using System.Threading;
@@ -14,8 +16,6 @@ namespace MediaBrowser.Model.Net
Task<SocketReceiveResult> ReceiveAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken);
- int Receive(byte[] buffer, int offset, int count);
-
IAsyncResult BeginReceive(byte[] buffer, int offset, int count, AsyncCallback callback);
SocketReceiveResult EndReceive(IAsyncResult result);
diff --git a/MediaBrowser.Model/Net/ISocketFactory.cs b/MediaBrowser.Model/Net/ISocketFactory.cs
index 2f857f1af..363abefc1 100644
--- a/MediaBrowser.Model/Net/ISocketFactory.cs
+++ b/MediaBrowser.Model/Net/ISocketFactory.cs
@@ -1,4 +1,5 @@
-using System.IO;
+#pragma warning disable CS1591
+
using System.Net;
namespace MediaBrowser.Model.Net
@@ -8,13 +9,6 @@ namespace MediaBrowser.Model.Net
/// </summary>
public interface ISocketFactory
{
- /// <summary>
- /// Creates a new unicast socket using the specified local port number.
- /// </summary>
- /// <param name="localPort">The local port to bind to.</param>
- /// <returns>A <see cref="ISocket"/> implementation.</returns>
- ISocket CreateUdpSocket(int localPort);
-
ISocket CreateUdpBroadcastSocket(int localPort);
/// <summary>
@@ -30,7 +24,5 @@ namespace MediaBrowser.Model.Net
/// <param name="localPort">The local port to bind to.</param>
/// <returns>A <see cref="ISocket"/> implementation.</returns>
ISocket CreateUdpMulticastSocket(string ipAddress, int multicastTimeToLive, int localPort);
-
- Stream CreateNetworkStream(ISocket socket, bool ownsSocket);
}
}
diff --git a/MediaBrowser.Model/Net/MimeTypes.cs b/MediaBrowser.Model/Net/MimeTypes.cs
index de5e58d22..68bcc590c 100644
--- a/MediaBrowser.Model/Net/MimeTypes.cs
+++ b/MediaBrowser.Model/Net/MimeTypes.cs
@@ -1,8 +1,9 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Model.Net
{
@@ -63,6 +64,7 @@ namespace MediaBrowser.Model.Net
{ ".m3u8", "application/x-mpegURL" },
{ ".mobi", "application/x-mobipocket-ebook" },
{ ".xml", "application/xml" },
+ { ".wasm", "application/wasm" },
// Type image
{ ".jpg", "image/jpeg" },
@@ -165,20 +167,20 @@ namespace MediaBrowser.Model.Net
}
// Type text
- if (StringHelper.EqualsIgnoreCase(ext, ".html")
- || StringHelper.EqualsIgnoreCase(ext, ".htm"))
+ if (string.Equals(ext, ".html", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(ext, ".htm", StringComparison.OrdinalIgnoreCase))
{
return "text/html; charset=UTF-8";
}
- if (StringHelper.EqualsIgnoreCase(ext, ".log")
- || StringHelper.EqualsIgnoreCase(ext, ".srt"))
+ if (string.Equals(ext, ".log", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(ext, ".srt", StringComparison.OrdinalIgnoreCase))
{
return "text/plain";
}
// Misc
- if (StringHelper.EqualsIgnoreCase(ext, ".dll"))
+ if (string.Equals(ext, ".dll", StringComparison.OrdinalIgnoreCase))
{
return "application/octet-stream";
}
diff --git a/MediaBrowser.Model/Net/NetworkShare.cs b/MediaBrowser.Model/Net/NetworkShare.cs
index 1f61414fc..744c6ec14 100644
--- a/MediaBrowser.Model/Net/NetworkShare.cs
+++ b/MediaBrowser.Model/Net/NetworkShare.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Net
{
public class NetworkShare
diff --git a/MediaBrowser.Model/Net/NetworkShareType.cs b/MediaBrowser.Model/Net/NetworkShareType.cs
index bf2d092a6..5d985f85d 100644
--- a/MediaBrowser.Model/Net/NetworkShareType.cs
+++ b/MediaBrowser.Model/Net/NetworkShareType.cs
@@ -1,28 +1,32 @@
namespace MediaBrowser.Model.Net
{
/// <summary>
- /// Enum NetworkShareType
+ /// Enum NetworkShareType.
/// </summary>
public enum NetworkShareType
{
/// <summary>
- /// Disk share
+ /// Disk share.
/// </summary>
Disk,
+
/// <summary>
- /// Printer share
+ /// Printer share.
/// </summary>
Printer,
+
/// <summary>
- /// Device share
+ /// Device share.
/// </summary>
Device,
+
/// <summary>
- /// IPC share
+ /// IPC share.
/// </summary>
Ipc,
+
/// <summary>
- /// Special share
+ /// Special share.
/// </summary>
Special
}
diff --git a/MediaBrowser.Model/Net/SocketReceiveResult.cs b/MediaBrowser.Model/Net/SocketReceiveResult.cs
index 3a4ad3738..141ae1608 100644
--- a/MediaBrowser.Model/Net/SocketReceiveResult.cs
+++ b/MediaBrowser.Model/Net/SocketReceiveResult.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Net;
namespace MediaBrowser.Model.Net
diff --git a/MediaBrowser.Model/Net/WebSocketMessage.cs b/MediaBrowser.Model/Net/WebSocketMessage.cs
index c763216f1..7575224d4 100644
--- a/MediaBrowser.Model/Net/WebSocketMessage.cs
+++ b/MediaBrowser.Model/Net/WebSocketMessage.cs
@@ -1,7 +1,9 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Net
{
/// <summary>
- /// Class WebSocketMessage
+ /// Class WebSocketMessage.
/// </summary>
/// <typeparam name="T"></typeparam>
public class WebSocketMessage<T>
@@ -13,6 +15,7 @@ namespace MediaBrowser.Model.Net
public string MessageType { get; set; }
public string MessageId { get; set; }
public string ServerId { get; set; }
+
/// <summary>
/// Gets or sets the data.
/// </summary>
diff --git a/MediaBrowser.Model/Notifications/NotificationLevel.cs b/MediaBrowser.Model/Notifications/NotificationLevel.cs
index 6a838b125..14fead3f0 100644
--- a/MediaBrowser.Model/Notifications/NotificationLevel.cs
+++ b/MediaBrowser.Model/Notifications/NotificationLevel.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Notifications
{
public enum NotificationLevel
diff --git a/MediaBrowser.Model/Notifications/NotificationOption.cs b/MediaBrowser.Model/Notifications/NotificationOption.cs
index 51a07370f..4fb724515 100644
--- a/MediaBrowser.Model/Notifications/NotificationOption.cs
+++ b/MediaBrowser.Model/Notifications/NotificationOption.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace MediaBrowser.Model.Notifications
diff --git a/MediaBrowser.Model/Notifications/NotificationOptions.cs b/MediaBrowser.Model/Notifications/NotificationOptions.cs
index f48b5ee7f..79a128e9b 100644
--- a/MediaBrowser.Model/Notifications/NotificationOptions.cs
+++ b/MediaBrowser.Model/Notifications/NotificationOptions.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.Users;
@@ -79,7 +81,7 @@ namespace MediaBrowser.Model.Notifications
{
foreach (NotificationOption i in Options)
{
- if (StringHelper.EqualsIgnoreCase(type, i.Type)) return i;
+ if (string.Equals(type, i.Type, StringComparison.OrdinalIgnoreCase)) return i;
}
return null;
}
diff --git a/MediaBrowser.Model/Notifications/NotificationRequest.cs b/MediaBrowser.Model/Notifications/NotificationRequest.cs
index 5a2634e73..ffcfab24f 100644
--- a/MediaBrowser.Model/Notifications/NotificationRequest.cs
+++ b/MediaBrowser.Model/Notifications/NotificationRequest.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace MediaBrowser.Model.Notifications
diff --git a/MediaBrowser.Model/Notifications/NotificationType.cs b/MediaBrowser.Model/Notifications/NotificationType.cs
index 38b519a14..d58fbbc21 100644
--- a/MediaBrowser.Model/Notifications/NotificationType.cs
+++ b/MediaBrowser.Model/Notifications/NotificationType.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Notifications
{
public enum NotificationType
diff --git a/MediaBrowser.Model/Notifications/NotificationTypeInfo.cs b/MediaBrowser.Model/Notifications/NotificationTypeInfo.cs
index ff957e644..bfa163b40 100644
--- a/MediaBrowser.Model/Notifications/NotificationTypeInfo.cs
+++ b/MediaBrowser.Model/Notifications/NotificationTypeInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Notifications
{
public class NotificationTypeInfo
diff --git a/MediaBrowser.Model/Notifications/SendToUserType.cs b/MediaBrowser.Model/Notifications/SendToUserType.cs
index 9f63d24bf..65fc4e1ab 100644
--- a/MediaBrowser.Model/Notifications/SendToUserType.cs
+++ b/MediaBrowser.Model/Notifications/SendToUserType.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Notifications
{
public enum SendToUserType
diff --git a/MediaBrowser.Model/Playlists/PlaylistCreationRequest.cs b/MediaBrowser.Model/Playlists/PlaylistCreationRequest.cs
index 007965c0f..b7003c4c8 100644
--- a/MediaBrowser.Model/Playlists/PlaylistCreationRequest.cs
+++ b/MediaBrowser.Model/Playlists/PlaylistCreationRequest.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace MediaBrowser.Model.Playlists
diff --git a/MediaBrowser.Model/Playlists/PlaylistCreationResult.cs b/MediaBrowser.Model/Playlists/PlaylistCreationResult.cs
index 301ae66a7..4f2067b98 100644
--- a/MediaBrowser.Model/Playlists/PlaylistCreationResult.cs
+++ b/MediaBrowser.Model/Playlists/PlaylistCreationResult.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Playlists
{
public class PlaylistCreationResult
diff --git a/MediaBrowser.Model/Playlists/PlaylistItemQuery.cs b/MediaBrowser.Model/Playlists/PlaylistItemQuery.cs
index 1f03c14d3..324a38e70 100644
--- a/MediaBrowser.Model/Playlists/PlaylistItemQuery.cs
+++ b/MediaBrowser.Model/Playlists/PlaylistItemQuery.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Model.Querying;
namespace MediaBrowser.Model.Playlists
diff --git a/MediaBrowser.Model/Plugins/IHasWebPages.cs b/MediaBrowser.Model/Plugins/IHasWebPages.cs
index 5bda7e65e..765c2d373 100644
--- a/MediaBrowser.Model/Plugins/IHasWebPages.cs
+++ b/MediaBrowser.Model/Plugins/IHasWebPages.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
namespace MediaBrowser.Model.Plugins
diff --git a/MediaBrowser.Model/Plugins/PluginPageInfo.cs b/MediaBrowser.Model/Plugins/PluginPageInfo.cs
index 8ed2064b9..eb6a1527d 100644
--- a/MediaBrowser.Model/Plugins/PluginPageInfo.cs
+++ b/MediaBrowser.Model/Plugins/PluginPageInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Plugins
{
public class PluginPageInfo
diff --git a/MediaBrowser.Model/Providers/ExternalIdInfo.cs b/MediaBrowser.Model/Providers/ExternalIdInfo.cs
index bff84c553..2b481ad7e 100644
--- a/MediaBrowser.Model/Providers/ExternalIdInfo.cs
+++ b/MediaBrowser.Model/Providers/ExternalIdInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Providers
{
public class ExternalIdInfo
diff --git a/MediaBrowser.Model/Providers/ExternalUrl.cs b/MediaBrowser.Model/Providers/ExternalUrl.cs
index 69cead92a..d4f4fa840 100644
--- a/MediaBrowser.Model/Providers/ExternalUrl.cs
+++ b/MediaBrowser.Model/Providers/ExternalUrl.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Providers
{
public class ExternalUrl
diff --git a/MediaBrowser.Model/Providers/ImageProviderInfo.cs b/MediaBrowser.Model/Providers/ImageProviderInfo.cs
index 1c4cff373..a22ec3c07 100644
--- a/MediaBrowser.Model/Providers/ImageProviderInfo.cs
+++ b/MediaBrowser.Model/Providers/ImageProviderInfo.cs
@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+
+using System;
using MediaBrowser.Model.Entities;
namespace MediaBrowser.Model.Providers
@@ -17,7 +20,7 @@ namespace MediaBrowser.Model.Providers
public ImageProviderInfo()
{
- SupportedImages = new ImageType[] { };
+ SupportedImages = Array.Empty<ImageType>();
}
}
}
diff --git a/MediaBrowser.Model/Providers/RemoteImageInfo.cs b/MediaBrowser.Model/Providers/RemoteImageInfo.cs
index aacd108ec..ee2b9d8fd 100644
--- a/MediaBrowser.Model/Providers/RemoteImageInfo.cs
+++ b/MediaBrowser.Model/Providers/RemoteImageInfo.cs
@@ -4,7 +4,7 @@ using MediaBrowser.Model.Entities;
namespace MediaBrowser.Model.Providers
{
/// <summary>
- /// Class RemoteImageInfo
+ /// Class RemoteImageInfo.
/// </summary>
public class RemoteImageInfo
{
@@ -21,7 +21,7 @@ namespace MediaBrowser.Model.Providers
public string Url { get; set; }
/// <summary>
- /// Gets a url used for previewing a smaller version
+ /// Gets a url used for previewing a smaller version.
/// </summary>
public string ThumbnailUrl { get; set; }
diff --git a/MediaBrowser.Model/Providers/RemoteImageQuery.cs b/MediaBrowser.Model/Providers/RemoteImageQuery.cs
index 7c9216ce7..2873c1003 100644
--- a/MediaBrowser.Model/Providers/RemoteImageQuery.cs
+++ b/MediaBrowser.Model/Providers/RemoteImageQuery.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Model.Entities;
namespace MediaBrowser.Model.Providers
diff --git a/MediaBrowser.Model/Providers/RemoteSearchResult.cs b/MediaBrowser.Model/Providers/RemoteSearchResult.cs
index 6e46b1556..161e04821 100644
--- a/MediaBrowser.Model/Providers/RemoteSearchResult.cs
+++ b/MediaBrowser.Model/Providers/RemoteSearchResult.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using MediaBrowser.Model.Entities;
diff --git a/MediaBrowser.Model/Providers/RemoteSubtitleInfo.cs b/MediaBrowser.Model/Providers/RemoteSubtitleInfo.cs
index 861aabf72..06f29df3f 100644
--- a/MediaBrowser.Model/Providers/RemoteSubtitleInfo.cs
+++ b/MediaBrowser.Model/Providers/RemoteSubtitleInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace MediaBrowser.Model.Providers
diff --git a/MediaBrowser.Model/Providers/SubtitleOptions.cs b/MediaBrowser.Model/Providers/SubtitleOptions.cs
index fde816dd3..9e6049246 100644
--- a/MediaBrowser.Model/Providers/SubtitleOptions.cs
+++ b/MediaBrowser.Model/Providers/SubtitleOptions.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace MediaBrowser.Model.Providers
diff --git a/MediaBrowser.Model/Providers/SubtitleProviderInfo.cs b/MediaBrowser.Model/Providers/SubtitleProviderInfo.cs
index 48a247818..fca93d176 100644
--- a/MediaBrowser.Model/Providers/SubtitleProviderInfo.cs
+++ b/MediaBrowser.Model/Providers/SubtitleProviderInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Providers
{
public class SubtitleProviderInfo
diff --git a/MediaBrowser.Model/Querying/AllThemeMediaResult.cs b/MediaBrowser.Model/Querying/AllThemeMediaResult.cs
index f843a33e6..a264c6178 100644
--- a/MediaBrowser.Model/Querying/AllThemeMediaResult.cs
+++ b/MediaBrowser.Model/Querying/AllThemeMediaResult.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Querying
{
public class AllThemeMediaResult
diff --git a/MediaBrowser.Model/Querying/EpisodeQuery.cs b/MediaBrowser.Model/Querying/EpisodeQuery.cs
index 0c8ea7ed4..6fb4df676 100644
--- a/MediaBrowser.Model/Querying/EpisodeQuery.cs
+++ b/MediaBrowser.Model/Querying/EpisodeQuery.cs
@@ -1,3 +1,7 @@
+#pragma warning disable CS1591
+
+using System;
+
namespace MediaBrowser.Model.Querying
{
public class EpisodeQuery
@@ -7,46 +11,55 @@ namespace MediaBrowser.Model.Querying
/// </summary>
/// <value>The user identifier.</value>
public string UserId { get; set; }
+
/// <summary>
/// Gets or sets the season identifier.
/// </summary>
/// <value>The season identifier.</value>
public string SeasonId { get; set; }
+
/// <summary>
/// Gets or sets the series identifier.
/// </summary>
/// <value>The series identifier.</value>
public string SeriesId { get; set; }
+
/// <summary>
/// Gets or sets a value indicating whether this instance is missing.
/// </summary>
/// <value><c>null</c> if [is missing] contains no value, <c>true</c> if [is missing]; otherwise, <c>false</c>.</value>
public bool? IsMissing { get; set; }
+
/// <summary>
/// Gets or sets a value indicating whether this instance is virtual unaired.
/// </summary>
/// <value><c>null</c> if [is virtual unaired] contains no value, <c>true</c> if [is virtual unaired]; otherwise, <c>false</c>.</value>
public bool? IsVirtualUnaired { get; set; }
+
/// <summary>
/// Gets or sets the season number.
/// </summary>
/// <value>The season number.</value>
public int? SeasonNumber { get; set; }
+
/// <summary>
/// Gets or sets the fields.
/// </summary>
/// <value>The fields.</value>
public ItemFields[] Fields { get; set; }
+
/// <summary>
/// Gets or sets the start index.
/// </summary>
/// <value>The start index.</value>
public int? StartIndex { get; set; }
+
/// <summary>
/// Gets or sets the limit.
/// </summary>
/// <value>The limit.</value>
public int? Limit { get; set; }
+
/// <summary>
/// Gets or sets the start item identifier.
/// </summary>
@@ -55,7 +68,7 @@ namespace MediaBrowser.Model.Querying
public EpisodeQuery()
{
- Fields = new ItemFields[] { };
+ Fields = Array.Empty<ItemFields>();
}
}
}
diff --git a/MediaBrowser.Model/Querying/ItemCountsQuery.cs b/MediaBrowser.Model/Querying/ItemCountsQuery.cs
index 02b809fa8..f113cf380 100644
--- a/MediaBrowser.Model/Querying/ItemCountsQuery.cs
+++ b/MediaBrowser.Model/Querying/ItemCountsQuery.cs
@@ -1,7 +1,7 @@
namespace MediaBrowser.Model.Querying
{
/// <summary>
- /// Class ItemCountsQuery
+ /// Class ItemCountsQuery.
/// </summary>
public class ItemCountsQuery
{
diff --git a/MediaBrowser.Model/Querying/ItemFields.cs b/MediaBrowser.Model/Querying/ItemFields.cs
index af1aaf486..d7cc5ebbe 100644
--- a/MediaBrowser.Model/Querying/ItemFields.cs
+++ b/MediaBrowser.Model/Querying/ItemFields.cs
@@ -1,7 +1,9 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Querying
{
/// <summary>
- /// Used to control the data that gets attached to DtoBaseItems
+ /// Used to control the data that gets attached to DtoBaseItems.
/// </summary>
public enum ItemFields
{
diff --git a/MediaBrowser.Model/Querying/ItemFilter.cs b/MediaBrowser.Model/Querying/ItemFilter.cs
index b8dcfbdad..0ebb5185f 100644
--- a/MediaBrowser.Model/Querying/ItemFilter.cs
+++ b/MediaBrowser.Model/Querying/ItemFilter.cs
@@ -1,44 +1,52 @@
namespace MediaBrowser.Model.Querying
{
/// <summary>
- /// Enum ItemFilter
+ /// Enum ItemFilter.
/// </summary>
public enum ItemFilter
{
/// <summary>
- /// The item is a folder
+ /// The item is a folder.
/// </summary>
IsFolder = 1,
+
/// <summary>
- /// The item is not folder
+ /// The item is not folder.
/// </summary>
IsNotFolder = 2,
+
/// <summary>
- /// The item is unplayed
+ /// The item is unplayed.
/// </summary>
IsUnplayed = 3,
+
/// <summary>
- /// The item is played
+ /// The item is played.
/// </summary>
IsPlayed = 4,
+
/// <summary>
- /// The item is a favorite
+ /// The item is a favorite.
/// </summary>
IsFavorite = 5,
+
/// <summary>
- /// The item is resumable
+ /// The item is resumable.
/// </summary>
IsResumable = 7,
+
/// <summary>
- /// The likes
+ /// The likes.
/// </summary>
Likes = 8,
+
/// <summary>
- /// The dislikes
+ /// The dislikes.
/// </summary>
Dislikes = 9,
+
/// <summary>
- /// The is favorite or likes
+ /// The is favorite or likes.
/// </summary>
IsFavoriteOrLikes = 10
}
diff --git a/MediaBrowser.Model/Querying/ItemSortBy.cs b/MediaBrowser.Model/Querying/ItemSortBy.cs
index 6a71e3bb3..15b60ad84 100644
--- a/MediaBrowser.Model/Querying/ItemSortBy.cs
+++ b/MediaBrowser.Model/Querying/ItemSortBy.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Querying
{
/// <summary>
diff --git a/MediaBrowser.Model/Querying/LatestItemsQuery.cs b/MediaBrowser.Model/Querying/LatestItemsQuery.cs
index 4a5818ac5..84e29e76a 100644
--- a/MediaBrowser.Model/Querying/LatestItemsQuery.cs
+++ b/MediaBrowser.Model/Querying/LatestItemsQuery.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using MediaBrowser.Model.Entities;
diff --git a/MediaBrowser.Model/Querying/MovieRecommendationQuery.cs b/MediaBrowser.Model/Querying/MovieRecommendationQuery.cs
index 52c138355..93de0a8cd 100644
--- a/MediaBrowser.Model/Querying/MovieRecommendationQuery.cs
+++ b/MediaBrowser.Model/Querying/MovieRecommendationQuery.cs
@@ -1,3 +1,7 @@
+#pragma warning disable CS1591
+
+using System;
+
namespace MediaBrowser.Model.Querying
{
public class MovieRecommendationQuery
@@ -7,21 +11,25 @@ namespace MediaBrowser.Model.Querying
/// </summary>
/// <value>The user identifier.</value>
public string UserId { get; set; }
+
/// <summary>
/// Gets or sets the parent identifier.
/// </summary>
/// <value>The parent identifier.</value>
public string ParentId { get; set; }
+
/// <summary>
/// Gets or sets the item limit.
/// </summary>
/// <value>The item limit.</value>
public int ItemLimit { get; set; }
+
/// <summary>
/// Gets or sets the category limit.
/// </summary>
/// <value>The category limit.</value>
public int CategoryLimit { get; set; }
+
/// <summary>
/// Gets or sets the fields.
/// </summary>
@@ -32,7 +40,7 @@ namespace MediaBrowser.Model.Querying
{
ItemLimit = 10;
CategoryLimit = 6;
- Fields = new ItemFields[] { };
+ Fields = Array.Empty<ItemFields>();
}
}
}
diff --git a/MediaBrowser.Model/Querying/NextUpQuery.cs b/MediaBrowser.Model/Querying/NextUpQuery.cs
index ff146cede..1543aea16 100644
--- a/MediaBrowser.Model/Querying/NextUpQuery.cs
+++ b/MediaBrowser.Model/Querying/NextUpQuery.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using MediaBrowser.Model.Entities;
@@ -40,16 +42,19 @@ namespace MediaBrowser.Model.Querying
/// </summary>
/// <value>The fields.</value>
public ItemFields[] Fields { get; set; }
+
/// <summary>
/// Gets or sets a value indicating whether [enable images].
/// </summary>
/// <value><c>null</c> if [enable images] contains no value, <c>true</c> if [enable images]; otherwise, <c>false</c>.</value>
public bool? EnableImages { get; set; }
+
/// <summary>
/// Gets or sets the image type limit.
/// </summary>
/// <value>The image type limit.</value>
public int? ImageTypeLimit { get; set; }
+
/// <summary>
/// Gets or sets the enable image types.
/// </summary>
@@ -60,7 +65,7 @@ namespace MediaBrowser.Model.Querying
public NextUpQuery()
{
- EnableImageTypes = new ImageType[] { };
+ EnableImageTypes = Array.Empty<ImageType>();
EnableTotalRecordCount = true;
}
}
diff --git a/MediaBrowser.Model/Querying/QueryFilters.cs b/MediaBrowser.Model/Querying/QueryFilters.cs
index 2f38299db..8d879c174 100644
--- a/MediaBrowser.Model/Querying/QueryFilters.cs
+++ b/MediaBrowser.Model/Querying/QueryFilters.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using MediaBrowser.Model.Dto;
diff --git a/MediaBrowser.Model/Querying/QueryResult.cs b/MediaBrowser.Model/Querying/QueryResult.cs
index 221645afb..266f1c7e6 100644
--- a/MediaBrowser.Model/Querying/QueryResult.cs
+++ b/MediaBrowser.Model/Querying/QueryResult.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
@@ -27,5 +29,11 @@ namespace MediaBrowser.Model.Querying
{
Items = Array.Empty<T>();
}
+
+ public QueryResult(IReadOnlyList<T> items)
+ {
+ Items = items;
+ TotalRecordCount = items.Count;
+ }
}
}
diff --git a/MediaBrowser.Model/Querying/SessionQuery.cs b/MediaBrowser.Model/Querying/SessionQuery.cs
deleted file mode 100644
index 1fac9d639..000000000
--- a/MediaBrowser.Model/Querying/SessionQuery.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-namespace MediaBrowser.Model.Querying
-{
- /// <summary>
- /// Class SessionQuery
- /// </summary>
- public class SessionQuery
- {
- /// <summary>
- /// Filter by sessions that are allowed to be controlled by a given user
- /// </summary>
- public string ControllableByUserId { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Querying/SimilarItemsQuery.cs b/MediaBrowser.Model/Querying/SimilarItemsQuery.cs
deleted file mode 100644
index 68f761bd4..000000000
--- a/MediaBrowser.Model/Querying/SimilarItemsQuery.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-namespace MediaBrowser.Model.Querying
-{
- public class SimilarItemsQuery
- {
- /// <summary>
- /// The user to localize search results for
- /// </summary>
- /// <value>The user id.</value>
- public string UserId { get; set; }
-
- /// <summary>
- /// Gets or sets the id.
- /// </summary>
- /// <value>The id.</value>
- public string Id { get; set; }
-
- /// <summary>
- /// The maximum number of items to return
- /// </summary>
- /// <value>The limit.</value>
- public int? Limit { get; set; }
-
- /// <summary>
- /// Fields to return within the items, in addition to basic information
- /// </summary>
- /// <value>The fields.</value>
- public ItemFields[] Fields { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Querying/UpcomingEpisodesQuery.cs b/MediaBrowser.Model/Querying/UpcomingEpisodesQuery.cs
index 5eac2860d..123d0fad2 100644
--- a/MediaBrowser.Model/Querying/UpcomingEpisodesQuery.cs
+++ b/MediaBrowser.Model/Querying/UpcomingEpisodesQuery.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Model.Entities;
namespace MediaBrowser.Model.Querying
diff --git a/MediaBrowser.Model/Querying/UserQuery.cs b/MediaBrowser.Model/Querying/UserQuery.cs
deleted file mode 100644
index 55cef664e..000000000
--- a/MediaBrowser.Model/Querying/UserQuery.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace MediaBrowser.Model.Querying
-{
- public class UserQuery
- {
- public bool? IsHidden { get; set; }
- public bool? IsDisabled { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Search/SearchHint.cs b/MediaBrowser.Model/Search/SearchHint.cs
index 8f4824903..6e52314fa 100644
--- a/MediaBrowser.Model/Search/SearchHint.cs
+++ b/MediaBrowser.Model/Search/SearchHint.cs
@@ -1,10 +1,12 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
namespace MediaBrowser.Model.Search
{
/// <summary>
- /// Class SearchHintResult
+ /// Class SearchHintResult.
/// </summary>
public class SearchHint
{
diff --git a/MediaBrowser.Model/Search/SearchHintResult.cs b/MediaBrowser.Model/Search/SearchHintResult.cs
index 069e7e025..3c4fbec9e 100644
--- a/MediaBrowser.Model/Search/SearchHintResult.cs
+++ b/MediaBrowser.Model/Search/SearchHintResult.cs
@@ -1,7 +1,7 @@
namespace MediaBrowser.Model.Search
{
/// <summary>
- /// Class SearchHintResult
+ /// Class SearchHintResult.
/// </summary>
public class SearchHintResult
{
diff --git a/MediaBrowser.Model/Search/SearchQuery.cs b/MediaBrowser.Model/Search/SearchQuery.cs
index 96a8cb00c..8a018312e 100644
--- a/MediaBrowser.Model/Search/SearchQuery.cs
+++ b/MediaBrowser.Model/Search/SearchQuery.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace MediaBrowser.Model.Search
diff --git a/MediaBrowser.Model/Serialization/IJsonSerializer.cs b/MediaBrowser.Model/Serialization/IJsonSerializer.cs
index 18f51f652..6223bb559 100644
--- a/MediaBrowser.Model/Serialization/IJsonSerializer.cs
+++ b/MediaBrowser.Model/Serialization/IJsonSerializer.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.IO;
using System.Threading.Tasks;
diff --git a/MediaBrowser.Model/Serialization/IXmlSerializer.cs b/MediaBrowser.Model/Serialization/IXmlSerializer.cs
index 902ebd4d1..1edd98fad 100644
--- a/MediaBrowser.Model/Serialization/IXmlSerializer.cs
+++ b/MediaBrowser.Model/Serialization/IXmlSerializer.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.IO;
diff --git a/MediaBrowser.Model/Services/ApiMemberAttribute.cs b/MediaBrowser.Model/Services/ApiMemberAttribute.cs
index 8b155c8ab..8e50836f4 100644
--- a/MediaBrowser.Model/Services/ApiMemberAttribute.cs
+++ b/MediaBrowser.Model/Services/ApiMemberAttribute.cs
@@ -2,6 +2,9 @@ using System;
namespace MediaBrowser.Model.Services
{
+ /// <summary>
+ /// Identifies a single API endpoint.
+ /// </summary>
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = true)]
public class ApiMemberAttribute : Attribute
{
diff --git a/MediaBrowser.Model/Services/IAsyncStreamWriter.cs b/MediaBrowser.Model/Services/IAsyncStreamWriter.cs
index f16a877e6..afbca78a2 100644
--- a/MediaBrowser.Model/Services/IAsyncStreamWriter.cs
+++ b/MediaBrowser.Model/Services/IAsyncStreamWriter.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.IO;
using System.Threading;
using System.Threading.Tasks;
diff --git a/MediaBrowser.Model/Services/IHasHeaders.cs b/MediaBrowser.Model/Services/IHasHeaders.cs
index b2d413b70..313f34b41 100644
--- a/MediaBrowser.Model/Services/IHasHeaders.cs
+++ b/MediaBrowser.Model/Services/IHasHeaders.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
namespace MediaBrowser.Model.Services
diff --git a/MediaBrowser.Model/Services/IHasRequestFilter.cs b/MediaBrowser.Model/Services/IHasRequestFilter.cs
index 81a2dba69..3d2e9c0dc 100644
--- a/MediaBrowser.Model/Services/IHasRequestFilter.cs
+++ b/MediaBrowser.Model/Services/IHasRequestFilter.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using Microsoft.AspNetCore.Http;
namespace MediaBrowser.Model.Services
diff --git a/MediaBrowser.Model/Services/IHttpRequest.cs b/MediaBrowser.Model/Services/IHttpRequest.cs
index daf91488f..4dccd2d68 100644
--- a/MediaBrowser.Model/Services/IHttpRequest.cs
+++ b/MediaBrowser.Model/Services/IHttpRequest.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Services
{
public interface IHttpRequest : IRequest
diff --git a/MediaBrowser.Model/Services/IHttpResult.cs b/MediaBrowser.Model/Services/IHttpResult.cs
index bfa30f60d..b153f15ec 100644
--- a/MediaBrowser.Model/Services/IHttpResult.cs
+++ b/MediaBrowser.Model/Services/IHttpResult.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Net;
namespace MediaBrowser.Model.Services
diff --git a/MediaBrowser.Model/Services/IRequest.cs b/MediaBrowser.Model/Services/IRequest.cs
index 7a4152698..3f4edced6 100644
--- a/MediaBrowser.Model/Services/IRequest.cs
+++ b/MediaBrowser.Model/Services/IRequest.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.IO;
diff --git a/MediaBrowser.Model/Services/IRequiresRequestStream.cs b/MediaBrowser.Model/Services/IRequiresRequestStream.cs
index 2f17c6a9a..622626edc 100644
--- a/MediaBrowser.Model/Services/IRequiresRequestStream.cs
+++ b/MediaBrowser.Model/Services/IRequiresRequestStream.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.IO;
namespace MediaBrowser.Model.Services
diff --git a/MediaBrowser.Model/Services/IService.cs b/MediaBrowser.Model/Services/IService.cs
index 8f2e63e98..a26d39455 100644
--- a/MediaBrowser.Model/Services/IService.cs
+++ b/MediaBrowser.Model/Services/IService.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Services
{
// marker interface
diff --git a/MediaBrowser.Model/Services/IStreamWriter.cs b/MediaBrowser.Model/Services/IStreamWriter.cs
index 9d65cff63..3ebfef66b 100644
--- a/MediaBrowser.Model/Services/IStreamWriter.cs
+++ b/MediaBrowser.Model/Services/IStreamWriter.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.IO;
namespace MediaBrowser.Model.Services
diff --git a/MediaBrowser.Model/Services/QueryParamCollection.cs b/MediaBrowser.Model/Services/QueryParamCollection.cs
index 7708db00a..19e9e53e7 100644
--- a/MediaBrowser.Model/Services/QueryParamCollection.cs
+++ b/MediaBrowser.Model/Services/QueryParamCollection.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Linq;
diff --git a/MediaBrowser.Model/Services/RouteAttribute.cs b/MediaBrowser.Model/Services/RouteAttribute.cs
index f6316e2b1..197ba05e5 100644
--- a/MediaBrowser.Model/Services/RouteAttribute.cs
+++ b/MediaBrowser.Model/Services/RouteAttribute.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace MediaBrowser.Model.Services
diff --git a/MediaBrowser.Model/Session/BrowseRequest.cs b/MediaBrowser.Model/Session/BrowseRequest.cs
index 65d58501b..f485d680e 100644
--- a/MediaBrowser.Model/Session/BrowseRequest.cs
+++ b/MediaBrowser.Model/Session/BrowseRequest.cs
@@ -1,12 +1,12 @@
namespace MediaBrowser.Model.Session
{
/// <summary>
- /// Class BrowseRequest
+ /// Class BrowseRequest.
/// </summary>
public class BrowseRequest
{
/// <summary>
- /// Artist, Genre, Studio, Person, or any kind of BaseItem
+ /// Artist, Genre, Studio, Person, or any kind of BaseItem.
/// </summary>
/// <value>The type of the item.</value>
public string ItemType { get; set; }
diff --git a/MediaBrowser.Model/Session/ClientCapabilities.cs b/MediaBrowser.Model/Session/ClientCapabilities.cs
index fa74efb1b..5da4998e8 100644
--- a/MediaBrowser.Model/Session/ClientCapabilities.cs
+++ b/MediaBrowser.Model/Session/ClientCapabilities.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using MediaBrowser.Model.Dlna;
diff --git a/MediaBrowser.Model/Session/GeneralCommand.cs b/MediaBrowser.Model/Session/GeneralCommand.cs
index 74e58e678..980e1f88b 100644
--- a/MediaBrowser.Model/Session/GeneralCommand.cs
+++ b/MediaBrowser.Model/Session/GeneralCommand.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
diff --git a/MediaBrowser.Model/Session/GeneralCommandType.cs b/MediaBrowser.Model/Session/GeneralCommandType.cs
index 4bb0c5cc5..5a9042d5f 100644
--- a/MediaBrowser.Model/Session/GeneralCommandType.cs
+++ b/MediaBrowser.Model/Session/GeneralCommandType.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Session
{
/// <summary>
diff --git a/MediaBrowser.Model/Session/MessageCommand.cs b/MediaBrowser.Model/Session/MessageCommand.cs
index 1e558ef07..473a7bccc 100644
--- a/MediaBrowser.Model/Session/MessageCommand.cs
+++ b/MediaBrowser.Model/Session/MessageCommand.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Session
{
public class MessageCommand
diff --git a/MediaBrowser.Model/Session/PlayCommand.cs b/MediaBrowser.Model/Session/PlayCommand.cs
index b7a8f39ba..3ab049320 100644
--- a/MediaBrowser.Model/Session/PlayCommand.cs
+++ b/MediaBrowser.Model/Session/PlayCommand.cs
@@ -1,28 +1,32 @@
namespace MediaBrowser.Model.Session
{
/// <summary>
- /// Enum PlayCommand
+ /// Enum PlayCommand.
/// </summary>
public enum PlayCommand
{
/// <summary>
- /// The play now
+ /// The play now.
/// </summary>
PlayNow = 0,
+
/// <summary>
- /// The play next
+ /// The play next.
/// </summary>
PlayNext = 1,
+
/// <summary>
- /// The play last
+ /// The play last.
/// </summary>
PlayLast = 2,
+
/// <summary>
- /// The play instant mix
+ /// The play instant mix.
/// </summary>
PlayInstantMix = 3,
+
/// <summary>
- /// The play shuffle
+ /// The play shuffle.
/// </summary>
PlayShuffle = 4
}
diff --git a/MediaBrowser.Model/Session/PlayMethod.cs b/MediaBrowser.Model/Session/PlayMethod.cs
index 8daf8c953..806762784 100644
--- a/MediaBrowser.Model/Session/PlayMethod.cs
+++ b/MediaBrowser.Model/Session/PlayMethod.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Session
{
public enum PlayMethod
diff --git a/MediaBrowser.Model/Session/PlayRequest.cs b/MediaBrowser.Model/Session/PlayRequest.cs
index 075ae7730..bdb2b2439 100644
--- a/MediaBrowser.Model/Session/PlayRequest.cs
+++ b/MediaBrowser.Model/Session/PlayRequest.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using MediaBrowser.Model.Services;
diff --git a/MediaBrowser.Model/Session/PlaybackProgressInfo.cs b/MediaBrowser.Model/Session/PlaybackProgressInfo.cs
index c1d630671..5687ba84b 100644
--- a/MediaBrowser.Model/Session/PlaybackProgressInfo.cs
+++ b/MediaBrowser.Model/Session/PlaybackProgressInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using MediaBrowser.Model.Dto;
diff --git a/MediaBrowser.Model/Session/PlaybackStopInfo.cs b/MediaBrowser.Model/Session/PlaybackStopInfo.cs
index 8a85b1998..f8cfacc20 100644
--- a/MediaBrowser.Model/Session/PlaybackStopInfo.cs
+++ b/MediaBrowser.Model/Session/PlaybackStopInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using MediaBrowser.Model.Dto;
@@ -13,36 +15,43 @@ namespace MediaBrowser.Model.Session
/// </summary>
/// <value>The item.</value>
public BaseItemDto Item { get; set; }
+
/// <summary>
/// Gets or sets the item identifier.
/// </summary>
/// <value>The item identifier.</value>
public Guid ItemId { get; set; }
+
/// <summary>
/// Gets or sets the session id.
/// </summary>
/// <value>The session id.</value>
public string SessionId { get; set; }
+
/// <summary>
/// Gets or sets the media version identifier.
/// </summary>
/// <value>The media version identifier.</value>
public string MediaSourceId { get; set; }
+
/// <summary>
/// Gets or sets the position ticks.
/// </summary>
/// <value>The position ticks.</value>
public long? PositionTicks { get; set; }
+
/// <summary>
/// Gets or sets the live stream identifier.
/// </summary>
/// <value>The live stream identifier.</value>
public string LiveStreamId { get; set; }
+
/// <summary>
/// Gets or sets the play session identifier.
/// </summary>
/// <value>The play session identifier.</value>
public string PlaySessionId { get; set; }
+
/// <summary>
/// Gets or sets a value indicating whether this <see cref="PlaybackStopInfo"/> is failed.
/// </summary>
diff --git a/MediaBrowser.Model/Session/PlayerStateInfo.cs b/MediaBrowser.Model/Session/PlayerStateInfo.cs
index 7e54e16c8..0f9956873 100644
--- a/MediaBrowser.Model/Session/PlayerStateInfo.cs
+++ b/MediaBrowser.Model/Session/PlayerStateInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Session
{
public class PlayerStateInfo
diff --git a/MediaBrowser.Model/Session/PlaystateCommand.cs b/MediaBrowser.Model/Session/PlaystateCommand.cs
index 6eb3e53c2..3aa091f79 100644
--- a/MediaBrowser.Model/Session/PlaystateCommand.cs
+++ b/MediaBrowser.Model/Session/PlaystateCommand.cs
@@ -1,40 +1,49 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Session
{
/// <summary>
- /// Enum PlaystateCommand
+ /// Enum PlaystateCommand.
/// </summary>
public enum PlaystateCommand
{
/// <summary>
- /// The stop
+ /// The stop.
/// </summary>
Stop,
+
/// <summary>
- /// The pause
+ /// The pause.
/// </summary>
Pause,
+
/// <summary>
- /// The unpause
+ /// The unpause.
/// </summary>
Unpause,
+
/// <summary>
- /// The next track
+ /// The next track.
/// </summary>
NextTrack,
+
/// <summary>
- /// The previous track
+ /// The previous track.
/// </summary>
PreviousTrack,
+
/// <summary>
- /// The seek
+ /// The seek.
/// </summary>
Seek,
+
/// <summary>
- /// The rewind
+ /// The rewind.
/// </summary>
Rewind,
+
/// <summary>
- /// The fast forward
+ /// The fast forward.
/// </summary>
FastForward,
PlayPause
diff --git a/MediaBrowser.Model/Session/PlaystateRequest.cs b/MediaBrowser.Model/Session/PlaystateRequest.cs
index 08d3f0072..493a8063a 100644
--- a/MediaBrowser.Model/Session/PlaystateRequest.cs
+++ b/MediaBrowser.Model/Session/PlaystateRequest.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Session
{
public class PlaystateRequest
diff --git a/MediaBrowser.Model/Session/TranscodingInfo.cs b/MediaBrowser.Model/Session/TranscodingInfo.cs
index 5161882fd..8f4e688f0 100644
--- a/MediaBrowser.Model/Session/TranscodingInfo.cs
+++ b/MediaBrowser.Model/Session/TranscodingInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Session
{
public class TranscodingInfo
diff --git a/MediaBrowser.Model/Session/UserDataChangeInfo.cs b/MediaBrowser.Model/Session/UserDataChangeInfo.cs
index ef0e2c89a..0872eb4b1 100644
--- a/MediaBrowser.Model/Session/UserDataChangeInfo.cs
+++ b/MediaBrowser.Model/Session/UserDataChangeInfo.cs
@@ -3,7 +3,7 @@ using MediaBrowser.Model.Dto;
namespace MediaBrowser.Model.Session
{
/// <summary>
- /// Class UserDataChangeInfo
+ /// Class UserDataChangeInfo.
/// </summary>
public class UserDataChangeInfo
{
diff --git a/MediaBrowser.Model/Sync/SyncCategory.cs b/MediaBrowser.Model/Sync/SyncCategory.cs
index 637c5ba74..215ac301e 100644
--- a/MediaBrowser.Model/Sync/SyncCategory.cs
+++ b/MediaBrowser.Model/Sync/SyncCategory.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Sync
{
public enum SyncCategory
diff --git a/MediaBrowser.Model/Sync/SyncJob.cs b/MediaBrowser.Model/Sync/SyncJob.cs
index 7a1f76fe9..30bf27f38 100644
--- a/MediaBrowser.Model/Sync/SyncJob.cs
+++ b/MediaBrowser.Model/Sync/SyncJob.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace MediaBrowser.Model.Sync
@@ -9,91 +11,109 @@ namespace MediaBrowser.Model.Sync
/// </summary>
/// <value>The identifier.</value>
public string Id { get; set; }
+
/// <summary>
/// Gets or sets the device identifier.
/// </summary>
/// <value>The device identifier.</value>
public string TargetId { get; set; }
+
/// <summary>
/// Gets or sets the name of the target.
/// </summary>
/// <value>The name of the target.</value>
public string TargetName { get; set; }
+
/// <summary>
/// Gets or sets the quality.
/// </summary>
/// <value>The quality.</value>
public string Quality { get; set; }
+
/// <summary>
/// Gets or sets the bitrate.
/// </summary>
/// <value>The bitrate.</value>
public int? Bitrate { get; set; }
+
/// <summary>
/// Gets or sets the profile.
/// </summary>
/// <value>The profile.</value>
public string Profile { get; set; }
+
/// <summary>
/// Gets or sets the category.
/// </summary>
/// <value>The category.</value>
public SyncCategory? Category { get; set; }
+
/// <summary>
/// Gets or sets the parent identifier.
/// </summary>
/// <value>The parent identifier.</value>
public string ParentId { get; set; }
+
/// <summary>
/// Gets or sets the current progress.
/// </summary>
/// <value>The current progress.</value>
public double? Progress { get; set; }
+
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
public string Name { get; set; }
+
/// <summary>
/// Gets or sets the status.
/// </summary>
/// <value>The status.</value>
public SyncJobStatus Status { get; set; }
+
/// <summary>
/// Gets or sets the user identifier.
/// </summary>
/// <value>The user identifier.</value>
public string UserId { get; set; }
+
/// <summary>
/// Gets or sets a value indicating whether [unwatched only].
/// </summary>
/// <value><c>true</c> if [unwatched only]; otherwise, <c>false</c>.</value>
public bool UnwatchedOnly { get; set; }
+
/// <summary>
/// Gets or sets a value indicating whether [synchronize new content].
/// </summary>
/// <value><c>true</c> if [synchronize new content]; otherwise, <c>false</c>.</value>
public bool SyncNewContent { get; set; }
+
/// <summary>
/// Gets or sets the item limit.
/// </summary>
/// <value>The item limit.</value>
public int? ItemLimit { get; set; }
+
/// <summary>
/// Gets or sets the requested item ids.
/// </summary>
/// <value>The requested item ids.</value>
public Guid[] RequestedItemIds { get; set; }
+
/// <summary>
/// Gets or sets the date created.
/// </summary>
/// <value>The date created.</value>
public DateTime DateCreated { get; set; }
+
/// <summary>
/// Gets or sets the date last modified.
/// </summary>
/// <value>The date last modified.</value>
public DateTime DateLastModified { get; set; }
+
/// <summary>
/// Gets or sets the item count.
/// </summary>
diff --git a/MediaBrowser.Model/Sync/SyncJobStatus.cs b/MediaBrowser.Model/Sync/SyncJobStatus.cs
index 4ea3d3fa5..226a47d4c 100644
--- a/MediaBrowser.Model/Sync/SyncJobStatus.cs
+++ b/MediaBrowser.Model/Sync/SyncJobStatus.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Sync
{
public enum SyncJobStatus
diff --git a/MediaBrowser.Model/Sync/SyncTarget.cs b/MediaBrowser.Model/Sync/SyncTarget.cs
index a94bf9a25..20a0c8cc7 100644
--- a/MediaBrowser.Model/Sync/SyncTarget.cs
+++ b/MediaBrowser.Model/Sync/SyncTarget.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Sync
{
public class SyncTarget
@@ -7,6 +9,7 @@ namespace MediaBrowser.Model.Sync
/// </summary>
/// <value>The name.</value>
public string Name { get; set; }
+
/// <summary>
/// Gets or sets the identifier.
/// </summary>
diff --git a/MediaBrowser.Model/System/LogFile.cs b/MediaBrowser.Model/System/LogFile.cs
index 913e8e1ea..a2b701664 100644
--- a/MediaBrowser.Model/System/LogFile.cs
+++ b/MediaBrowser.Model/System/LogFile.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace MediaBrowser.Model.System
diff --git a/MediaBrowser.Model/System/OperatingSystemId.cs b/MediaBrowser.Model/System/OperatingSystemId.cs
index e81dd4213..2e417f6b5 100644
--- a/MediaBrowser.Model/System/OperatingSystemId.cs
+++ b/MediaBrowser.Model/System/OperatingSystemId.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.System
{
public enum OperatingSystemId
diff --git a/MediaBrowser.Model/System/PublicSystemInfo.cs b/MediaBrowser.Model/System/PublicSystemInfo.cs
index 23f6d378c..1775470b5 100644
--- a/MediaBrowser.Model/System/PublicSystemInfo.cs
+++ b/MediaBrowser.Model/System/PublicSystemInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.System
{
public class PublicSystemInfo
diff --git a/MediaBrowser.Model/System/SystemInfo.cs b/MediaBrowser.Model/System/SystemInfo.cs
index 7014a5c87..cfa7684c9 100644
--- a/MediaBrowser.Model/System/SystemInfo.cs
+++ b/MediaBrowser.Model/System/SystemInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Runtime.InteropServices;
using MediaBrowser.Model.Updates;
diff --git a/MediaBrowser.Model/Tasks/IConfigurableScheduledTask.cs b/MediaBrowser.Model/Tasks/IConfigurableScheduledTask.cs
index 9c4b75c54..fbfaed22e 100644
--- a/MediaBrowser.Model/Tasks/IConfigurableScheduledTask.cs
+++ b/MediaBrowser.Model/Tasks/IConfigurableScheduledTask.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Tasks
{
public interface IConfigurableScheduledTask
diff --git a/MediaBrowser.Model/Tasks/IScheduledTask.cs b/MediaBrowser.Model/Tasks/IScheduledTask.cs
index a615ebb07..ed160e176 100644
--- a/MediaBrowser.Model/Tasks/IScheduledTask.cs
+++ b/MediaBrowser.Model/Tasks/IScheduledTask.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Threading;
@@ -39,9 +41,9 @@ namespace MediaBrowser.Model.Tasks
Task Execute(CancellationToken cancellationToken, IProgress<double> progress);
/// <summary>
- /// Gets the default triggers.
+ /// Gets the default triggers that define when the task will run.
/// </summary>
- /// <returns>IEnumerable{BaseTaskTrigger}.</returns>
+ /// <returns>The default triggers that define when the task will run.</returns>
IEnumerable<TaskTriggerInfo> GetDefaultTriggers();
}
}
diff --git a/MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs b/MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs
index 61e3a65eb..4dd1bb5d0 100644
--- a/MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs
+++ b/MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs
@@ -4,7 +4,7 @@ using MediaBrowser.Model.Events;
namespace MediaBrowser.Model.Tasks
{
/// <summary>
- /// Interface IScheduledTaskWorker
+ /// Interface IScheduledTaskWorker.
/// </summary>
public interface IScheduledTaskWorker : IDisposable
{
diff --git a/MediaBrowser.Model/Tasks/ITaskManager.cs b/MediaBrowser.Model/Tasks/ITaskManager.cs
index 57b8d1af8..4a7f579ec 100644
--- a/MediaBrowser.Model/Tasks/ITaskManager.cs
+++ b/MediaBrowser.Model/Tasks/ITaskManager.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
diff --git a/MediaBrowser.Model/Tasks/ITaskTrigger.cs b/MediaBrowser.Model/Tasks/ITaskTrigger.cs
index c8433ed21..5c30d6c22 100644
--- a/MediaBrowser.Model/Tasks/ITaskTrigger.cs
+++ b/MediaBrowser.Model/Tasks/ITaskTrigger.cs
@@ -4,12 +4,12 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Model.Tasks
{
/// <summary>
- /// Interface ITaskTrigger
+ /// Interface ITaskTrigger.
/// </summary>
public interface ITaskTrigger
{
/// <summary>
- /// Fires when the trigger condition is satisfied and the task should run
+ /// Fires when the trigger condition is satisfied and the task should run.
/// </summary>
event EventHandler<EventArgs> Triggered;
@@ -19,12 +19,12 @@ namespace MediaBrowser.Model.Tasks
TaskOptions TaskOptions { get; set; }
/// <summary>
- /// Stars waiting for the trigger action
+ /// Stars waiting for the trigger action.
/// </summary>
void Start(TaskResult lastResult, ILogger logger, string taskName, bool isApplicationStartup);
/// <summary>
- /// Stops waiting for the trigger action
+ /// Stops waiting for the trigger action.
/// </summary>
void Stop();
}
diff --git a/MediaBrowser.Model/Tasks/ScheduledTaskHelpers.cs b/MediaBrowser.Model/Tasks/ScheduledTaskHelpers.cs
index e461e4a4b..ca0743cca 100644
--- a/MediaBrowser.Model/Tasks/ScheduledTaskHelpers.cs
+++ b/MediaBrowser.Model/Tasks/ScheduledTaskHelpers.cs
@@ -1,7 +1,7 @@
namespace MediaBrowser.Model.Tasks
{
/// <summary>
- /// Class ScheduledTaskHelpers
+ /// Class ScheduledTaskHelpers.
/// </summary>
public static class ScheduledTaskHelpers
{
diff --git a/MediaBrowser.Model/Tasks/TaskCompletionEventArgs.cs b/MediaBrowser.Model/Tasks/TaskCompletionEventArgs.cs
index 05eaff8da..cc6c2b62b 100644
--- a/MediaBrowser.Model/Tasks/TaskCompletionEventArgs.cs
+++ b/MediaBrowser.Model/Tasks/TaskCompletionEventArgs.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace MediaBrowser.Model.Tasks
diff --git a/MediaBrowser.Model/Tasks/TaskCompletionStatus.cs b/MediaBrowser.Model/Tasks/TaskCompletionStatus.cs
index 4d7ff523d..5c8b5f223 100644
--- a/MediaBrowser.Model/Tasks/TaskCompletionStatus.cs
+++ b/MediaBrowser.Model/Tasks/TaskCompletionStatus.cs
@@ -1,27 +1,27 @@
namespace MediaBrowser.Model.Tasks
{
/// <summary>
- /// Enum TaskCompletionStatus
+ /// Enum TaskCompletionStatus.
/// </summary>
public enum TaskCompletionStatus
{
/// <summary>
- /// The completed
+ /// The completed.
/// </summary>
Completed,
/// <summary>
- /// The failed
+ /// The failed.
/// </summary>
Failed,
/// <summary>
- /// Manually cancelled by the user
+ /// Manually cancelled by the user.
/// </summary>
Cancelled,
/// <summary>
- /// Aborted due to a system failure or shutdown
+ /// Aborted due to a system failure or shutdown.
/// </summary>
Aborted
}
diff --git a/MediaBrowser.Model/Tasks/TaskInfo.cs b/MediaBrowser.Model/Tasks/TaskInfo.cs
index 8d80e68cf..5144c035a 100644
--- a/MediaBrowser.Model/Tasks/TaskInfo.cs
+++ b/MediaBrowser.Model/Tasks/TaskInfo.cs
@@ -1,7 +1,9 @@
+using System;
+
namespace MediaBrowser.Model.Tasks
{
/// <summary>
- /// Class TaskInfo
+ /// Class TaskInfo.
/// </summary>
public class TaskInfo
{
@@ -70,7 +72,7 @@ namespace MediaBrowser.Model.Tasks
/// </summary>
public TaskInfo()
{
- Triggers = new TaskTriggerInfo[] { };
+ Triggers = Array.Empty<TaskTriggerInfo>();
}
}
}
diff --git a/MediaBrowser.Model/Tasks/TaskOptions.cs b/MediaBrowser.Model/Tasks/TaskOptions.cs
index a9f03303a..3a221b878 100644
--- a/MediaBrowser.Model/Tasks/TaskOptions.cs
+++ b/MediaBrowser.Model/Tasks/TaskOptions.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Tasks
{
public class TaskOptions
diff --git a/MediaBrowser.Model/Tasks/TaskResult.cs b/MediaBrowser.Model/Tasks/TaskResult.cs
index eede9069f..c6f92e7ed 100644
--- a/MediaBrowser.Model/Tasks/TaskResult.cs
+++ b/MediaBrowser.Model/Tasks/TaskResult.cs
@@ -3,7 +3,7 @@ using System;
namespace MediaBrowser.Model.Tasks
{
/// <summary>
- /// Class TaskExecutionInfo
+ /// Class TaskExecutionInfo.
/// </summary>
public class TaskResult
{
diff --git a/MediaBrowser.Model/Tasks/TaskState.cs b/MediaBrowser.Model/Tasks/TaskState.cs
index 91bc7f682..619dedb70 100644
--- a/MediaBrowser.Model/Tasks/TaskState.cs
+++ b/MediaBrowser.Model/Tasks/TaskState.cs
@@ -1,20 +1,22 @@
namespace MediaBrowser.Model.Tasks
{
/// <summary>
- /// Enum TaskState
+ /// Enum TaskState.
/// </summary>
public enum TaskState
{
/// <summary>
- /// The idle
+ /// The idle.
/// </summary>
Idle,
+
/// <summary>
- /// The cancelling
+ /// The cancelling.
/// </summary>
Cancelling,
+
/// <summary>
- /// The running
+ /// The running.
/// </summary>
Running
}
diff --git a/MediaBrowser.Model/Tasks/TaskTriggerInfo.cs b/MediaBrowser.Model/Tasks/TaskTriggerInfo.cs
index 714f11872..699e0ea3a 100644
--- a/MediaBrowser.Model/Tasks/TaskTriggerInfo.cs
+++ b/MediaBrowser.Model/Tasks/TaskTriggerInfo.cs
@@ -1,9 +1,11 @@
+#pragma warning disable CS1591
+
using System;
namespace MediaBrowser.Model.Tasks
{
/// <summary>
- /// Class TaskTriggerInfo
+ /// Class TaskTriggerInfo.
/// </summary>
public class TaskTriggerInfo
{
diff --git a/MediaBrowser.Model/Updates/CheckForUpdateResult.cs b/MediaBrowser.Model/Updates/CheckForUpdateResult.cs
index 4c66c6d49..be1b08223 100644
--- a/MediaBrowser.Model/Updates/CheckForUpdateResult.cs
+++ b/MediaBrowser.Model/Updates/CheckForUpdateResult.cs
@@ -1,7 +1,7 @@
namespace MediaBrowser.Model.Updates
{
/// <summary>
- /// Class CheckForUpdateResult
+ /// Class CheckForUpdateResult.
/// </summary>
public class CheckForUpdateResult
{
diff --git a/MediaBrowser.Model/Updates/InstallationInfo.cs b/MediaBrowser.Model/Updates/InstallationInfo.cs
index 7554e9fe2..42c2105f5 100644
--- a/MediaBrowser.Model/Updates/InstallationInfo.cs
+++ b/MediaBrowser.Model/Updates/InstallationInfo.cs
@@ -3,7 +3,7 @@ using System;
namespace MediaBrowser.Model.Updates
{
/// <summary>
- /// Class InstallationInfo
+ /// Class InstallationInfo.
/// </summary>
public class InstallationInfo
{
diff --git a/MediaBrowser.Model/Updates/PackageInfo.cs b/MediaBrowser.Model/Updates/PackageInfo.cs
index 5dd9c6591..abbe91eff 100644
--- a/MediaBrowser.Model/Updates/PackageInfo.cs
+++ b/MediaBrowser.Model/Updates/PackageInfo.cs
@@ -4,7 +4,7 @@ using System.Collections.Generic;
namespace MediaBrowser.Model.Updates
{
/// <summary>
- /// Class PackageInfo
+ /// Class PackageInfo.
/// </summary>
public class PackageInfo
{
@@ -170,7 +170,7 @@ namespace MediaBrowser.Model.Updates
/// </summary>
public PackageInfo()
{
- versions = new PackageVersionInfo[] { };
+ versions = Array.Empty<PackageVersionInfo>();
}
}
}
diff --git a/MediaBrowser.Model/Updates/PackageTargetSystem.cs b/MediaBrowser.Model/Updates/PackageTargetSystem.cs
index a0646f959..11af7f02d 100644
--- a/MediaBrowser.Model/Updates/PackageTargetSystem.cs
+++ b/MediaBrowser.Model/Updates/PackageTargetSystem.cs
@@ -1,20 +1,22 @@
namespace MediaBrowser.Model.Updates
{
/// <summary>
- /// Enum PackageType
+ /// Enum PackageType.
/// </summary>
public enum PackageTargetSystem
{
/// <summary>
- /// Server
+ /// Server.
/// </summary>
Server,
+
/// <summary>
- /// MB Theater
+ /// MB Theater.
/// </summary>
MBTheater,
+
/// <summary>
- /// MB Classic
+ /// MB Classic.
/// </summary>
MBClassic
}
diff --git a/MediaBrowser.Model/Updates/PackageVersionClass.cs b/MediaBrowser.Model/Updates/PackageVersionClass.cs
index 52f08b73b..f813f2c97 100644
--- a/MediaBrowser.Model/Updates/PackageVersionClass.cs
+++ b/MediaBrowser.Model/Updates/PackageVersionClass.cs
@@ -1,20 +1,22 @@
namespace MediaBrowser.Model.Updates
{
/// <summary>
- /// Enum PackageVersionClass
+ /// Enum PackageVersionClass.
/// </summary>
public enum PackageVersionClass
{
/// <summary>
- /// The release
+ /// The release.
/// </summary>
Release = 0,
+
/// <summary>
- /// The beta
+ /// The beta.
/// </summary>
Beta = 1,
+
/// <summary>
- /// The dev
+ /// The dev.
/// </summary>
Dev = 2
}
diff --git a/MediaBrowser.Model/Updates/PackageVersionInfo.cs b/MediaBrowser.Model/Updates/PackageVersionInfo.cs
index c0790317d..3eef965dd 100644
--- a/MediaBrowser.Model/Updates/PackageVersionInfo.cs
+++ b/MediaBrowser.Model/Updates/PackageVersionInfo.cs
@@ -1,10 +1,12 @@
+#pragma warning disable CS1591
+
using System;
using System.Text.Json.Serialization;
namespace MediaBrowser.Model.Updates
{
/// <summary>
- /// Class PackageVersionInfo
+ /// Class PackageVersionInfo.
/// </summary>
public class PackageVersionInfo
{
diff --git a/MediaBrowser.Model/Users/ForgotPasswordAction.cs b/MediaBrowser.Model/Users/ForgotPasswordAction.cs
index 2124126c1..f198476e3 100644
--- a/MediaBrowser.Model/Users/ForgotPasswordAction.cs
+++ b/MediaBrowser.Model/Users/ForgotPasswordAction.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Users
{
public enum ForgotPasswordAction
diff --git a/MediaBrowser.Model/Users/ForgotPasswordResult.cs b/MediaBrowser.Model/Users/ForgotPasswordResult.cs
index 2f9b4cf48..368c642e8 100644
--- a/MediaBrowser.Model/Users/ForgotPasswordResult.cs
+++ b/MediaBrowser.Model/Users/ForgotPasswordResult.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace MediaBrowser.Model.Users
@@ -9,11 +11,13 @@ namespace MediaBrowser.Model.Users
/// </summary>
/// <value>The action.</value>
public ForgotPasswordAction Action { get; set; }
+
/// <summary>
/// Gets or sets the pin file.
/// </summary>
/// <value>The pin file.</value>
public string PinFile { get; set; }
+
/// <summary>
/// Gets or sets the pin expiration date.
/// </summary>
diff --git a/MediaBrowser.Model/Users/PinRedeemResult.cs b/MediaBrowser.Model/Users/PinRedeemResult.cs
index 35663ba57..ab868cad4 100644
--- a/MediaBrowser.Model/Users/PinRedeemResult.cs
+++ b/MediaBrowser.Model/Users/PinRedeemResult.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Users
{
public class PinRedeemResult
@@ -7,6 +9,7 @@ namespace MediaBrowser.Model.Users
/// </summary>
/// <value><c>true</c> if success; otherwise, <c>false</c>.</value>
public bool Success { get; set; }
+
/// <summary>
/// Gets or sets the users reset.
/// </summary>
diff --git a/MediaBrowser.Model/Users/UserAction.cs b/MediaBrowser.Model/Users/UserAction.cs
index 48b5bbef1..f6bb6451b 100644
--- a/MediaBrowser.Model/Users/UserAction.cs
+++ b/MediaBrowser.Model/Users/UserAction.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace MediaBrowser.Model.Users
diff --git a/MediaBrowser.Model/Users/UserActionType.cs b/MediaBrowser.Model/Users/UserActionType.cs
index 5d843a738..dbb1513f2 100644
--- a/MediaBrowser.Model/Users/UserActionType.cs
+++ b/MediaBrowser.Model/Users/UserActionType.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace MediaBrowser.Model.Users
{
public enum UserActionType
diff --git a/MediaBrowser.Model/Users/UserPolicy.cs b/MediaBrowser.Model/Users/UserPolicy.cs
index 9c3e1f980..ae2b3fd4e 100644
--- a/MediaBrowser.Model/Users/UserPolicy.cs
+++ b/MediaBrowser.Model/Users/UserPolicy.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using MediaBrowser.Model.Configuration;
@@ -44,6 +46,7 @@ namespace MediaBrowser.Model.Users
public bool EnableAudioPlaybackTranscoding { get; set; }
public bool EnableVideoPlaybackTranscoding { get; set; }
public bool EnablePlaybackRemuxing { get; set; }
+ public bool ForceRemoteSourceTranscoding { get; set; }
public bool EnableContentDeletion { get; set; }
public string[] EnableContentDeletionFromFolders { get; set; }
@@ -80,7 +83,7 @@ namespace MediaBrowser.Model.Users
public UserPolicy()
{
IsHidden = true;
-
+
EnableContentDeletion = false;
EnableContentDeletionFromFolders = Array.Empty<string>();
@@ -91,7 +94,7 @@ namespace MediaBrowser.Model.Users
EnableAudioPlaybackTranscoding = true;
EnableVideoPlaybackTranscoding = true;
EnablePlaybackRemuxing = true;
-
+ ForceRemoteSourceTranscoding = false;
EnableLiveTvManagement = true;
EnableLiveTvAccess = true;
diff --git a/MediaBrowser.Providers/Books/AudioBookMetadataService.cs b/MediaBrowser.Providers/Books/AudioBookMetadataService.cs
index 309241bfa..8eaeeea08 100644
--- a/MediaBrowser.Providers/Books/AudioBookMetadataService.cs
+++ b/MediaBrowser.Providers/Books/AudioBookMetadataService.cs
@@ -13,7 +13,7 @@ namespace MediaBrowser.Providers.Books
{
public AudioBookMetadataService(
IServerConfigurationManager serverConfigurationManager,
- ILogger logger,
+ ILogger<AudioBookMetadataService> logger,
IProviderManager providerManager,
IFileSystem fileSystem,
ILibraryManager libraryManager)
diff --git a/MediaBrowser.Providers/Books/BookMetadataService.cs b/MediaBrowser.Providers/Books/BookMetadataService.cs
index 9d6a1ef59..340641711 100644
--- a/MediaBrowser.Providers/Books/BookMetadataService.cs
+++ b/MediaBrowser.Providers/Books/BookMetadataService.cs
@@ -13,7 +13,7 @@ namespace MediaBrowser.Providers.Books
{
public BookMetadataService(
IServerConfigurationManager serverConfigurationManager,
- ILogger logger,
+ ILogger<BookMetadataService> logger,
IProviderManager providerManager,
IFileSystem fileSystem,
ILibraryManager libraryManager)
diff --git a/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs b/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs
index 5bf01232c..3c9760ea7 100644
--- a/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs
+++ b/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs
@@ -16,7 +16,7 @@ namespace MediaBrowser.Providers.BoxSets
{
public BoxSetMetadataService(
IServerConfigurationManager serverConfigurationManager,
- ILogger logger,
+ ILogger<BoxSetMetadataService> logger,
IProviderManager providerManager,
IFileSystem fileSystem,
ILibraryManager libraryManager)
diff --git a/MediaBrowser.Providers/Channels/ChannelMetadataService.cs b/MediaBrowser.Providers/Channels/ChannelMetadataService.cs
index da41f208c..9afa82319 100644
--- a/MediaBrowser.Providers/Channels/ChannelMetadataService.cs
+++ b/MediaBrowser.Providers/Channels/ChannelMetadataService.cs
@@ -13,7 +13,7 @@ namespace MediaBrowser.Providers.Channels
{
public ChannelMetadataService(
IServerConfigurationManager serverConfigurationManager,
- ILogger logger,
+ ILogger<ChannelMetadataService> logger,
IProviderManager providerManager,
IFileSystem fileSystem,
ILibraryManager libraryManager)
diff --git a/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs b/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs
index dd1b4709d..921222543 100644
--- a/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs
+++ b/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs
@@ -14,7 +14,7 @@ namespace MediaBrowser.Providers.Folders
{
public CollectionFolderMetadataService(
IServerConfigurationManager serverConfigurationManager,
- ILogger logger,
+ ILogger<CollectionFolderMetadataService> logger,
IProviderManager providerManager,
IFileSystem fileSystem,
ILibraryManager libraryManager)
diff --git a/MediaBrowser.Providers/Folders/FolderMetadataService.cs b/MediaBrowser.Providers/Folders/FolderMetadataService.cs
index 8409e03fd..b6bd2515d 100644
--- a/MediaBrowser.Providers/Folders/FolderMetadataService.cs
+++ b/MediaBrowser.Providers/Folders/FolderMetadataService.cs
@@ -13,7 +13,7 @@ namespace MediaBrowser.Providers.Folders
{
public FolderMetadataService(
IServerConfigurationManager serverConfigurationManager,
- ILogger logger,
+ ILogger<FolderMetadataService> logger,
IProviderManager providerManager,
IFileSystem fileSystem,
ILibraryManager libraryManager)
diff --git a/MediaBrowser.Providers/Folders/UserViewMetadataService.cs b/MediaBrowser.Providers/Folders/UserViewMetadataService.cs
index 2ceb71afc..60ee81114 100644
--- a/MediaBrowser.Providers/Folders/UserViewMetadataService.cs
+++ b/MediaBrowser.Providers/Folders/UserViewMetadataService.cs
@@ -13,7 +13,7 @@ namespace MediaBrowser.Providers.Folders
{
public UserViewMetadataService(
IServerConfigurationManager serverConfigurationManager,
- ILogger logger,
+ ILogger<UserViewMetadataService> logger,
IProviderManager providerManager,
IFileSystem fileSystem,
ILibraryManager libraryManager)
diff --git a/MediaBrowser.Providers/Genres/GenreMetadataService.cs b/MediaBrowser.Providers/Genres/GenreMetadataService.cs
index 932eb368c..f3406c1ab 100644
--- a/MediaBrowser.Providers/Genres/GenreMetadataService.cs
+++ b/MediaBrowser.Providers/Genres/GenreMetadataService.cs
@@ -13,7 +13,7 @@ namespace MediaBrowser.Providers.Genres
{
public GenreMetadataService(
IServerConfigurationManager serverConfigurationManager,
- ILogger logger,
+ ILogger<GenreMetadataService> logger,
IProviderManager providerManager,
IFileSystem fileSystem,
ILibraryManager libraryManager)
diff --git a/MediaBrowser.Providers/LiveTv/ProgramMetadataService.cs b/MediaBrowser.Providers/LiveTv/ProgramMetadataService.cs
index 13dd97215..7dd49c71a 100644
--- a/MediaBrowser.Providers/LiveTv/ProgramMetadataService.cs
+++ b/MediaBrowser.Providers/LiveTv/ProgramMetadataService.cs
@@ -13,7 +13,7 @@ namespace MediaBrowser.Providers.LiveTv
{
public LiveTvMetadataService(
IServerConfigurationManager serverConfigurationManager,
- ILogger logger,
+ ILogger<LiveTvMetadataService> logger,
IProviderManager providerManager,
IFileSystem fileSystem,
ILibraryManager libraryManager)
diff --git a/MediaBrowser.Providers/Manager/ImageSaver.cs b/MediaBrowser.Providers/Manager/ImageSaver.cs
index ab906809f..3ab621ba4 100644
--- a/MediaBrowser.Providers/Manager/ImageSaver.cs
+++ b/MediaBrowser.Providers/Manager/ImageSaver.cs
@@ -244,9 +244,9 @@ namespace MediaBrowser.Providers.Manager
_fileSystem.SetAttributes(path, false, false);
- using (var fs = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, FileOpenOptions.Asynchronous))
+ using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous))
{
- await source.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, cancellationToken).ConfigureAwait(false);
+ await source.CopyToAsync(fs, IODefaults.CopyToBufferSize, cancellationToken).ConfigureAwait(false);
}
if (_config.Configuration.SaveMetadataHidden)
diff --git a/MediaBrowser.Providers/Manager/ItemImageProvider.cs b/MediaBrowser.Providers/Manager/ItemImageProvider.cs
index e9179815e..6ef0e44a2 100644
--- a/MediaBrowser.Providers/Manager/ItemImageProvider.cs
+++ b/MediaBrowser.Providers/Manager/ItemImageProvider.cs
@@ -5,7 +5,6 @@ using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
-using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
@@ -25,14 +24,12 @@ namespace MediaBrowser.Providers.Manager
{
private readonly ILogger _logger;
private readonly IProviderManager _providerManager;
- private readonly IServerConfigurationManager _config;
private readonly IFileSystem _fileSystem;
- public ItemImageProvider(ILogger logger, IProviderManager providerManager, IServerConfigurationManager config, IFileSystem fileSystem)
+ public ItemImageProvider(ILogger logger, IProviderManager providerManager, IFileSystem fileSystem)
{
_logger = logger;
_providerManager = providerManager;
- _config = config;
_fileSystem = fileSystem;
}
@@ -42,7 +39,7 @@ namespace MediaBrowser.Providers.Manager
if (!(item is Photo))
{
- var images = providers.OfType<ILocalImageFileProvider>()
+ var images = providers.OfType<ILocalImageProvider>()
.SelectMany(i => i.GetImages(item, directoryService))
.ToList();
@@ -141,7 +138,7 @@ namespace MediaBrowser.Providers.Manager
{
var mimeType = MimeTypes.GetMimeType(response.Path);
- var stream = _fileSystem.GetFileStream(response.Path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read, true);
+ var stream = new FileStream(response.Path, FileMode.Open, FileAccess.Read, FileShare.Read, IODefaults.FileStreamBufferSize, true);
await _providerManager.SaveImage(item, stream, mimeType, imageType, null, cancellationToken).ConfigureAwait(false);
}
diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs
index c3401f12b..c49aa407a 100644
--- a/MediaBrowser.Providers/Manager/MetadataService.cs
+++ b/MediaBrowser.Providers/Manager/MetadataService.cs
@@ -73,7 +73,7 @@ namespace MediaBrowser.Providers.Manager
}
}
- var itemImageProvider = new ItemImageProvider(Logger, ProviderManager, ServerConfigurationManager, FileSystem);
+ var itemImageProvider = new ItemImageProvider(Logger, ProviderManager, FileSystem);
var localImagesFailed = false;
var allImageProviders = ((ProviderManager)ProviderManager).GetImageProviders(item, refreshOptions).ToList();
@@ -606,7 +606,6 @@ namespace MediaBrowser.Providers.Manager
// Run custom refresh providers if they report a change or any remote providers change
return anyRemoteProvidersChanged || providersWithChanges.Contains(i);
-
}).ToList();
}
}
diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs
index 631d063a5..e7b349f67 100644
--- a/MediaBrowser.Providers/Manager/ProviderManager.cs
+++ b/MediaBrowser.Providers/Manager/ProviderManager.cs
@@ -182,7 +182,7 @@ namespace MediaBrowser.Providers.Manager
throw new ArgumentNullException(nameof(source));
}
- var fileStream = _fileSystem.GetFileStream(source, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, true);
+ var fileStream = new FileStream(source, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, IODefaults.FileStreamBufferSize, true);
return new ImageSaver(ConfigurationManager, _libraryMonitor, _fileSystem, _logger).SaveImage(item, fileStream, mimeType, type, imageIndex, saveLocallyWithMedia, cancellationToken);
}
diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
index 8d373be28..dfe3eb2ef 100644
--- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj
+++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
@@ -11,11 +11,11 @@
</ItemGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.0.1" />
- <PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="3.0.1" />
+ <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.1" />
+ <PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="3.1.1" />
<PackageReference Include="OptimizedPriorityQueue" Version="4.2.0" />
<PackageReference Include="PlaylistsNET" Version="1.0.4" />
- <PackageReference Include="TvDbSharper" Version="2.0.0" />
+ <PackageReference Include="TvDbSharper" Version="3.0.1" />
</ItemGroup>
<PropertyGroup>
@@ -24,4 +24,14 @@
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
+ <ItemGroup>
+ <None Remove="Plugins\AudioDb\Configuration\config.html" />
+ <EmbeddedResource Include="Plugins\AudioDb\Configuration\config.html" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <None Remove="Plugins\MusicBrainz\Configuration\config.html" />
+ <EmbeddedResource Include="Plugins\MusicBrainz\Configuration\config.html" />
+ </ItemGroup>
+
</Project>
diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
index 4e11fcbb2..db6e49634 100644
--- a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
+++ b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
@@ -121,7 +121,24 @@ namespace MediaBrowser.Providers.MediaInfo
}
private SubtitleResolver _subtitleResolver;
- public FFProbeProvider(ILogger logger, IMediaSourceManager mediaSourceManager, IChannelManager channelManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, IBlurayExaminer blurayExaminer, ILocalizationManager localization, IApplicationPaths appPaths, IJsonSerializer json, IEncodingManager encodingManager, IFileSystem fileSystem, IServerConfigurationManager config, ISubtitleManager subtitleManager, IChapterManager chapterManager, ILibraryManager libraryManager)
+
+ 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,
+ IFileSystem fileSystem,
+ IServerConfigurationManager config,
+ ISubtitleManager subtitleManager,
+ IChapterManager chapterManager,
+ ILibraryManager libraryManager)
{
_logger = logger;
_isoManager = isoManager;
diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
index d2abd2a63..2b178d4d4 100644
--- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
+++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
@@ -158,11 +158,13 @@ namespace MediaBrowser.Providers.MediaInfo
MetadataRefreshOptions options)
{
List<MediaStream> mediaStreams;
+ IReadOnlyList<MediaAttachment> mediaAttachments;
List<ChapterInfo> chapters;
if (mediaInfo != null)
{
mediaStreams = mediaInfo.MediaStreams;
+ mediaAttachments = mediaInfo.MediaAttachments;
video.TotalBitrate = mediaInfo.Bitrate;
//video.FormatName = (mediaInfo.Container ?? string.Empty)
@@ -198,6 +200,7 @@ namespace MediaBrowser.Providers.MediaInfo
else
{
mediaStreams = new List<MediaStream>();
+ mediaAttachments = Array.Empty<MediaAttachment>();
chapters = new List<ChapterInfo>();
}
@@ -210,19 +213,20 @@ namespace MediaBrowser.Providers.MediaInfo
FetchEmbeddedInfo(video, mediaInfo, options, libraryOptions);
FetchPeople(video, mediaInfo, options);
video.Timestamp = mediaInfo.Timestamp;
- video.Video3DFormat = video.Video3DFormat ?? mediaInfo.Video3DFormat;
+ video.Video3DFormat ??= mediaInfo.Video3DFormat;
}
var videoStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video);
- video.Height = videoStream == null ? 0 : videoStream.Height ?? 0;
- video.Width = videoStream == null ? 0 : videoStream.Width ?? 0;
+ video.Height = videoStream?.Height ?? 0;
+ video.Width = videoStream?.Width ?? 0;
video.DefaultVideoStreamIndex = videoStream == null ? (int?)null : videoStream.Index;
video.HasSubtitles = mediaStreams.Any(i => i.Type == MediaStreamType.Subtitle);
_itemRepo.SaveMediaStreams(video.Id, mediaStreams, cancellationToken);
+ _itemRepo.SaveMediaAttachments(video.Id, mediaAttachments, cancellationToken);
if (options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh ||
options.MetadataRefreshMode == MetadataRefreshMode.Default)
diff --git a/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs b/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs
index 8195591e1..7ebbb9e23 100644
--- a/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs
+++ b/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs
@@ -5,7 +5,6 @@ using System.Linq;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
diff --git a/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs b/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs
index 74f41f9df..3a936632a 100644
--- a/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs
+++ b/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs
@@ -26,7 +26,13 @@ namespace MediaBrowser.Providers.MediaInfo
private readonly ILogger _logger;
private readonly IJsonSerializer _json;
- public SubtitleScheduledTask(ILibraryManager libraryManager, IJsonSerializer json, IServerConfigurationManager config, ISubtitleManager subtitleManager, ILogger logger, IMediaSourceManager mediaSourceManager)
+ public SubtitleScheduledTask(
+ ILibraryManager libraryManager,
+ IJsonSerializer json,
+ IServerConfigurationManager config,
+ ISubtitleManager subtitleManager,
+ ILogger<SubtitleScheduledTask> logger,
+ IMediaSourceManager mediaSourceManager)
{
_libraryManager = libraryManager;
_config = config;
diff --git a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs
index 95b915b3d..f40570040 100644
--- a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs
+++ b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs
@@ -20,7 +20,7 @@ namespace MediaBrowser.Providers.MediaInfo
private readonly ILogger _logger;
private readonly IFileSystem _fileSystem;
- public VideoImageProvider(IMediaEncoder mediaEncoder, ILogger logger, IFileSystem fileSystem)
+ public VideoImageProvider(IMediaEncoder mediaEncoder, ILogger<VideoImageProvider> logger, IFileSystem fileSystem)
{
_mediaEncoder = mediaEncoder;
_logger = logger;
diff --git a/MediaBrowser.Providers/Movies/MovieMetadataService.cs b/MediaBrowser.Providers/Movies/MovieMetadataService.cs
index c6cc5c7dc..1e2c325d9 100644
--- a/MediaBrowser.Providers/Movies/MovieMetadataService.cs
+++ b/MediaBrowser.Providers/Movies/MovieMetadataService.cs
@@ -13,7 +13,7 @@ namespace MediaBrowser.Providers.Movies
{
public MovieMetadataService(
IServerConfigurationManager serverConfigurationManager,
- ILogger logger,
+ ILogger<MovieMetadataService> logger,
IProviderManager providerManager,
IFileSystem fileSystem,
ILibraryManager libraryManager)
diff --git a/MediaBrowser.Providers/Movies/TrailerMetadataService.cs b/MediaBrowser.Providers/Movies/TrailerMetadataService.cs
index 53b556940..2e6f762b8 100644
--- a/MediaBrowser.Providers/Movies/TrailerMetadataService.cs
+++ b/MediaBrowser.Providers/Movies/TrailerMetadataService.cs
@@ -13,7 +13,7 @@ namespace MediaBrowser.Providers.Movies
{
public TrailerMetadataService(
IServerConfigurationManager serverConfigurationManager,
- ILogger logger,
+ ILogger<TrailerMetadataService> logger,
IProviderManager providerManager,
IFileSystem fileSystem,
ILibraryManager libraryManager)
diff --git a/MediaBrowser.Providers/Music/AlbumMetadataService.cs b/MediaBrowser.Providers/Music/AlbumMetadataService.cs
index 69133c1c1..ed6c01968 100644
--- a/MediaBrowser.Providers/Music/AlbumMetadataService.cs
+++ b/MediaBrowser.Providers/Music/AlbumMetadataService.cs
@@ -17,7 +17,7 @@ namespace MediaBrowser.Providers.Music
{
public AlbumMetadataService(
IServerConfigurationManager serverConfigurationManager,
- ILogger logger,
+ ILogger<AlbumMetadataService> logger,
IProviderManager providerManager,
IFileSystem fileSystem,
ILibraryManager libraryManager)
diff --git a/MediaBrowser.Providers/Music/ArtistMetadataService.cs b/MediaBrowser.Providers/Music/ArtistMetadataService.cs
index 1f099c60f..f90a631c6 100644
--- a/MediaBrowser.Providers/Music/ArtistMetadataService.cs
+++ b/MediaBrowser.Providers/Music/ArtistMetadataService.cs
@@ -15,7 +15,7 @@ namespace MediaBrowser.Providers.Music
{
public ArtistMetadataService(
IServerConfigurationManager serverConfigurationManager,
- ILogger logger,
+ ILogger<ArtistMetadataService> logger,
IProviderManager providerManager,
IFileSystem fileSystem,
ILibraryManager libraryManager)
diff --git a/MediaBrowser.Providers/Music/AudioMetadataService.cs b/MediaBrowser.Providers/Music/AudioMetadataService.cs
index 4d4739cef..e726fa1e2 100644
--- a/MediaBrowser.Providers/Music/AudioMetadataService.cs
+++ b/MediaBrowser.Providers/Music/AudioMetadataService.cs
@@ -13,7 +13,7 @@ namespace MediaBrowser.Providers.Music
{
public AudioMetadataService(
IServerConfigurationManager serverConfigurationManager,
- ILogger logger,
+ ILogger<AudioMetadataService> logger,
IProviderManager providerManager,
IFileSystem fileSystem,
ILibraryManager libraryManager)
diff --git a/MediaBrowser.Providers/Music/MusicExternalIds.cs b/MediaBrowser.Providers/Music/MusicExternalIds.cs
index 585c98af9..628b9a9a1 100644
--- a/MediaBrowser.Providers/Music/MusicExternalIds.cs
+++ b/MediaBrowser.Providers/Music/MusicExternalIds.cs
@@ -1,105 +1,9 @@
using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
namespace MediaBrowser.Providers.Music
{
- public class MusicBrainzReleaseGroupExternalId : IExternalId
- {
- /// <inheritdoc />
- public string Name => "MusicBrainz Release Group";
-
- /// <inheritdoc />
- public string Key => MetadataProviders.MusicBrainzReleaseGroup.ToString();
-
- /// <inheritdoc />
- public string UrlFormatString => "https://musicbrainz.org/release-group/{0}";
-
- /// <inheritdoc />
- public bool Supports(IHasProviderIds item)
- => item is Audio || item is MusicAlbum;
- }
-
- public class MusicBrainzAlbumArtistExternalId : IExternalId
- {
- /// <inheritdoc />
- public string Name => "MusicBrainz Album Artist";
-
- /// <inheritdoc />
- public string Key => MetadataProviders.MusicBrainzAlbumArtist.ToString();
-
- /// <inheritdoc />
- public string UrlFormatString => "https://musicbrainz.org/artist/{0}";
-
- /// <inheritdoc />
- public bool Supports(IHasProviderIds item)
- => item is Audio;
- }
-
- public class MusicBrainzAlbumExternalId : IExternalId
- {
- /// <inheritdoc />
- public string Name => "MusicBrainz Album";
-
- /// <inheritdoc />
- public string Key => MetadataProviders.MusicBrainzAlbum.ToString();
-
- /// <inheritdoc />
- public string UrlFormatString => "https://musicbrainz.org/release/{0}";
-
- /// <inheritdoc />
- public bool Supports(IHasProviderIds item)
- => item is Audio || item is MusicAlbum;
- }
-
- public class MusicBrainzArtistExternalId : IExternalId
- {
- /// <inheritdoc />
- public string Name => "MusicBrainz";
-
- /// <inheritdoc />
- public string Key => MetadataProviders.MusicBrainzArtist.ToString();
-
- /// <inheritdoc />
- public string UrlFormatString => "https://musicbrainz.org/artist/{0}";
-
- /// <inheritdoc />
- public bool Supports(IHasProviderIds item) => item is MusicArtist;
- }
-
- public class MusicBrainzOtherArtistExternalId : IExternalId
- {
- /// <inheritdoc />
- public string Name => "MusicBrainz Artist";
-
- /// <inheritdoc />
-
- public string Key => MetadataProviders.MusicBrainzArtist.ToString();
-
- /// <inheritdoc />
- public string UrlFormatString => "https://musicbrainz.org/artist/{0}";
-
- /// <inheritdoc />
- public bool Supports(IHasProviderIds item)
- => item is Audio || item is MusicAlbum;
- }
-
- public class MusicBrainzTrackId : IExternalId
- {
- /// <inheritdoc />
- public string Name => "MusicBrainz Track";
-
- /// <inheritdoc />
- public string Key => MetadataProviders.MusicBrainzTrack.ToString();
-
- /// <inheritdoc />
- public string UrlFormatString => "https://musicbrainz.org/track/{0}";
-
- /// <inheritdoc />
- public bool Supports(IHasProviderIds item) => item is Audio;
- }
-
public class ImvdbId : IExternalId
{
/// <inheritdoc />
diff --git a/MediaBrowser.Providers/Music/MusicVideoMetadataService.cs b/MediaBrowser.Providers/Music/MusicVideoMetadataService.cs
index bbf0cd8db..d653e1063 100644
--- a/MediaBrowser.Providers/Music/MusicVideoMetadataService.cs
+++ b/MediaBrowser.Providers/Music/MusicVideoMetadataService.cs
@@ -13,7 +13,7 @@ namespace MediaBrowser.Providers.Music
{
public MusicVideoMetadataService(
IServerConfigurationManager serverConfigurationManager,
- ILogger logger,
+ ILogger<MusicVideoMetadataService> logger,
IProviderManager providerManager,
IFileSystem fileSystem,
ILibraryManager libraryManager)
diff --git a/MediaBrowser.Providers/MusicGenres/MusicGenreMetadataService.cs b/MediaBrowser.Providers/MusicGenres/MusicGenreMetadataService.cs
index d74e91ad6..bb47de40b 100644
--- a/MediaBrowser.Providers/MusicGenres/MusicGenreMetadataService.cs
+++ b/MediaBrowser.Providers/MusicGenres/MusicGenreMetadataService.cs
@@ -13,7 +13,7 @@ namespace MediaBrowser.Providers.MusicGenres
{
public MusicGenreMetadataService(
IServerConfigurationManager serverConfigurationManager,
- ILogger logger,
+ ILogger<MusicGenreMetadataService> logger,
IProviderManager providerManager,
IFileSystem fileSystem,
ILibraryManager libraryManager)
diff --git a/MediaBrowser.Providers/People/PersonMetadataService.cs b/MediaBrowser.Providers/People/PersonMetadataService.cs
index cdc3c77b7..804f3f3e3 100644
--- a/MediaBrowser.Providers/People/PersonMetadataService.cs
+++ b/MediaBrowser.Providers/People/PersonMetadataService.cs
@@ -13,7 +13,7 @@ namespace MediaBrowser.Providers.People
{
public PersonMetadataService(
IServerConfigurationManager serverConfigurationManager,
- ILogger logger,
+ ILogger<PersonMetadataService> logger,
IProviderManager providerManager,
IFileSystem fileSystem,
ILibraryManager libraryManager)
diff --git a/MediaBrowser.Providers/Photos/PhotoAlbumMetadataService.cs b/MediaBrowser.Providers/Photos/PhotoAlbumMetadataService.cs
index 845404dfb..af8f7a262 100644
--- a/MediaBrowser.Providers/Photos/PhotoAlbumMetadataService.cs
+++ b/MediaBrowser.Providers/Photos/PhotoAlbumMetadataService.cs
@@ -13,7 +13,7 @@ namespace MediaBrowser.Providers.Photos
{
public PhotoAlbumMetadataService(
IServerConfigurationManager serverConfigurationManager,
- ILogger logger,
+ ILogger<PhotoAlbumMetadataService> logger,
IProviderManager providerManager,
IFileSystem fileSystem,
ILibraryManager libraryManager)
diff --git a/MediaBrowser.Providers/Photos/PhotoMetadataService.cs b/MediaBrowser.Providers/Photos/PhotoMetadataService.cs
index 5d6ff8814..579b5a4d0 100644
--- a/MediaBrowser.Providers/Photos/PhotoMetadataService.cs
+++ b/MediaBrowser.Providers/Photos/PhotoMetadataService.cs
@@ -13,7 +13,7 @@ namespace MediaBrowser.Providers.Photos
{
public PhotoMetadataService(
IServerConfigurationManager serverConfigurationManager,
- ILogger logger,
+ ILogger<PhotoMetadataService> logger,
IProviderManager providerManager,
IFileSystem fileSystem,
ILibraryManager libraryManager)
diff --git a/MediaBrowser.Providers/Playlists/PlaylistItemsProvider.cs b/MediaBrowser.Providers/Playlists/PlaylistItemsProvider.cs
index dacb63f84..ae837c591 100644
--- a/MediaBrowser.Providers/Playlists/PlaylistItemsProvider.cs
+++ b/MediaBrowser.Providers/Playlists/PlaylistItemsProvider.cs
@@ -23,7 +23,7 @@ namespace MediaBrowser.Providers.Playlists
private ILogger _logger;
private IFileSystem _fileSystem;
- public PlaylistItemsProvider(IFileSystem fileSystem, ILogger logger)
+ public PlaylistItemsProvider(IFileSystem fileSystem, ILogger<PlaylistItemsProvider> logger)
{
_fileSystem = fileSystem;
_logger = logger;
diff --git a/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs b/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs
index 32bd6c282..a41362ea3 100644
--- a/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs
+++ b/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs
@@ -15,7 +15,7 @@ namespace MediaBrowser.Providers.Playlists
{
public PlaylistMetadataService(
IServerConfigurationManager serverConfigurationManager,
- ILogger logger,
+ ILogger<PlaylistMetadataService> logger,
IProviderManager providerManager,
IFileSystem fileSystem,
ILibraryManager libraryManager)
diff --git a/MediaBrowser.Providers/Music/AudioDbAlbumImageProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs
index 85a87630d..dee2d59f0 100644
--- a/MediaBrowser.Providers/Music/AudioDbAlbumImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs
@@ -10,7 +10,7 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Serialization;
-namespace MediaBrowser.Providers.Music
+namespace MediaBrowser.Providers.Plugins.AudioDb
{
public class AudioDbAlbumImageProvider : IRemoteImageProvider, IHasOrder
{
@@ -102,6 +102,7 @@ namespace MediaBrowser.Providers.Music
}
/// <inheritdoc />
- public bool Supports(BaseItem item) => item is MusicAlbum;
+ public bool Supports(BaseItem item)
+ => Plugin.Instance.Configuration.Enable && item is MusicAlbum;
}
}
diff --git a/MediaBrowser.Providers/Music/AudioDbAlbumProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs
index e61d8792c..1a0e87871 100644
--- a/MediaBrowser.Providers/Music/AudioDbAlbumProvider.cs
+++ b/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
+using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
@@ -15,8 +16,9 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Serialization;
+using MediaBrowser.Providers.Music;
-namespace MediaBrowser.Providers.Music
+namespace MediaBrowser.Providers.Plugins.AudioDb
{
public class AudioDbAlbumProvider : IRemoteMetadataProvider<MusicAlbum, AlbumInfo>, IHasOrder
{
@@ -53,6 +55,12 @@ namespace MediaBrowser.Providers.Music
{
var result = new MetadataResult<MusicAlbum>();
+ // TODO maybe remove when artist metadata can be disabled
+ if (!Plugin.Instance.Configuration.Enable)
+ {
+ return result;
+ }
+
var id = info.GetReleaseGroupId();
if (!string.IsNullOrWhiteSpace(id))
@@ -76,6 +84,11 @@ namespace MediaBrowser.Providers.Music
private void ProcessResult(MusicAlbum item, Album result, string preferredLanguage)
{
+ if (Plugin.Instance.Configuration.ReplaceAlbumName && !string.IsNullOrWhiteSpace(result.strAlbum))
+ {
+ item.Album = result.strAlbum;
+ }
+
if (!string.IsNullOrWhiteSpace(result.strArtist))
{
item.AlbumArtists = new string[] { result.strArtist };
@@ -164,11 +177,10 @@ namespace MediaBrowser.Providers.Music
{
Url = url,
CancellationToken = cancellationToken
-
},
- "GET").ConfigureAwait(false))
+ HttpMethod.Get).ConfigureAwait(false))
using (var response = httpResponse.Content)
- using (var xmlFileStream = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
+ using (var xmlFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true))
{
await response.CopyToAsync(xmlFileStream).ConfigureAwait(false);
}
diff --git a/MediaBrowser.Providers/Music/AudioDbArtistImageProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs
index b9315744f..18afd5dd5 100644
--- a/MediaBrowser.Providers/Music/AudioDbArtistImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs
@@ -10,7 +10,7 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Serialization;
-namespace MediaBrowser.Providers.Music
+namespace MediaBrowser.Providers.Plugins.AudioDb
{
public class AudioDbArtistImageProvider : IRemoteImageProvider, IHasOrder
{
@@ -143,6 +143,7 @@ namespace MediaBrowser.Providers.Music
}
/// <inheritdoc />
- public bool Supports(BaseItem item) => item is MusicArtist;
+ public bool Supports(BaseItem item)
+ => Plugin.Instance.Configuration.Enable && item is MusicArtist;
}
}
diff --git a/MediaBrowser.Providers/Music/AudioDbArtistProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs
index 7e5893d49..df0f3df8f 100644
--- a/MediaBrowser.Providers/Music/AudioDbArtistProvider.cs
+++ b/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs
@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
@@ -14,8 +15,9 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Serialization;
+using MediaBrowser.Providers.Music;
-namespace MediaBrowser.Providers.Music
+namespace MediaBrowser.Providers.Plugins.AudioDb
{
public class AudioDbArtistProvider : IRemoteMetadataProvider<MusicArtist, ArtistInfo>, IHasOrder
{
@@ -54,6 +56,12 @@ namespace MediaBrowser.Providers.Music
{
var result = new MetadataResult<MusicArtist>();
+ // TODO maybe remove when artist metadata can be disabled
+ if (!Plugin.Instance.Configuration.Enable)
+ {
+ return result;
+ }
+
var id = info.GetMusicBrainzArtistId();
if (!string.IsNullOrWhiteSpace(id))
@@ -152,12 +160,12 @@ namespace MediaBrowser.Providers.Music
CancellationToken = cancellationToken,
BufferContent = true
},
- "GET").ConfigureAwait(false))
+ HttpMethod.Get).ConfigureAwait(false))
using (var response = httpResponse.Content)
{
Directory.CreateDirectory(Path.GetDirectoryName(path));
- using (var xmlFileStream = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
+ using (var xmlFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true))
{
await response.CopyToAsync(xmlFileStream).ConfigureAwait(false);
}
diff --git a/MediaBrowser.Providers/Plugins/AudioDb/Configuration/PluginConfiguration.cs b/MediaBrowser.Providers/Plugins/AudioDb/Configuration/PluginConfiguration.cs
new file mode 100644
index 000000000..ad3c7eb4b
--- /dev/null
+++ b/MediaBrowser.Providers/Plugins/AudioDb/Configuration/PluginConfiguration.cs
@@ -0,0 +1,11 @@
+using MediaBrowser.Model.Plugins;
+
+namespace MediaBrowser.Providers.Plugins.AudioDb
+{
+ public class PluginConfiguration : BasePluginConfiguration
+ {
+ public bool Enable { get; set; }
+
+ public bool ReplaceAlbumName { get; set; }
+ }
+}
diff --git a/MediaBrowser.Providers/Plugins/AudioDb/Configuration/config.html b/MediaBrowser.Providers/Plugins/AudioDb/Configuration/config.html
new file mode 100644
index 000000000..34494644d
--- /dev/null
+++ b/MediaBrowser.Providers/Plugins/AudioDb/Configuration/config.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>AudioDB</title>
+</head>
+<body>
+ <div data-role="page" class="page type-interior pluginConfigurationPage configPage" data-require="emby-input,emby-button,emby-checkbox">
+ <div data-role="content">
+ <div class="content-primary">
+ <form class="configForm">
+ <label class="checkboxContainer">
+ <input is="emby-checkbox" type="checkbox" id="enable" />
+ <span>Enable this provider for metadata searches on artists and albums.</span>
+ </label>
+ <label class="checkboxContainer">
+ <input is="emby-checkbox" type="checkbox" id="replaceAlbumName" />
+ <span>When an album is found during a metadata search, replace the name with the value on the server.</span>
+ </label>
+ <br />
+ <div>
+ <button is="emby-button" type="submit" class="raised button-submit block"><span>Save</span></button>
+ </div>
+ </form>
+ </div>
+ </div>
+ <script type="text/javascript">
+ var PluginConfig = {
+ pluginId: "a629c0da-fac5-4c7e-931a-7174223f14c8"
+ };
+
+ $('.configPage').on('pageshow', function () {
+ Dashboard.showLoadingMsg();
+ ApiClient.getPluginConfiguration(PluginConfig.pluginId).then(function (config) {
+ $('#enable').checked(config.Enable);
+ $('#replaceAlbumName').checked(config.ReplaceAlbumName);
+
+ Dashboard.hideLoadingMsg();
+ });
+ });
+
+ $('.configForm').on('submit', function (e) {
+ Dashboard.showLoadingMsg();
+
+ var form = this;
+ ApiClient.getPluginConfiguration(PluginConfig.pluginId).then(function (config) {
+ config.Enable = $('#enable', form).checked();
+ config.ReplaceAlbumName = $('#replaceAlbumName', form).checked();
+
+ ApiClient.updatePluginConfiguration(PluginConfig.pluginId, config).then(Dashboard.processPluginConfigurationUpdateResult);
+ });
+
+ return false;
+ });
+ </script>
+ </div>
+</body>
+</html>
diff --git a/MediaBrowser.Providers/Music/AudioDbExternalIds.cs b/MediaBrowser.Providers/Plugins/AudioDb/ExternalIds.cs
index c866d12de..2d8cb431c 100644
--- a/MediaBrowser.Providers/Music/AudioDbExternalIds.cs
+++ b/MediaBrowser.Providers/Plugins/AudioDb/ExternalIds.cs
@@ -2,7 +2,7 @@ using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
-namespace MediaBrowser.Providers.Music
+namespace MediaBrowser.Providers.Plugins.AudioDb
{
public class AudioDbAlbumExternalId : IExternalId
{
@@ -16,8 +16,7 @@ namespace MediaBrowser.Providers.Music
public string UrlFormatString => "https://www.theaudiodb.com/album/{0}";
/// <inheritdoc />
- public bool Supports(IHasProviderIds item)
- => item is MusicAlbum;
+ public bool Supports(IHasProviderIds item) => item is MusicAlbum;
}
public class AudioDbOtherAlbumExternalId : IExternalId
@@ -62,7 +61,6 @@ namespace MediaBrowser.Providers.Music
public string UrlFormatString => "https://www.theaudiodb.com/artist/{0}";
/// <inheritdoc />
- public bool Supports(IHasProviderIds item)
- => item is Audio || item is MusicAlbum;
+ public bool Supports(IHasProviderIds item) => item is Audio || item is MusicAlbum;
}
}
diff --git a/MediaBrowser.Providers/Plugins/AudioDb/Plugin.cs b/MediaBrowser.Providers/Plugins/AudioDb/Plugin.cs
new file mode 100644
index 000000000..8532c4df3
--- /dev/null
+++ b/MediaBrowser.Providers/Plugins/AudioDb/Plugin.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Collections.Generic;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Plugins;
+using MediaBrowser.Model.Plugins;
+using MediaBrowser.Model.Serialization;
+
+namespace MediaBrowser.Providers.Plugins.AudioDb
+{
+ public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
+ {
+ public static Plugin Instance { get; private set; }
+
+ public override Guid Id => new Guid("a629c0da-fac5-4c7e-931a-7174223f14c8");
+
+ public override string Name => "AudioDB";
+
+ public override string Description => "Get artist and album metadata or images from AudioDB.";
+
+ public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer)
+ : base(applicationPaths, xmlSerializer)
+ {
+ Instance = this;
+ }
+
+ public IEnumerable<PluginPageInfo> GetPages()
+ {
+ yield return new PluginPageInfo
+ {
+ Name = Name,
+ EmbeddedResourcePath = GetType().Namespace + ".Configuration.config.html"
+ };
+ }
+ }
+}
diff --git a/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/AlbumProvider.cs
index 8e71b625e..bc973dee5 100644
--- a/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs
+++ b/MediaBrowser.Providers/Plugins/MusicBrainz/AlbumProvider.cs
@@ -15,7 +15,7 @@ using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
-using Microsoft.Extensions.Configuration;
+using MediaBrowser.Providers.Plugins.MusicBrainz;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.Providers.Music
@@ -28,7 +28,7 @@ namespace MediaBrowser.Providers.Music
/// Be prudent, use a value slightly above the minimun required.
/// https://musicbrainz.org/doc/XML_Web_Service/Rate_Limiting
/// </summary>
- private const long MusicBrainzQueryIntervalMs = 1050u;
+ private readonly long _musicBrainzQueryIntervalMs;
/// <summary>
/// For each single MB lookup/search, this is the maximum number of
@@ -50,14 +50,14 @@ namespace MediaBrowser.Providers.Music
public MusicBrainzAlbumProvider(
IHttpClient httpClient,
IApplicationHost appHost,
- ILogger logger,
- IConfiguration configuration)
+ ILogger<MusicBrainzAlbumProvider> logger)
{
_httpClient = httpClient;
_appHost = appHost;
_logger = logger;
- _musicBrainzBaseUrl = configuration["MusicBrainz:BaseUrl"];
+ _musicBrainzBaseUrl = Plugin.Instance.Configuration.Server;
+ _musicBrainzQueryIntervalMs = Plugin.Instance.Configuration.RateLimit;
// Use a stopwatch to ensure we don't exceed the MusicBrainz rate limit
_stopWatchMusicBrainz.Start();
@@ -74,6 +74,12 @@ namespace MediaBrowser.Providers.Music
/// <inheritdoc />
public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(AlbumInfo searchInfo, CancellationToken cancellationToken)
{
+ // TODO maybe remove when artist metadata can be disabled
+ if (!Plugin.Instance.Configuration.Enable)
+ {
+ return Enumerable.Empty<RemoteSearchResult>();
+ }
+
var releaseId = searchInfo.GetReleaseId();
var releaseGroupId = searchInfo.GetReleaseGroupId();
@@ -107,8 +113,8 @@ namespace MediaBrowser.Providers.Music
url = string.Format(
CultureInfo.InvariantCulture,
"/ws/2/release/?query=\"{0}\" AND artist:\"{1}\"",
- WebUtility.UrlEncode(queryName),
- WebUtility.UrlEncode(searchInfo.GetAlbumArtist()));
+ WebUtility.UrlEncode(queryName),
+ WebUtility.UrlEncode(searchInfo.GetAlbumArtist()));
}
}
@@ -170,7 +176,6 @@ namespace MediaBrowser.Providers.Music
}
return result;
-
});
}
}
@@ -187,6 +192,12 @@ namespace MediaBrowser.Providers.Music
Item = new MusicAlbum()
};
+ // TODO maybe remove when artist metadata can be disabled
+ if (!Plugin.Instance.Configuration.Enable)
+ {
+ return result;
+ }
+
// If we have a release group Id but not a release Id...
if (string.IsNullOrWhiteSpace(releaseId) && !string.IsNullOrWhiteSpace(releaseGroupId))
{
@@ -456,18 +467,6 @@ namespace MediaBrowser.Providers.Music
}
case "artist-credit":
{
- // TODO
-
- /*
- * <artist-credit>
-<name-credit>
-<artist id="e225cda5-882d-4b80-b8a3-b36d7175b1ea">
-<name>SARCASTIC+ZOOKEEPER</name>
-<sort-name>SARCASTIC+ZOOKEEPER</sort-name>
-</artist>
-</name-credit>
-</artist-credit>
- */
using (var subReader = reader.ReadSubtree())
{
var artist = ParseArtistCredit(subReader);
@@ -764,10 +763,10 @@ namespace MediaBrowser.Providers.Music
{
attempts++;
- if (_stopWatchMusicBrainz.ElapsedMilliseconds < MusicBrainzQueryIntervalMs)
+ if (_stopWatchMusicBrainz.ElapsedMilliseconds < _musicBrainzQueryIntervalMs)
{
// MusicBrainz is extremely adamant about limiting to one request per second
- var delayMs = MusicBrainzQueryIntervalMs - _stopWatchMusicBrainz.ElapsedMilliseconds;
+ var delayMs = _musicBrainzQueryIntervalMs - _stopWatchMusicBrainz.ElapsedMilliseconds;
await Task.Delay((int)delayMs, cancellationToken).ConfigureAwait(false);
}
@@ -778,7 +777,7 @@ namespace MediaBrowser.Providers.Music
response = await _httpClient.SendAsync(options, "GET").ConfigureAwait(false);
- // We retry a finite number of times, and only whilst MB is indcating 503 (throttling)
+ // We retry a finite number of times, and only whilst MB is indicating 503 (throttling)
}
while (attempts < MusicBrainzQueryAttempts && response.StatusCode == HttpStatusCode.ServiceUnavailable);
diff --git a/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs
index 5d675392c..260a3b6e7 100644
--- a/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs
+++ b/MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs
@@ -14,6 +14,7 @@ using MediaBrowser.Controller.Extensions;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
+using MediaBrowser.Providers.Plugins.MusicBrainz;
namespace MediaBrowser.Providers.Music
{
@@ -22,6 +23,12 @@ namespace MediaBrowser.Providers.Music
/// <inheritdoc />
public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(ArtistInfo searchInfo, CancellationToken cancellationToken)
{
+ // TODO maybe remove when artist metadata can be disabled
+ if (!Plugin.Instance.Configuration.Enable)
+ {
+ return Enumerable.Empty<RemoteSearchResult>();
+ }
+
var musicBrainzId = searchInfo.GetMusicBrainzArtistId();
if (!string.IsNullOrWhiteSpace(musicBrainzId))
@@ -226,6 +233,12 @@ namespace MediaBrowser.Providers.Music
Item = new MusicArtist()
};
+ // TODO maybe remove when artist metadata can be disabled
+ if (!Plugin.Instance.Configuration.Enable)
+ {
+ return result;
+ }
+
var musicBrainzId = id.GetMusicBrainzArtistId();
if (string.IsNullOrWhiteSpace(musicBrainzId))
@@ -237,8 +250,12 @@ namespace MediaBrowser.Providers.Music
if (singleResult != null)
{
musicBrainzId = singleResult.GetProviderId(MetadataProviders.MusicBrainzArtist);
- //result.Item.Name = singleResult.Name;
result.Item.Overview = singleResult.Overview;
+
+ if (Plugin.Instance.Configuration.ReplaceArtistName)
+ {
+ result.Item.Name = singleResult.Name;
+ }
}
}
diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/PluginConfiguration.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/PluginConfiguration.cs
new file mode 100644
index 000000000..6910b4bb4
--- /dev/null
+++ b/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/PluginConfiguration.cs
@@ -0,0 +1,44 @@
+using MediaBrowser.Model.Plugins;
+
+namespace MediaBrowser.Providers.Plugins.MusicBrainz
+{
+ public class PluginConfiguration : BasePluginConfiguration
+ {
+ private string _server = Plugin.DefaultServer;
+
+ private long _rateLimit = Plugin.DefaultRateLimit;
+
+ public string Server
+ {
+ get
+ {
+ return _server;
+ }
+
+ set
+ {
+ _server = value.TrimEnd('/');
+ }
+ }
+
+ public long RateLimit
+ {
+ get
+ {
+ return _rateLimit;
+ }
+
+ set
+ {
+ if (value < Plugin.DefaultRateLimit && _server == Plugin.DefaultServer)
+ {
+ RateLimit = Plugin.DefaultRateLimit;
+ }
+ }
+ }
+
+ public bool Enable { get; set; }
+
+ public bool ReplaceArtistName { get; set; }
+ }
+}
diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/config.html b/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/config.html
new file mode 100644
index 000000000..1f02461da
--- /dev/null
+++ b/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/config.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>MusicBrainz</title>
+</head>
+<body>
+ <div data-role="page" class="page type-interior pluginConfigurationPage musicBrainzConfigPage" data-require="emby-input,emby-button,emby-checkbox">
+ <div data-role="content">
+ <div class="content-primary">
+ <form class="musicBrainzConfigForm">
+ <div class="inputContainer">
+ <input is="emby-input" type="text" id="server" required label="Server" />
+ <div class="fieldDescription">This can be a mirror of the official server or even a custom server.</div>
+ </div>
+ <div class="inputContainer">
+ <input is="emby-input" type="number" id="rateLimit" pattern="[0-9]*" required min="0" max="10000" label="Rate Limit" />
+ <div class="fieldDescription">Span of time between requests in milliseconds. The official server is limited to one request every two seconds.</div>
+ </div>
+ <label class="checkboxContainer">
+ <input is="emby-checkbox" type="checkbox" id="enable" />
+ <span>Enable this provider for metadata searches on artists and albums.</span>
+ </label>
+ <label class="checkboxContainer">
+ <input is="emby-checkbox" type="checkbox" id="replaceArtistName" />
+ <span>When an artist is found during a metadata search, replace the artist name with the value on the server.</span>
+ </label>
+ <br />
+ <div>
+ <button is="emby-button" type="submit" class="raised button-submit block"><span>Save</span></button>
+ </div>
+ </form>
+ </div>
+ </div>
+ <script type="text/javascript">
+ var MusicBrainzPluginConfig = {
+ uniquePluginId: "8c95c4d2-e50c-4fb0-a4f3-6c06ff0f9a1a"
+ };
+
+ $('.musicBrainzConfigPage').on('pageshow', function () {
+ Dashboard.showLoadingMsg();
+ ApiClient.getPluginConfiguration(MusicBrainzPluginConfig.uniquePluginId).then(function (config) {
+ $('#server').val(config.Server).change();
+ $('#rateLimit').val(config.RateLimit).change();
+ $('#enable').checked(config.Enable);
+ $('#replaceArtistName').checked(config.ReplaceArtistName);
+
+ Dashboard.hideLoadingMsg();
+ });
+ });
+
+ $('.musicBrainzConfigForm').on('submit', function (e) {
+ Dashboard.showLoadingMsg();
+
+ var form = this;
+ ApiClient.getPluginConfiguration(MusicBrainzPluginConfig.uniquePluginId).then(function (config) {
+ config.Server = $('#server', form).val();
+ config.RateLimit = $('#rateLimit', form).val();
+ config.Enable = $('#enable', form).checked();
+ config.ReplaceArtistName = $('#replaceArtistName', form).checked();
+
+ ApiClient.updatePluginConfiguration(MusicBrainzPluginConfig.uniquePluginId, config).then(Dashboard.processPluginConfigurationUpdateResult);
+ });
+
+ return false;
+ });
+ </script>
+ </div>
+</body>
+</html>
diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/ExternalIds.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/ExternalIds.cs
new file mode 100644
index 000000000..03565a34c
--- /dev/null
+++ b/MediaBrowser.Providers/Plugins/MusicBrainz/ExternalIds.cs
@@ -0,0 +1,98 @@
+using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Providers.Plugins.MusicBrainz;
+
+namespace MediaBrowser.Providers.Music
+{
+ public class MusicBrainzReleaseGroupExternalId : IExternalId
+ {
+ /// <inheritdoc />
+ public string Name => "MusicBrainz Release Group";
+
+ /// <inheritdoc />
+ public string Key => MetadataProviders.MusicBrainzReleaseGroup.ToString();
+
+ /// <inheritdoc />
+ public string UrlFormatString => Plugin.Instance.Configuration.Server + "/release-group/{0}";
+
+ /// <inheritdoc />
+ public bool Supports(IHasProviderIds item) => item is Audio || item is MusicAlbum;
+ }
+
+ public class MusicBrainzAlbumArtistExternalId : IExternalId
+ {
+ /// <inheritdoc />
+ public string Name => "MusicBrainz Album Artist";
+
+ /// <inheritdoc />
+ public string Key => MetadataProviders.MusicBrainzAlbumArtist.ToString();
+
+ /// <inheritdoc />
+ public string UrlFormatString => Plugin.Instance.Configuration.Server + "/artist/{0}";
+
+ /// <inheritdoc />
+ public bool Supports(IHasProviderIds item) => item is Audio;
+ }
+
+ public class MusicBrainzAlbumExternalId : IExternalId
+ {
+ /// <inheritdoc />
+ public string Name => "MusicBrainz Album";
+
+ /// <inheritdoc />
+ public string Key => MetadataProviders.MusicBrainzAlbum.ToString();
+
+ /// <inheritdoc />
+ public string UrlFormatString => Plugin.Instance.Configuration.Server + "/release/{0}";
+
+ /// <inheritdoc />
+ public bool Supports(IHasProviderIds item) => item is Audio || item is MusicAlbum;
+ }
+
+ public class MusicBrainzArtistExternalId : IExternalId
+ {
+ /// <inheritdoc />
+ public string Name => "MusicBrainz";
+
+ /// <inheritdoc />
+ public string Key => MetadataProviders.MusicBrainzArtist.ToString();
+
+ /// <inheritdoc />
+ public string UrlFormatString => Plugin.Instance.Configuration.Server + "/artist/{0}";
+
+ /// <inheritdoc />
+ public bool Supports(IHasProviderIds item) => item is MusicArtist;
+ }
+
+ public class MusicBrainzOtherArtistExternalId : IExternalId
+ {
+ /// <inheritdoc />
+ public string Name => "MusicBrainz Artist";
+
+ /// <inheritdoc />
+
+ public string Key => MetadataProviders.MusicBrainzArtist.ToString();
+
+ /// <inheritdoc />
+ public string UrlFormatString => Plugin.Instance.Configuration.Server + "/artist/{0}";
+
+ /// <inheritdoc />
+ public bool Supports(IHasProviderIds item) => item is Audio || item is MusicAlbum;
+ }
+
+ public class MusicBrainzTrackId : IExternalId
+ {
+ /// <inheritdoc />
+ public string Name => "MusicBrainz Track";
+
+ /// <inheritdoc />
+ public string Key => MetadataProviders.MusicBrainzTrack.ToString();
+
+ /// <inheritdoc />
+ public string UrlFormatString => Plugin.Instance.Configuration.Server + "/track/{0}";
+
+ /// <inheritdoc />
+ public bool Supports(IHasProviderIds item) => item is Audio;
+ }
+}
diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/Plugin.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/Plugin.cs
new file mode 100644
index 000000000..8e1b3ea37
--- /dev/null
+++ b/MediaBrowser.Providers/Plugins/MusicBrainz/Plugin.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Collections.Generic;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Plugins;
+using MediaBrowser.Model.Plugins;
+using MediaBrowser.Model.Serialization;
+
+namespace MediaBrowser.Providers.Plugins.MusicBrainz
+{
+ public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
+ {
+ public static Plugin Instance { get; private set; }
+
+ public override Guid Id => new Guid("8c95c4d2-e50c-4fb0-a4f3-6c06ff0f9a1a");
+
+ public override string Name => "MusicBrainz";
+
+ public override string Description => "Get artist and album metadata from any MusicBrainz server.";
+
+ public const string DefaultServer = "https://musicbrainz.org";
+
+ public const long DefaultRateLimit = 2000u;
+
+ public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer)
+ : base(applicationPaths, xmlSerializer)
+ {
+ Instance = this;
+ }
+
+ public IEnumerable<PluginPageInfo> GetPages()
+ {
+ yield return new PluginPageInfo
+ {
+ Name = Name,
+ EmbeddedResourcePath = GetType().Namespace + ".Configuration.config.html"
+ };
+ }
+ }
+}
diff --git a/MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbEpisodeProvider.cs
index dee3030af..37160dd2c 100644
--- a/MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbEpisodeProvider.cs
@@ -11,10 +11,9 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Serialization;
-using MediaBrowser.Providers.Omdb;
using Microsoft.Extensions.Logging;
-namespace MediaBrowser.Providers.TV.Omdb
+namespace MediaBrowser.Providers.Plugins.Omdb
{
public class OmdbEpisodeProvider :
IRemoteMetadataProvider<Episode, EpisodeInfo>,
diff --git a/MediaBrowser.Providers/Omdb/OmdbImageProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs
index 1e0bcacf2..a450c2a6d 100644
--- a/MediaBrowser.Providers/Omdb/OmdbImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs
@@ -13,7 +13,7 @@ using MediaBrowser.Model.IO;
using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Serialization;
-namespace MediaBrowser.Providers.Omdb
+namespace MediaBrowser.Providers.Plugins.Omdb
{
public class OmdbImageProvider : IRemoteImageProvider, IHasOrder
{
diff --git a/MediaBrowser.Providers/Omdb/OmdbItemProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs
index 44b9dcca1..3aadda5d0 100644
--- a/MediaBrowser.Providers/Omdb/OmdbItemProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs
@@ -19,7 +19,7 @@ using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Serialization;
using Microsoft.Extensions.Logging;
-namespace MediaBrowser.Providers.Omdb
+namespace MediaBrowser.Providers.Plugins.Omdb
{
public class OmdbItemProvider : IRemoteMetadataProvider<Series, SeriesInfo>,
IRemoteMetadataProvider<Movie, MovieInfo>, IRemoteMetadataProvider<Trailer, TrailerInfo>, IHasOrder
diff --git a/MediaBrowser.Providers/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs
index f8b876580..fbdd293ed 100644
--- a/MediaBrowser.Providers/Omdb/OmdbProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs
@@ -16,7 +16,7 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
-namespace MediaBrowser.Providers.Omdb
+namespace MediaBrowser.Providers.Plugins.Omdb
{
public class OmdbProvider
{
@@ -209,7 +209,7 @@ namespace MediaBrowser.Providers.Omdb
string resultString;
- using (var stream = _fileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read))
+ using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
{
using (var reader = new StreamReader(stream, new UTF8Encoding(false)))
{
@@ -228,7 +228,7 @@ namespace MediaBrowser.Providers.Omdb
string resultString;
- using (var stream = _fileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read))
+ using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
{
using (var reader = new StreamReader(stream, new UTF8Encoding(false)))
{
diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbClientManager.cs
index 4abe6a943..a12b4d3ad 100644
--- a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs
+++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbClientManager.cs
@@ -10,9 +10,9 @@ using Microsoft.Extensions.Caching.Memory;
using TvDbSharper;
using TvDbSharper.Dto;
-namespace MediaBrowser.Providers.TV.TheTVDB
+namespace MediaBrowser.Providers.Plugins.TheTvdb
{
- public class TvDbClientManager
+ public class TvdbClientManager
{
private const string DefaultLanguage = "en";
@@ -21,7 +21,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB
private readonly TvDbClient _tvDbClient;
private DateTime _tokenCreatedAt;
- public TvDbClientManager(IMemoryCache memoryCache)
+ public TvdbClientManager(IMemoryCache memoryCache)
{
_cache = memoryCache;
_tvDbClient = new TvDbClient();
diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeImageProvider.cs
index fc7f12b1a..6118a9c53 100644
--- a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeImageProvider.cs
@@ -12,19 +12,19 @@ using Microsoft.Extensions.Logging;
using TvDbSharper;
using TvDbSharper.Dto;
-namespace MediaBrowser.Providers.TV.TheTVDB
+namespace MediaBrowser.Providers.Plugins.TheTvdb
{
public class TvdbEpisodeImageProvider : IRemoteImageProvider
{
private readonly IHttpClient _httpClient;
private readonly ILogger _logger;
- private readonly TvDbClientManager _tvDbClientManager;
+ private readonly TvdbClientManager _tvdbClientManager;
- public TvdbEpisodeImageProvider(IHttpClient httpClient, ILogger<TvdbEpisodeImageProvider> logger, TvDbClientManager tvDbClientManager)
+ public TvdbEpisodeImageProvider(IHttpClient httpClient, ILogger<TvdbEpisodeImageProvider> logger, TvdbClientManager tvdbClientManager)
{
_httpClient = httpClient;
_logger = logger;
- _tvDbClientManager = tvDbClientManager;
+ _tvdbClientManager = tvdbClientManager;
}
public string Name => "TheTVDB";
@@ -60,7 +60,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB
SeriesProviderIds = series.ProviderIds,
SeriesDisplayOrder = series.DisplayOrder
};
- string episodeTvdbId = await _tvDbClientManager
+ string episodeTvdbId = await _tvdbClientManager
.GetEpisodeTvdbId(episodeInfo, language, cancellationToken).ConfigureAwait(false);
if (string.IsNullOrEmpty(episodeTvdbId))
{
@@ -73,7 +73,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB
}
var episodeResult =
- await _tvDbClientManager
+ await _tvdbClientManager
.GetEpisodesAsync(Convert.ToInt32(episodeTvdbId), language, cancellationToken)
.ConfigureAwait(false);
diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeProvider.cs
index 4269d3420..f58c58a2e 100644
--- a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs
+++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeProvider.cs
@@ -12,7 +12,7 @@ using Microsoft.Extensions.Logging;
using TvDbSharper;
using TvDbSharper.Dto;
-namespace MediaBrowser.Providers.TV.TheTVDB
+namespace MediaBrowser.Providers.Plugins.TheTvdb
{
/// <summary>
@@ -22,13 +22,13 @@ namespace MediaBrowser.Providers.TV.TheTVDB
{
private readonly IHttpClient _httpClient;
private readonly ILogger _logger;
- private readonly TvDbClientManager _tvDbClientManager;
+ private readonly TvdbClientManager _tvdbClientManager;
- public TvdbEpisodeProvider(IHttpClient httpClient, ILogger<TvdbEpisodeProvider> logger, TvDbClientManager tvDbClientManager)
+ public TvdbEpisodeProvider(IHttpClient httpClient, ILogger<TvdbEpisodeProvider> logger, TvdbClientManager tvdbClientManager)
{
_httpClient = httpClient;
_logger = logger;
- _tvDbClientManager = tvDbClientManager;
+ _tvdbClientManager = tvdbClientManager;
}
public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(EpisodeInfo searchInfo, CancellationToken cancellationToken)
@@ -99,7 +99,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB
string episodeTvdbId = null;
try
{
- episodeTvdbId = await _tvDbClientManager
+ episodeTvdbId = await _tvdbClientManager
.GetEpisodeTvdbId(searchInfo, searchInfo.MetadataLanguage, cancellationToken)
.ConfigureAwait(false);
if (string.IsNullOrEmpty(episodeTvdbId))
@@ -109,7 +109,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB
return result;
}
- var episodeResult = await _tvDbClientManager.GetEpisodesAsync(
+ var episodeResult = await _tvdbClientManager.GetEpisodesAsync(
Convert.ToInt32(episodeTvdbId), searchInfo.MetadataLanguage,
cancellationToken).ConfigureAwait(false);
diff --git a/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbPersonImageProvider.cs
index 50476044b..c1cdc90e9 100644
--- a/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbPersonImageProvider.cs
@@ -11,25 +11,24 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
-using MediaBrowser.Providers.TV.TheTVDB;
using Microsoft.Extensions.Logging;
using TvDbSharper;
-namespace MediaBrowser.Providers.People
+namespace MediaBrowser.Providers.Plugins.TheTvdb
{
public class TvdbPersonImageProvider : IRemoteImageProvider, IHasOrder
{
private readonly IHttpClient _httpClient;
private readonly ILogger _logger;
private readonly ILibraryManager _libraryManager;
- private readonly TvDbClientManager _tvDbClientManager;
+ private readonly TvdbClientManager _tvdbClientManager;
- public TvdbPersonImageProvider(ILibraryManager libraryManager, IHttpClient httpClient, ILogger<TvdbPersonImageProvider> logger, TvDbClientManager tvDbClientManager)
+ public TvdbPersonImageProvider(ILibraryManager libraryManager, IHttpClient httpClient, ILogger<TvdbPersonImageProvider> logger, TvdbClientManager tvdbClientManager)
{
_libraryManager = libraryManager;
_httpClient = httpClient;
_logger = logger;
- _tvDbClientManager = tvDbClientManager;
+ _tvdbClientManager = tvdbClientManager;
}
/// <inheritdoc />
@@ -78,7 +77,7 @@ namespace MediaBrowser.Providers.People
try
{
- var actorsResult = await _tvDbClientManager
+ var actorsResult = await _tvdbClientManager
.GetActorsAsync(tvdbId, series.GetPreferredMetadataLanguage(), cancellationToken)
.ConfigureAwait(false);
var actor = actorsResult.Data.FirstOrDefault(a =>
diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeasonImageProvider.cs
index 94ca603f2..a5d183df7 100644
--- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeasonImageProvider.cs
@@ -14,19 +14,19 @@ using TvDbSharper;
using TvDbSharper.Dto;
using RatingType = MediaBrowser.Model.Dto.RatingType;
-namespace MediaBrowser.Providers.TV.TheTVDB
+namespace MediaBrowser.Providers.Plugins.TheTvdb
{
public class TvdbSeasonImageProvider : IRemoteImageProvider, IHasOrder
{
private readonly IHttpClient _httpClient;
private readonly ILogger _logger;
- private readonly TvDbClientManager _tvDbClientManager;
+ private readonly TvdbClientManager _tvdbClientManager;
- public TvdbSeasonImageProvider(IHttpClient httpClient, ILogger<TvdbSeasonImageProvider> logger, TvDbClientManager tvDbClientManager)
+ public TvdbSeasonImageProvider(IHttpClient httpClient, ILogger<TvdbSeasonImageProvider> logger, TvdbClientManager tvdbClientManager)
{
_httpClient = httpClient;
_logger = logger;
- _tvDbClientManager = tvDbClientManager;
+ _tvdbClientManager = tvdbClientManager;
}
public string Name => ProviderName;
@@ -73,7 +73,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB
};
try
{
- var imageResults = await _tvDbClientManager
+ var imageResults = await _tvdbClientManager
.GetImagesAsync(tvdbId, imageQuery, language, cancellationToken).ConfigureAwait(false);
remoteImages.AddRange(GetImages(imageResults.Data, language));
}
@@ -89,7 +89,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB
private IEnumerable<RemoteImageInfo> GetImages(Image[] images, string preferredLanguage)
{
var list = new List<RemoteImageInfo>();
- var languages = _tvDbClientManager.GetLanguagesAsync(CancellationToken.None).Result.Data;
+ var languages = _tvdbClientManager.GetLanguagesAsync(CancellationToken.None).Result.Data;
foreach (Image image in images)
{
var imageInfo = new RemoteImageInfo
diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesImageProvider.cs
index 365f49fb7..1bad60756 100644
--- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesImageProvider.cs
@@ -14,19 +14,19 @@ using TvDbSharper.Dto;
using RatingType = MediaBrowser.Model.Dto.RatingType;
using Series = MediaBrowser.Controller.Entities.TV.Series;
-namespace MediaBrowser.Providers.TV.TheTVDB
+namespace MediaBrowser.Providers.Plugins.TheTvdb
{
public class TvdbSeriesImageProvider : IRemoteImageProvider, IHasOrder
{
private readonly IHttpClient _httpClient;
private readonly ILogger _logger;
- private readonly TvDbClientManager _tvDbClientManager;
+ private readonly TvdbClientManager _tvdbClientManager;
- public TvdbSeriesImageProvider(IHttpClient httpClient, ILogger<TvdbSeriesImageProvider> logger, TvDbClientManager tvDbClientManager)
+ public TvdbSeriesImageProvider(IHttpClient httpClient, ILogger<TvdbSeriesImageProvider> logger, TvdbClientManager tvdbClientManager)
{
_httpClient = httpClient;
_logger = logger;
- _tvDbClientManager = tvDbClientManager;
+ _tvdbClientManager = tvdbClientManager;
}
public string Name => ProviderName;
@@ -68,7 +68,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB
try
{
var imageResults =
- await _tvDbClientManager.GetImagesAsync(tvdbId, imageQuery, language, cancellationToken)
+ await _tvdbClientManager.GetImagesAsync(tvdbId, imageQuery, language, cancellationToken)
.ConfigureAwait(false);
remoteImages.AddRange(GetImages(imageResults.Data, language));
@@ -85,7 +85,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB
private IEnumerable<RemoteImageInfo> GetImages(Image[] images, string preferredLanguage)
{
var list = new List<RemoteImageInfo>();
- var languages = _tvDbClientManager.GetLanguagesAsync(CancellationToken.None).Result.Data;
+ var languages = _tvdbClientManager.GetLanguagesAsync(CancellationToken.None).Result.Data;
foreach (Image image in images)
{
diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesProvider.cs
index 72ceadaf1..97a5b3478 100644
--- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs
+++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesProvider.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
-using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
@@ -18,7 +17,7 @@ using TvDbSharper;
using TvDbSharper.Dto;
using Series = MediaBrowser.Controller.Entities.TV.Series;
-namespace MediaBrowser.Providers.TV.TheTVDB
+namespace MediaBrowser.Providers.Plugins.TheTvdb
{
public class TvdbSeriesProvider : IRemoteMetadataProvider<Series, SeriesInfo>, IHasOrder
{
@@ -27,16 +26,16 @@ namespace MediaBrowser.Providers.TV.TheTVDB
private readonly ILogger _logger;
private readonly ILibraryManager _libraryManager;
private readonly ILocalizationManager _localizationManager;
- private readonly TvDbClientManager _tvDbClientManager;
+ private readonly TvdbClientManager _tvdbClientManager;
- public TvdbSeriesProvider(IHttpClient httpClient, ILogger<TvdbSeriesProvider> logger, ILibraryManager libraryManager, ILocalizationManager localizationManager, TvDbClientManager tvDbClientManager)
+ public TvdbSeriesProvider(IHttpClient httpClient, ILogger<TvdbSeriesProvider> logger, ILibraryManager libraryManager, ILocalizationManager localizationManager, TvdbClientManager tvdbClientManager)
{
_httpClient = httpClient;
_logger = logger;
_libraryManager = libraryManager;
_localizationManager = localizationManager;
Current = this;
- _tvDbClientManager = tvDbClientManager;
+ _tvdbClientManager = tvdbClientManager;
}
public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(SeriesInfo searchInfo, CancellationToken cancellationToken)
@@ -117,7 +116,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB
try
{
var seriesResult =
- await _tvDbClientManager
+ await _tvdbClientManager
.GetSeriesByIdAsync(Convert.ToInt32(tvdbId), metadataLanguage, cancellationToken)
.ConfigureAwait(false);
MapSeriesToResult(result, seriesResult.Data, metadataLanguage);
@@ -134,7 +133,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB
try
{
- var actorsResult = await _tvDbClientManager
+ var actorsResult = await _tvdbClientManager
.GetActorsAsync(Convert.ToInt32(tvdbId), metadataLanguage, cancellationToken).ConfigureAwait(false);
MapActorsToResult(result, actorsResult.Data);
}
@@ -153,12 +152,12 @@ namespace MediaBrowser.Providers.TV.TheTVDB
{
if (string.Equals(idType, MetadataProviders.Zap2It.ToString(), StringComparison.OrdinalIgnoreCase))
{
- result = await _tvDbClientManager.GetSeriesByZap2ItIdAsync(id, language, cancellationToken)
+ result = await _tvdbClientManager.GetSeriesByZap2ItIdAsync(id, language, cancellationToken)
.ConfigureAwait(false);
}
else
{
- result = await _tvDbClientManager.GetSeriesByImdbIdAsync(id, language, cancellationToken)
+ result = await _tvdbClientManager.GetSeriesByImdbIdAsync(id, language, cancellationToken)
.ConfigureAwait(false);
}
}
@@ -224,7 +223,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB
TvDbResponse<SeriesSearchResult[]> result;
try
{
- result = await _tvDbClientManager.GetSeriesByNameAsync(comparableName, language, cancellationToken)
+ result = await _tvdbClientManager.GetSeriesByNameAsync(comparableName, language, cancellationToken)
.ConfigureAwait(false);
}
catch (TvDbServerException e)
@@ -253,7 +252,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB
try
{
var seriesSesult =
- await _tvDbClientManager.GetSeriesByIdAsync(seriesSearchResult.Id, language, cancellationToken)
+ await _tvdbClientManager.GetSeriesByIdAsync(seriesSearchResult.Id, language, cancellationToken)
.ConfigureAwait(false);
remoteSearchResult.SetProviderId(MetadataProviders.Imdb, seriesSesult.Data.ImdbId);
remoteSearchResult.SetProviderId(MetadataProviders.Zap2It, seriesSesult.Data.Zap2itId);
@@ -360,7 +359,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB
{
try
{
- var episodeSummary = _tvDbClientManager
+ var episodeSummary = _tvdbClientManager
.GetSeriesEpisodeSummaryAsync(tvdbSeries.Id, metadataLanguage, CancellationToken.None).Result.Data;
var maxSeasonNumber = episodeSummary.AiredSeasons.Select(s => Convert.ToInt32(s)).Max();
var episodeQuery = new EpisodeQuery
@@ -368,7 +367,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB
AiredSeason = maxSeasonNumber
};
var episodesPage =
- _tvDbClientManager.GetEpisodesPageAsync(tvdbSeries.Id, episodeQuery, metadataLanguage, CancellationToken.None).Result.Data;
+ _tvdbClientManager.GetEpisodesPageAsync(tvdbSeries.Id, episodeQuery, metadataLanguage, CancellationToken.None).Result.Data;
result.Item.EndDate = episodesPage.Select(e =>
{
DateTime.TryParse(e.FirstAired, out var firstAired);
diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbUtils.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbUtils.cs
index dd5ebf270..79d879aa1 100644
--- a/MediaBrowser.Providers/TV/TheTVDB/TvdbUtils.cs
+++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbUtils.cs
@@ -1,6 +1,7 @@
using System;
using MediaBrowser.Model.Entities;
-namespace MediaBrowser.Providers.TV.TheTVDB
+
+namespace MediaBrowser.Providers.Plugins.TheTvdb
{
public static class TvdbUtils
{
diff --git a/MediaBrowser.Providers/Studios/StudioMetadataService.cs b/MediaBrowser.Providers/Studios/StudioMetadataService.cs
index 6fa30c753..847e47f1b 100644
--- a/MediaBrowser.Providers/Studios/StudioMetadataService.cs
+++ b/MediaBrowser.Providers/Studios/StudioMetadataService.cs
@@ -13,7 +13,7 @@ namespace MediaBrowser.Providers.Studios
{
public StudioMetadataService(
IServerConfigurationManager serverConfigurationManager,
- ILogger logger,
+ ILogger<StudioMetadataService> logger,
IProviderManager providerManager,
IFileSystem fileSystem, ILibraryManager libraryManager)
: base(serverConfigurationManager, logger, providerManager, fileSystem, libraryManager)
diff --git a/MediaBrowser.Providers/Studios/StudiosImageProvider.cs b/MediaBrowser.Providers/Studios/StudiosImageProvider.cs
index ef412db5a..cbef27a09 100644
--- a/MediaBrowser.Providers/Studios/StudiosImageProvider.cs
+++ b/MediaBrowser.Providers/Studios/StudiosImageProvider.cs
@@ -78,7 +78,7 @@ namespace MediaBrowser.Providers.Studios
private RemoteImageInfo GetImage(BaseItem item, string filename, ImageType type, string remoteFilename)
{
- var list = GetAvailableImages(filename, _fileSystem);
+ var list = GetAvailableImages(filename);
var match = FindMatch(item, list);
@@ -179,9 +179,9 @@ namespace MediaBrowser.Providers.Studios
.Replace("/", string.Empty);
}
- public IEnumerable<string> GetAvailableImages(string file, IFileSystem fileSystem)
+ public IEnumerable<string> GetAvailableImages(string file)
{
- using (var fileStream = fileSystem.GetFileStream(file, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read))
+ using (var fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read))
{
using (var reader = new StreamReader(fileStream))
{
diff --git a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs
index 37d1230e2..583c7e8ea 100644
--- a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs
+++ b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs
@@ -19,7 +19,7 @@ using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Providers;
using Microsoft.Extensions.Logging;
-using static MediaBrowser.Model.IO.StreamDefaults;
+using static MediaBrowser.Model.IO.IODefaults;
namespace MediaBrowser.Providers.Subtitles
{
@@ -210,7 +210,7 @@ namespace MediaBrowser.Providers.Subtitles
{
Directory.CreateDirectory(Path.GetDirectoryName(savePath));
- using (var fs = new FileStream(savePath, FileMode.Create, FileAccess.Write, FileShare.Read, DefaultFileStreamBufferSize, true))
+ using (var fs = new FileStream(savePath, FileMode.Create, FileAccess.Write, FileShare.Read, FileStreamBufferSize, true))
{
await stream.CopyToAsync(fs).ConfigureAwait(false);
}
diff --git a/MediaBrowser.Providers/TV/DummySeasonProvider.cs b/MediaBrowser.Providers/TV/DummySeasonProvider.cs
index 4a6676cb9..6a1e6df8f 100644
--- a/MediaBrowser.Providers/TV/DummySeasonProvider.cs
+++ b/MediaBrowser.Providers/TV/DummySeasonProvider.cs
@@ -3,7 +3,6 @@ using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
diff --git a/MediaBrowser.Providers/TV/EpisodeMetadataService.cs b/MediaBrowser.Providers/TV/EpisodeMetadataService.cs
index 89615f406..758c47ba0 100644
--- a/MediaBrowser.Providers/TV/EpisodeMetadataService.cs
+++ b/MediaBrowser.Providers/TV/EpisodeMetadataService.cs
@@ -14,7 +14,7 @@ namespace MediaBrowser.Providers.TV
{
public EpisodeMetadataService(
IServerConfigurationManager serverConfigurationManager,
- ILogger logger,
+ ILogger<EpisodeMetadataService> logger,
IProviderManager providerManager,
IFileSystem fileSystem,
ILibraryManager libraryManager)
diff --git a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs
index e72df50de..0721c4bb4 100644
--- a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs
+++ b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs
@@ -12,7 +12,7 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
-using MediaBrowser.Providers.TV.TheTVDB;
+using MediaBrowser.Providers.Plugins.TheTvdb;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.Providers.TV
@@ -26,7 +26,7 @@ namespace MediaBrowser.Providers.TV
private readonly ILibraryManager _libraryManager;
private readonly ILocalizationManager _localization;
private readonly IFileSystem _fileSystem;
- private readonly TvDbClientManager _tvDbClientManager;
+ private readonly TvdbClientManager _tvdbClientManager;
public MissingEpisodeProvider(
ILogger logger,
@@ -34,14 +34,14 @@ namespace MediaBrowser.Providers.TV
ILibraryManager libraryManager,
ILocalizationManager localization,
IFileSystem fileSystem,
- TvDbClientManager tvDbClientManager)
+ TvdbClientManager tvdbClientManager)
{
_logger = logger;
_config = config;
_libraryManager = libraryManager;
_localization = localization;
_fileSystem = fileSystem;
- _tvDbClientManager = tvDbClientManager;
+ _tvdbClientManager = tvdbClientManager;
}
public async Task<bool> Run(Series series, bool addNewItems, CancellationToken cancellationToken)
@@ -52,7 +52,7 @@ namespace MediaBrowser.Providers.TV
return false;
}
- var episodes = await _tvDbClientManager.GetAllEpisodesAsync(Convert.ToInt32(tvdbId), series.GetPreferredMetadataLanguage(), cancellationToken);
+ var episodes = await _tvdbClientManager.GetAllEpisodesAsync(Convert.ToInt32(tvdbId), series.GetPreferredMetadataLanguage(), cancellationToken);
var episodeLookup = episodes
.Select(i =>
diff --git a/MediaBrowser.Providers/TV/SeasonMetadataService.cs b/MediaBrowser.Providers/TV/SeasonMetadataService.cs
index 0672f886a..eb8032e0e 100644
--- a/MediaBrowser.Providers/TV/SeasonMetadataService.cs
+++ b/MediaBrowser.Providers/TV/SeasonMetadataService.cs
@@ -17,7 +17,7 @@ namespace MediaBrowser.Providers.TV
{
public SeasonMetadataService(
IServerConfigurationManager serverConfigurationManager,
- ILogger logger,
+ ILogger<SeasonMetadataService> logger,
IProviderManager providerManager,
IFileSystem fileSystem,
ILibraryManager libraryManager)
diff --git a/MediaBrowser.Providers/TV/SeriesMetadataService.cs b/MediaBrowser.Providers/TV/SeriesMetadataService.cs
index e9e633ce7..5e75a8125 100644
--- a/MediaBrowser.Providers/TV/SeriesMetadataService.cs
+++ b/MediaBrowser.Providers/TV/SeriesMetadataService.cs
@@ -9,7 +9,7 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
using MediaBrowser.Providers.Manager;
-using MediaBrowser.Providers.TV.TheTVDB;
+using MediaBrowser.Providers.Plugins.TheTvdb;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.Providers.TV
@@ -17,20 +17,20 @@ namespace MediaBrowser.Providers.TV
public class SeriesMetadataService : MetadataService<Series, SeriesInfo>
{
private readonly ILocalizationManager _localization;
- private readonly TvDbClientManager _tvDbClientManager;
+ private readonly TvdbClientManager _tvdbClientManager;
public SeriesMetadataService(
IServerConfigurationManager serverConfigurationManager,
- ILogger logger,
+ ILogger<SeriesMetadataService> logger,
IProviderManager providerManager,
IFileSystem fileSystem,
ILibraryManager libraryManager,
ILocalizationManager localization,
- TvDbClientManager tvDbClientManager)
+ TvdbClientManager tvdbClientManager)
: base(serverConfigurationManager, logger, providerManager, fileSystem, libraryManager)
{
_localization = localization;
- _tvDbClientManager = tvDbClientManager;
+ _tvdbClientManager = tvdbClientManager;
}
/// <inheritdoc />
@@ -47,7 +47,7 @@ namespace MediaBrowser.Providers.TV
LibraryManager,
_localization,
FileSystem,
- _tvDbClientManager);
+ _tvdbClientManager);
try
{
diff --git a/MediaBrowser.Providers/TV/TvExternalIds.cs b/MediaBrowser.Providers/TV/TvExternalIds.cs
index 646dae3e0..baf854285 100644
--- a/MediaBrowser.Providers/TV/TvExternalIds.cs
+++ b/MediaBrowser.Providers/TV/TvExternalIds.cs
@@ -1,7 +1,7 @@
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Providers.TV.TheTVDB;
+using MediaBrowser.Providers.Plugins.TheTvdb;
namespace MediaBrowser.Providers.TV
{
diff --git a/MediaBrowser.Providers/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs b/MediaBrowser.Providers/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs
index 5db0edac2..0bdf2bce1 100644
--- a/MediaBrowser.Providers/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs
+++ b/MediaBrowser.Providers/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs
@@ -10,7 +10,6 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
-using MediaBrowser.Providers.Movies;
using MediaBrowser.Providers.Tmdb.Models.Collections;
using MediaBrowser.Providers.Tmdb.Models.General;
using MediaBrowser.Providers.Tmdb.Movies;
diff --git a/MediaBrowser.Providers/Tmdb/BoxSets/TmdbBoxSetProvider.cs b/MediaBrowser.Providers/Tmdb/BoxSets/TmdbBoxSetProvider.cs
index a215177a9..dd3783ffb 100644
--- a/MediaBrowser.Providers/Tmdb/BoxSets/TmdbBoxSetProvider.cs
+++ b/MediaBrowser.Providers/Tmdb/BoxSets/TmdbBoxSetProvider.cs
@@ -37,7 +37,14 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets
private readonly IHttpClient _httpClient;
private readonly ILibraryManager _libraryManager;
- public TmdbBoxSetProvider(ILogger logger, IJsonSerializer json, IServerConfigurationManager config, IFileSystem fileSystem, ILocalizationManager localization, IHttpClient httpClient, ILibraryManager libraryManager)
+ public TmdbBoxSetProvider(
+ ILogger<TmdbBoxSetProvider> logger,
+ IJsonSerializer json,
+ IServerConfigurationManager config,
+ IFileSystem fileSystem,
+ ILocalizationManager localization,
+ IHttpClient httpClient,
+ ILibraryManager libraryManager)
{
_logger = logger;
_json = json;
diff --git a/MediaBrowser.Providers/Tmdb/Models/Search/ExternalIdLookupResult.cs b/MediaBrowser.Providers/Tmdb/Models/Search/ExternalIdLookupResult.cs
index 6d9fe7081..d19f4e8cb 100644
--- a/MediaBrowser.Providers/Tmdb/Models/Search/ExternalIdLookupResult.cs
+++ b/MediaBrowser.Providers/Tmdb/Models/Search/ExternalIdLookupResult.cs
@@ -1,5 +1,4 @@
using System.Collections.Generic;
-using MediaBrowser.Providers.Movies;
namespace MediaBrowser.Providers.Tmdb.Models.Search
{
diff --git a/MediaBrowser.Providers/Tmdb/Movies/GenericTmdbMovieInfo.cs b/MediaBrowser.Providers/Tmdb/Movies/GenericTmdbMovieInfo.cs
index b7b447b68..ad42b564c 100644
--- a/MediaBrowser.Providers/Tmdb/Movies/GenericTmdbMovieInfo.cs
+++ b/MediaBrowser.Providers/Tmdb/Movies/GenericTmdbMovieInfo.cs
@@ -11,10 +11,8 @@ using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
-using MediaBrowser.Providers.Movies;
using MediaBrowser.Providers.Tmdb.Models.Movies;
using Microsoft.Extensions.Logging;
diff --git a/MediaBrowser.Providers/Tmdb/Movies/TmdbImageProvider.cs b/MediaBrowser.Providers/Tmdb/Movies/TmdbImageProvider.cs
index cdb96e6ac..039a49728 100644
--- a/MediaBrowser.Providers/Tmdb/Movies/TmdbImageProvider.cs
+++ b/MediaBrowser.Providers/Tmdb/Movies/TmdbImageProvider.cs
@@ -13,7 +13,6 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Serialization;
-using MediaBrowser.Providers.Movies;
using MediaBrowser.Providers.Tmdb.Models.General;
using MediaBrowser.Providers.Tmdb.Models.Movies;
diff --git a/MediaBrowser.Providers/Tmdb/Movies/TmdbMovieProvider.cs b/MediaBrowser.Providers/Tmdb/Movies/TmdbMovieProvider.cs
index a1bea5847..fbb87d25d 100644
--- a/MediaBrowser.Providers/Tmdb/Movies/TmdbMovieProvider.cs
+++ b/MediaBrowser.Providers/Tmdb/Movies/TmdbMovieProvider.cs
@@ -19,7 +19,6 @@ using MediaBrowser.Model.IO;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Serialization;
-using MediaBrowser.Providers.Movies;
using MediaBrowser.Providers.Tmdb.Models.Movies;
using Microsoft.Extensions.Logging;
@@ -43,7 +42,15 @@ namespace MediaBrowser.Providers.Tmdb.Movies
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
- public TmdbMovieProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILogger logger, ILocalizationManager localization, ILibraryManager libraryManager, IApplicationHost appHost)
+ public TmdbMovieProvider(
+ IJsonSerializer jsonSerializer,
+ IHttpClient httpClient,
+ IFileSystem fileSystem,
+ IServerConfigurationManager configurationManager,
+ ILogger<TmdbMovieProvider> logger,
+ ILocalizationManager localization,
+ ILibraryManager libraryManager,
+ IApplicationHost appHost)
{
_jsonSerializer = jsonSerializer;
_httpClient = httpClient;
diff --git a/MediaBrowser.Providers/Tmdb/Music/TmdbMusicVideoProvider.cs b/MediaBrowser.Providers/Tmdb/Music/TmdbMusicVideoProvider.cs
index f3f8a92cf..81909fa38 100644
--- a/MediaBrowser.Providers/Tmdb/Music/TmdbMusicVideoProvider.cs
+++ b/MediaBrowser.Providers/Tmdb/Music/TmdbMusicVideoProvider.cs
@@ -6,7 +6,6 @@ using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Providers;
-using MediaBrowser.Providers.Movies;
using MediaBrowser.Providers.Tmdb.Movies;
namespace MediaBrowser.Providers.Tmdb.Music
diff --git a/MediaBrowser.Providers/Tmdb/People/TmdbPersonImageProvider.cs b/MediaBrowser.Providers/Tmdb/People/TmdbPersonImageProvider.cs
index 44ccbf453..e205d796a 100644
--- a/MediaBrowser.Providers/Tmdb/People/TmdbPersonImageProvider.cs
+++ b/MediaBrowser.Providers/Tmdb/People/TmdbPersonImageProvider.cs
@@ -10,7 +10,6 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Serialization;
-using MediaBrowser.Providers.Movies;
using MediaBrowser.Providers.Tmdb.Models.General;
using MediaBrowser.Providers.Tmdb.Models.People;
using MediaBrowser.Providers.Tmdb.Movies;
diff --git a/MediaBrowser.Providers/Tmdb/People/TmdbPersonProvider.cs b/MediaBrowser.Providers/Tmdb/People/TmdbPersonProvider.cs
index 130403e4d..588001169 100644
--- a/MediaBrowser.Providers/Tmdb/People/TmdbPersonProvider.cs
+++ b/MediaBrowser.Providers/Tmdb/People/TmdbPersonProvider.cs
@@ -37,7 +37,12 @@ namespace MediaBrowser.Providers.Tmdb.People
private readonly IHttpClient _httpClient;
private readonly ILogger _logger;
- public TmdbPersonProvider(IFileSystem fileSystem, IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IHttpClient httpClient, ILogger logger)
+ public TmdbPersonProvider(
+ IFileSystem fileSystem,
+ IServerConfigurationManager configurationManager,
+ IJsonSerializer jsonSerializer,
+ IHttpClient httpClient,
+ ILogger<TmdbPersonProvider> logger)
{
_fileSystem = fileSystem;
_configurationManager = configurationManager;
@@ -234,7 +239,7 @@ namespace MediaBrowser.Providers.Tmdb.People
{
Directory.CreateDirectory(Path.GetDirectoryName(dataFilePath));
- using (var fs = _fileSystem.GetFileStream(dataFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
+ using (var fs = new FileStream(dataFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true))
{
await json.CopyToAsync(fs).ConfigureAwait(false);
}
diff --git a/MediaBrowser.Providers/Tmdb/TV/TmdbEpisodeImageProvider.cs b/MediaBrowser.Providers/Tmdb/TV/TmdbEpisodeImageProvider.cs
index 51e7891a1..558c8149e 100644
--- a/MediaBrowser.Providers/Tmdb/TV/TmdbEpisodeImageProvider.cs
+++ b/MediaBrowser.Providers/Tmdb/TV/TmdbEpisodeImageProvider.cs
@@ -13,7 +13,6 @@ using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Serialization;
-using MediaBrowser.Providers.Movies;
using MediaBrowser.Providers.Tmdb.Models.General;
using MediaBrowser.Providers.Tmdb.Movies;
using Microsoft.Extensions.Logging;
diff --git a/MediaBrowser.Providers/Tmdb/TV/TmdbEpisodeProviderBase.cs b/MediaBrowser.Providers/Tmdb/TV/TmdbEpisodeProviderBase.cs
index 2003261c9..e87fe9332 100644
--- a/MediaBrowser.Providers/Tmdb/TV/TmdbEpisodeProviderBase.cs
+++ b/MediaBrowser.Providers/Tmdb/TV/TmdbEpisodeProviderBase.cs
@@ -8,7 +8,6 @@ using MediaBrowser.Controller.Configuration;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
-using MediaBrowser.Providers.Movies;
using MediaBrowser.Providers.Tmdb.Models.TV;
using MediaBrowser.Providers.Tmdb.Movies;
using Microsoft.Extensions.Logging;
diff --git a/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonImageProvider.cs b/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonImageProvider.cs
index 24cc8c73b..698a43604 100644
--- a/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonImageProvider.cs
+++ b/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonImageProvider.cs
@@ -10,8 +10,6 @@ using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Globalization;
-using MediaBrowser.Model.IO;
using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Providers.Tmdb.Models.General;
diff --git a/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonProvider.cs b/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonProvider.cs
index fc0cde8b3..5ad331971 100644
--- a/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonProvider.cs
+++ b/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonProvider.cs
@@ -14,7 +14,6 @@ using MediaBrowser.Model.IO;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Serialization;
-using MediaBrowser.Providers.Movies;
using MediaBrowser.Providers.Tmdb.Models.TV;
using MediaBrowser.Providers.Tmdb.Movies;
using Microsoft.Extensions.Logging;
diff --git a/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesImageProvider.cs b/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesImageProvider.cs
index 882ec7574..0460fe994 100644
--- a/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesImageProvider.cs
+++ b/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesImageProvider.cs
@@ -12,7 +12,6 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Serialization;
-using MediaBrowser.Providers.Movies;
using MediaBrowser.Providers.Tmdb.Models.General;
using MediaBrowser.Providers.Tmdb.Models.TV;
using MediaBrowser.Providers.Tmdb.Movies;
diff --git a/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesProvider.cs b/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesProvider.cs
index 304f34c25..7195dc42a 100644
--- a/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesProvider.cs
+++ b/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesProvider.cs
@@ -17,7 +17,6 @@ using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Serialization;
-using MediaBrowser.Providers.Movies;
using MediaBrowser.Providers.Tmdb.Models.Search;
using MediaBrowser.Providers.Tmdb.Models.TV;
using MediaBrowser.Providers.Tmdb.Movies;
@@ -40,7 +39,14 @@ namespace MediaBrowser.Providers.Tmdb.TV
private readonly IHttpClient _httpClient;
private readonly ILibraryManager _libraryManager;
- public TmdbSeriesProvider(IJsonSerializer jsonSerializer, IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILogger logger, ILocalizationManager localization, IHttpClient httpClient, ILibraryManager libraryManager)
+ public TmdbSeriesProvider(
+ IJsonSerializer jsonSerializer,
+ IFileSystem fileSystem,
+ IServerConfigurationManager configurationManager,
+ ILogger<TmdbSeriesProvider> logger,
+ ILocalizationManager localization,
+ IHttpClient httpClient,
+ ILibraryManager libraryManager)
{
_jsonSerializer = jsonSerializer;
_fileSystem = fileSystem;
diff --git a/MediaBrowser.Providers/Tmdb/Trailers/TmdbTrailerProvider.cs b/MediaBrowser.Providers/Tmdb/Trailers/TmdbTrailerProvider.cs
index b0dec0245..b15de0125 100644
--- a/MediaBrowser.Providers/Tmdb/Trailers/TmdbTrailerProvider.cs
+++ b/MediaBrowser.Providers/Tmdb/Trailers/TmdbTrailerProvider.cs
@@ -5,7 +5,6 @@ using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Providers;
-using MediaBrowser.Providers.Movies;
using MediaBrowser.Providers.Tmdb.Movies;
namespace MediaBrowser.Providers.Tmdb.Trailers
diff --git a/MediaBrowser.Providers/Users/UserMetadataService.cs b/MediaBrowser.Providers/Users/UserMetadataService.cs
index 9c2e27816..fb6c91b6c 100644
--- a/MediaBrowser.Providers/Users/UserMetadataService.cs
+++ b/MediaBrowser.Providers/Users/UserMetadataService.cs
@@ -13,7 +13,7 @@ namespace MediaBrowser.Providers.Users
{
public UserMetadataService(
IServerConfigurationManager serverConfigurationManager,
- ILogger logger,
+ ILogger<UserMetadataService> logger,
IProviderManager providerManager,
IFileSystem fileSystem,
ILibraryManager libraryManager)
diff --git a/MediaBrowser.Providers/Videos/VideoMetadataService.cs b/MediaBrowser.Providers/Videos/VideoMetadataService.cs
index 996af0368..21378ada0 100644
--- a/MediaBrowser.Providers/Videos/VideoMetadataService.cs
+++ b/MediaBrowser.Providers/Videos/VideoMetadataService.cs
@@ -13,7 +13,7 @@ namespace MediaBrowser.Providers.Videos
{
public VideoMetadataService(
IServerConfigurationManager serverConfigurationManager,
- ILogger logger,
+ ILogger<VideoMetadataService> logger,
IProviderManager providerManager,
IFileSystem fileSystem,
ILibraryManager libraryManager)
diff --git a/MediaBrowser.Providers/Years/YearMetadataService.cs b/MediaBrowser.Providers/Years/YearMetadataService.cs
index 414795e35..2a0fa19ea 100644
--- a/MediaBrowser.Providers/Years/YearMetadataService.cs
+++ b/MediaBrowser.Providers/Years/YearMetadataService.cs
@@ -13,7 +13,7 @@ namespace MediaBrowser.Providers.Years
{
public YearMetadataService(
IServerConfigurationManager serverConfigurationManager,
- ILogger logger,
+ ILogger<YearMetadataService> logger,
IProviderManager providerManager,
IFileSystem fileSystem,
ILibraryManager libraryManager)
diff --git a/MediaBrowser.WebDashboard/Api/ConfigurationPageInfo.cs b/MediaBrowser.WebDashboard/Api/ConfigurationPageInfo.cs
index b8f9e09b5..e49a4be8a 100644
--- a/MediaBrowser.WebDashboard/Api/ConfigurationPageInfo.cs
+++ b/MediaBrowser.WebDashboard/Api/ConfigurationPageInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using MediaBrowser.Common.Plugins;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Model.Plugins;
@@ -6,29 +8,6 @@ namespace MediaBrowser.WebDashboard.Api
{
public class ConfigurationPageInfo
{
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
- public bool EnableInMainMenu { get; set; }
- public string MenuSection { get; set; }
- public string MenuIcon { get; set; }
-
- public string DisplayName { get; set; }
-
- /// <summary>
- /// Gets the type of the configuration page.
- /// </summary>
- /// <value>The type of the configuration page.</value>
- public ConfigurationPageType ConfigurationPageType { get; set; }
-
- /// <summary>
- /// Gets or sets the plugin id.
- /// </summary>
- /// <value>The plugin id.</value>
- public string PluginId { get; set; }
-
public ConfigurationPageInfo(IPluginConfigurationPage page)
{
Name = page.Name;
@@ -54,5 +33,31 @@ namespace MediaBrowser.WebDashboard.Api
// Don't use "N" because it needs to match Plugin.Id
PluginId = plugin.Id.ToString();
}
+
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ /// <value>The name.</value>
+ public string Name { get; set; }
+
+ public bool EnableInMainMenu { get; set; }
+
+ public string MenuSection { get; set; }
+
+ public string MenuIcon { get; set; }
+
+ public string DisplayName { get; set; }
+
+ /// <summary>
+ /// Gets or sets the type of the configuration page.
+ /// </summary>
+ /// <value>The type of the configuration page.</value>
+ public ConfigurationPageType ConfigurationPageType { get; set; }
+
+ /// <summary>
+ /// Gets or sets the plugin id.
+ /// </summary>
+ /// <value>The plugin id.</value>
+ public string PluginId { get; set; }
}
}
diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs
index fadf32b28..99d8d044f 100644
--- a/MediaBrowser.WebDashboard/Api/DashboardService.cs
+++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs
@@ -1,5 +1,10 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1402
+#pragma warning disable SA1649
+
using System;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
@@ -9,18 +14,16 @@ using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Plugins;
-using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Plugins;
-using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Services;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.WebDashboard.Api
{
/// <summary>
- /// Class GetDashboardConfigurationPages
+ /// Class GetDashboardConfigurationPages.
/// </summary>
[Route("/web/ConfigurationPages", "GET")]
public class GetDashboardConfigurationPages : IReturn<List<ConfigurationPageInfo>>
@@ -30,11 +33,12 @@ namespace MediaBrowser.WebDashboard.Api
/// </summary>
/// <value>The type of the page.</value>
public ConfigurationPageType? PageType { get; set; }
+
public bool? EnableInMainMenu { get; set; }
}
/// <summary>
- /// Class GetDashboardConfigurationPage
+ /// Class GetDashboardConfigurationPage.
/// </summary>
[Route("/web/ConfigurationPage", "GET")]
public class GetDashboardConfigurationPage
@@ -58,7 +62,7 @@ namespace MediaBrowser.WebDashboard.Api
}
/// <summary>
- /// Class GetDashboardResource
+ /// Class GetDashboardResource.
/// </summary>
[Route("/web/{ResourceName*}", "GET", IsHidden = true)]
public class GetDashboardResource
@@ -68,6 +72,7 @@ namespace MediaBrowser.WebDashboard.Api
/// </summary>
/// <value>The name.</value>
public string ResourceName { get; set; }
+
/// <summary>
/// Gets or sets the V.
/// </summary>
@@ -81,7 +86,7 @@ namespace MediaBrowser.WebDashboard.Api
}
/// <summary>
- /// Class DashboardService
+ /// Class DashboardService.
/// </summary>
public class DashboardService : IService, IRequiresRequest
{
@@ -96,46 +101,43 @@ namespace MediaBrowser.WebDashboard.Api
/// </summary>
/// <value>The HTTP result factory.</value>
private readonly IHttpResultFactory _resultFactory;
-
- /// <summary>
- /// Gets or sets the request context.
- /// </summary>
- /// <value>The request context.</value>
- public IRequest Request { get; set; }
-
- /// <summary>
- /// The _app host
- /// </summary>
private readonly IServerApplicationHost _appHost;
-
- /// <summary>
- /// The _server configuration manager
- /// </summary>
private readonly IServerConfigurationManager _serverConfigurationManager;
-
private readonly IFileSystem _fileSystem;
- private IResourceFileManager _resourceFileManager;
+ private readonly IResourceFileManager _resourceFileManager;
/// <summary>
/// Initializes a new instance of the <see cref="DashboardService" /> class.
/// </summary>
+ /// <param name="logger">The logger.</param>
+ /// <param name="appHost">The application host.</param>
+ /// <param name="resourceFileManager">The resource file manager.</param>
+ /// <param name="serverConfigurationManager">The server configuration manager.</param>
+ /// <param name="fileSystem">The file system.</param>
+ /// <param name="resultFactory">The result factory.</param>
public DashboardService(
+ ILogger<DashboardService> logger,
IServerApplicationHost appHost,
IResourceFileManager resourceFileManager,
IServerConfigurationManager serverConfigurationManager,
IFileSystem fileSystem,
- ILogger logger,
IHttpResultFactory resultFactory)
{
+ _logger = logger;
_appHost = appHost;
+ _resourceFileManager = resourceFileManager;
_serverConfigurationManager = serverConfigurationManager;
_fileSystem = fileSystem;
- _logger = logger;
_resultFactory = resultFactory;
- _resourceFileManager = resourceFileManager;
}
/// <summary>
+ /// Gets or sets the request context.
+ /// </summary>
+ /// <value>The request context.</value>
+ public IRequest Request { get; set; }
+
+ /// <summary>
/// Gets the path for the web interface.
/// </summary>
/// <value>The path for the web interface.</value>
@@ -152,6 +154,7 @@ namespace MediaBrowser.WebDashboard.Api
}
}
+ [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
public object Get(GetFavIcon request)
{
return Get(new GetDashboardResource
@@ -165,6 +168,7 @@ namespace MediaBrowser.WebDashboard.Api
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.Object.</returns>
+ [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
public Task<object> Get(GetDashboardConfigurationPage request)
{
IPlugin plugin = null;
@@ -189,7 +193,7 @@ namespace MediaBrowser.WebDashboard.Api
stream = plugin.GetType().Assembly.GetManifestResourceStream(altPage.Item1.EmbeddedResourcePath);
isJs = string.Equals(Path.GetExtension(altPage.Item1.EmbeddedResourcePath), ".js", StringComparison.OrdinalIgnoreCase);
- isTemplate = altPage.Item1.EmbeddedResourcePath.EndsWith(".template.html");
+ isTemplate = altPage.Item1.EmbeddedResourcePath.EndsWith(".template.html", StringComparison.Ordinal);
}
}
@@ -237,7 +241,6 @@ namespace MediaBrowser.WebDashboard.Api
// Don't allow a failing plugin to fail them all
var configPages = pages.Select(p =>
{
-
try
{
return new ConfigurationPageInfo(p);
@@ -288,6 +291,7 @@ namespace MediaBrowser.WebDashboard.Api
return GetPluginPages(plugin).Select(i => new ConfigurationPageInfo(plugin, i.Item1));
}
+ [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
public object Get(GetRobotsTxt request)
{
return Get(new GetDashboardResource
@@ -350,7 +354,7 @@ namespace MediaBrowser.WebDashboard.Api
return await _resultFactory.GetStaticResult(Request, cacheKey, null, cacheDuration, contentType, () => GetResourceStream(basePath, path, localizationCulture)).ConfigureAwait(false);
}
- return await _resultFactory.GetStaticFileResult(Request, _resourceFileManager.GetResourcePath(basePath, path));
+ return await _resultFactory.GetStaticFileResult(Request, _resourceFileManager.GetResourcePath(basePath, path)).ConfigureAwait(false);
}
private string GetLocalizationCulture()
@@ -392,9 +396,9 @@ namespace MediaBrowser.WebDashboard.Api
{
Directory.Delete(targetPath, true);
}
- catch (IOException)
+ catch (IOException ex)
{
-
+ _logger.LogError(ex, "Error deleting {Path}", targetPath);
}
CopyDirectory(inputPath, targetPath);
@@ -402,9 +406,9 @@ namespace MediaBrowser.WebDashboard.Api
var appVersion = _appHost.ApplicationVersionString;
- await DumpHtml(packageCreator, inputPath, targetPath, mode, appVersion);
+ await DumpHtml(packageCreator, inputPath, targetPath, mode, appVersion).ConfigureAwait(false);
- return "";
+ return string.Empty;
}
private async Task DumpHtml(PackageCreator packageCreator, string source, string destination, string mode, string appVersion)
@@ -425,9 +429,9 @@ namespace MediaBrowser.WebDashboard.Api
private async Task DumpFile(PackageCreator packageCreator, string resourceVirtualPath, string destinationFilePath, string mode, string appVersion)
{
using (var stream = await packageCreator.GetResource(resourceVirtualPath, mode, null, appVersion).ConfigureAwait(false))
- using (var fs = _fileSystem.GetFileStream(destinationFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
+ using (var fs = new FileStream(destinationFilePath, FileMode.Create, FileAccess.Write, FileShare.Read))
{
- await stream.CopyToAsync(fs);
+ await stream.CopyToAsync(fs).ConfigureAwait(false);
}
}
@@ -435,14 +439,17 @@ namespace MediaBrowser.WebDashboard.Api
{
Directory.CreateDirectory(destination);
- //Now Create all of the directories
+ // Now Create all of the directories
foreach (var dirPath in _fileSystem.GetDirectories(source, true))
- Directory.CreateDirectory(dirPath.FullName.Replace(source, destination));
+ {
+ Directory.CreateDirectory(dirPath.FullName.Replace(source, destination, StringComparison.Ordinal));
+ }
- //Copy all the files & Replaces any files with the same name
+ // Copy all the files & Replaces any files with the same name
foreach (var newPath in _fileSystem.GetFiles(source, true))
- File.Copy(newPath.FullName, newPath.FullName.Replace(source, destination), true);
+ {
+ File.Copy(newPath.FullName, newPath.FullName.Replace(source, destination, StringComparison.Ordinal), true);
+ }
}
}
-
}
diff --git a/MediaBrowser.WebDashboard/Api/PackageCreator.cs b/MediaBrowser.WebDashboard/Api/PackageCreator.cs
index 133bf61e8..ad996c5a9 100644
--- a/MediaBrowser.WebDashboard/Api/PackageCreator.cs
+++ b/MediaBrowser.WebDashboard/Api/PackageCreator.cs
@@ -1,4 +1,7 @@
+#pragma warning disable CS1591
+
using System;
+using System.Globalization;
using System.IO;
using System.Text;
using System.Threading.Tasks;
@@ -44,10 +47,7 @@ namespace MediaBrowser.WebDashboard.Api
return string.Equals(Path.GetExtension(path), ".html", StringComparison.OrdinalIgnoreCase);
}
- /// <summary>
- /// Modifies the HTML by adding common meta tags, css and js.
- /// </summary>
- /// <returns>Task{Stream}.</returns>
+ // Modifies the HTML by adding common meta tags, css and js.
public async Task<Stream> ModifyHtml(
string path,
Stream sourceStream,
@@ -67,30 +67,29 @@ namespace MediaBrowser.WebDashboard.Api
{
var lang = localizationCulture.Split('-')[0];
- html = html.Replace("<html", "<html data-culture=\"" + localizationCulture + "\" lang=\"" + lang + "\"");
+ html = html.Replace("<html", "<html data-culture=\"" + localizationCulture + "\" lang=\"" + lang + "\"", StringComparison.Ordinal);
}
if (isMainIndexPage)
{
- html = html.Replace("<head>", "<head>" + GetMetaTags(mode));
+ html = html.Replace("<head>", "<head>" + GetMetaTags(mode), StringComparison.Ordinal);
}
// Disable embedded scripts from plugins. We'll run them later once resources have loaded
if (html.IndexOf("<script", StringComparison.OrdinalIgnoreCase) != -1)
{
- html = html.Replace("<script", "<!--<script");
- html = html.Replace("</script>", "</script>-->");
+ html = html.Replace("<script", "<!--<script", StringComparison.Ordinal);
+ html = html.Replace("</script>", "</script>-->", StringComparison.Ordinal);
}
if (isMainIndexPage)
{
- html = html.Replace("</body>", GetCommonJavascript(mode, appVersion) + "</body>");
+ html = html.Replace("</body>", GetCommonJavascript(mode, appVersion) + "</body>", StringComparison.Ordinal);
}
var bytes = Encoding.UTF8.GetBytes(html);
return new MemoryStream(bytes);
-
}
/// <summary>
@@ -123,11 +122,11 @@ namespace MediaBrowser.WebDashboard.Api
builder.Append("<script>");
if (!string.IsNullOrWhiteSpace(mode))
{
- builder.AppendFormat("window.appMode='{0}';", mode);
+ builder.AppendFormat(CultureInfo.InvariantCulture, "window.appMode='{0}';", mode);
}
else
{
- builder.AppendFormat("window.dashboardVersion='{0}';", version);
+ builder.AppendFormat(CultureInfo.InvariantCulture, "window.dashboardVersion='{0}';", version);
}
builder.Append("</script>");
diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
index 1d256d689..da52b852a 100644
--- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
+++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
@@ -19,6 +19,19 @@
<TargetFramework>netstandard2.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
+ <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+ </PropertyGroup>
+
+ <!-- Code Analyzers-->
+ <ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
+ <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8" PrivateAssets="All" />
+ <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
+ <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" />
+ <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
+ </ItemGroup>
+
+ <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
+ <CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
</Project>
diff --git a/MediaBrowser.WebDashboard/ServerEntryPoint.cs b/MediaBrowser.WebDashboard/ServerEntryPoint.cs
index 18ed54a78..5c7e8b3c7 100644
--- a/MediaBrowser.WebDashboard/ServerEntryPoint.cs
+++ b/MediaBrowser.WebDashboard/ServerEntryPoint.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
@@ -6,24 +8,25 @@ using MediaBrowser.Controller.Plugins;
namespace MediaBrowser.WebDashboard
{
- public class ServerEntryPoint : IServerEntryPoint
+ public sealed class ServerEntryPoint : IServerEntryPoint
{
- /// <summary>
- /// Gets the list of plugin configuration pages
- /// </summary>
- /// <value>The configuration pages.</value>
- public List<IPluginConfigurationPage> PluginConfigurationPages { get; private set; }
-
private readonly IApplicationHost _appHost;
- public static ServerEntryPoint Instance { get; private set; }
-
public ServerEntryPoint(IApplicationHost appHost)
{
_appHost = appHost;
Instance = this;
}
+ public static ServerEntryPoint Instance { get; private set; }
+
+ /// <summary>
+ /// Gets the list of plugin configuration pages.
+ /// </summary>
+ /// <value>The configuration pages.</value>
+ public List<IPluginConfigurationPage> PluginConfigurationPages { get; private set; }
+
+ /// <inheritdoc />
public Task RunAsync()
{
PluginConfigurationPages = _appHost.GetExports<IPluginConfigurationPage>().ToList();
@@ -31,6 +34,7 @@ namespace MediaBrowser.WebDashboard
return Task.CompletedTask;
}
+ /// <inheritdoc />
public void Dispose()
{
}
diff --git a/MediaBrowser.XbmcMetadata/Configuration/NfoConfigurationExtension.cs b/MediaBrowser.XbmcMetadata/Configuration/NfoConfigurationExtension.cs
new file mode 100644
index 000000000..fe3bc3cd3
--- /dev/null
+++ b/MediaBrowser.XbmcMetadata/Configuration/NfoConfigurationExtension.cs
@@ -0,0 +1,15 @@
+#pragma warning disable CS1591
+
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Model.Configuration;
+
+namespace MediaBrowser.XbmcMetadata.Configuration
+{
+ public static class NfoConfigurationExtension
+ {
+ public static XbmcMetadataOptions GetNfoConfiguration(this IConfigurationManager manager)
+ {
+ return manager.GetConfiguration<XbmcMetadataOptions>("xbmcmetadata");
+ }
+ }
+}
diff --git a/MediaBrowser.XbmcMetadata/Configuration/NfoOptions.cs b/MediaBrowser.XbmcMetadata/Configuration/NfoConfigurationFactory.cs
index 60dcde4db..8325bfdbd 100644
--- a/MediaBrowser.XbmcMetadata/Configuration/NfoOptions.cs
+++ b/MediaBrowser.XbmcMetadata/Configuration/NfoConfigurationFactory.cs
@@ -1,10 +1,12 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.Configuration;
namespace MediaBrowser.XbmcMetadata.Configuration
{
- public class ConfigurationFactory : IConfigurationFactory
+ public class NfoConfigurationFactory : IConfigurationFactory
{
/// <inheritdoc />
public IEnumerable<ConfigurationStore> GetConfigurations()
@@ -19,12 +21,4 @@ namespace MediaBrowser.XbmcMetadata.Configuration
};
}
}
-
- public static class ConfigurationExtension
- {
- public static XbmcMetadataOptions GetNfoConfiguration(this IConfigurationManager manager)
- {
- return manager.GetConfiguration<XbmcMetadataOptions>("xbmcmetadata");
- }
- }
}
diff --git a/MediaBrowser.XbmcMetadata/EntryPoint.cs b/MediaBrowser.XbmcMetadata/EntryPoint.cs
index fe4d50efa..571953b47 100644
--- a/MediaBrowser.XbmcMetadata/EntryPoint.cs
+++ b/MediaBrowser.XbmcMetadata/EntryPoint.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
@@ -12,7 +14,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.XbmcMetadata
{
- public class EntryPoint : IServerEntryPoint
+ public sealed class EntryPoint : IServerEntryPoint
{
private readonly IUserDataManager _userDataManager;
private readonly ILogger _logger;
@@ -21,7 +23,7 @@ namespace MediaBrowser.XbmcMetadata
public EntryPoint(
IUserDataManager userDataManager,
- ILogger logger,
+ ILogger<EntryPoint> logger,
IProviderManager providerManager,
IConfigurationManager config)
{
diff --git a/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj b/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj
index 0d62cf8c5..e26282095 100644
--- a/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj
+++ b/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj
@@ -13,6 +13,19 @@
<TargetFramework>netstandard2.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
+ <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+ </PropertyGroup>
+
+ <!-- Code Analyzers-->
+ <ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
+ <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8" PrivateAssets="All" />
+ <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
+ <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" />
+ <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
+ </ItemGroup>
+
+ <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
+ <CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
</Project>
diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
index b8d0e6560..36b9a9c1f 100644
--- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
+++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -12,7 +14,6 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Extensions;
using MediaBrowser.XbmcMetadata.Configuration;
using MediaBrowser.XbmcMetadata.Savers;
using Microsoft.Extensions.Logging;
@@ -28,6 +29,9 @@ namespace MediaBrowser.XbmcMetadata.Parsers
/// <summary>
/// Initializes a new instance of the <see cref="BaseNfoParser{T}" /> class.
/// </summary>
+ /// <param name="logger">The logger.</param>
+ /// <param name="config">the configuration manager.</param>
+ /// <param name="providerManager">The provider manager.</param>
public BaseNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager)
{
Logger = logger;
@@ -49,13 +53,13 @@ namespace MediaBrowser.XbmcMetadata.Parsers
protected virtual string MovieDbParserSearchString => "themoviedb.org/movie/";
/// <summary>
- /// Fetches metadata for an item from one xml file
+ /// Fetches metadata for an item from one xml file.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="metadataFile">The metadata file.</param>
/// <param name="cancellationToken">The cancellation token.</param>
- /// <exception cref="ArgumentNullException">
- /// </exception>
+ /// <exception cref="ArgumentNullException"><c>item</c> is <c>null</c>.</exception>
+ /// <exception cref="ArgumentException"><c>metadataFile</c> is <c>null</c> or empty.</exception>
public void Fetch(MetadataResult<T> item, string metadataFile, CancellationToken cancellationToken)
{
if (item == null)
@@ -81,7 +85,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
}
}
- //Additional Mappings
+ // Additional Mappings
_validProviderIds.Add("collectionnumber", "TmdbCollection");
_validProviderIds.Add("tmdbcolid", "TmdbCollection");
_validProviderIds.Add("imdb_id", "Imdb");
@@ -124,6 +128,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
}
}
}
+
return;
}
@@ -197,14 +202,13 @@ namespace MediaBrowser.XbmcMetadata.Parsers
}
catch (XmlException)
{
-
}
}
}
protected void ParseProviderLinks(T item, string xml)
{
- //Look for a match for the Regex pattern "tt" followed by 7 digits
+ // Look for a match for the Regex pattern "tt" followed by 7 digits
var m = Regex.Match(xml, @"tt([0-9]{7})", RegexOptions.IgnoreCase);
if (m.Success)
{
@@ -268,6 +272,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
Logger.LogWarning("Invalid Added value found: " + val);
}
}
+
break;
}
@@ -279,6 +284,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
{
item.OriginalTitle = val;
}
+
break;
}
@@ -310,6 +316,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
{
item.ForcedSortName = val;
}
+
break;
}
@@ -359,7 +366,6 @@ namespace MediaBrowser.XbmcMetadata.Parsers
}
return null;
-
}).Where(i => i.HasValue).Select(i => i.Value).ToArray();
}
@@ -374,6 +380,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
{
item.Tagline = val;
}
+
break;
}
@@ -388,6 +395,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
.Where(i => !string.IsNullOrWhiteSpace(i))
.ToArray();
}
+
break;
}
@@ -399,6 +407,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
{
item.OfficialRating = rating;
}
+
break;
}
@@ -410,6 +419,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
{
item.CustomRating = val;
}
+
break;
}
@@ -424,6 +434,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
item.RunTimeTicks = TimeSpan.FromMinutes(runtime).Ticks;
}
}
+
break;
}
@@ -436,6 +447,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
{
hasAspectRatio.AspectRatio = val;
}
+
break;
}
@@ -447,6 +459,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
{
item.IsLocked = string.Equals("true", val, StringComparison.OrdinalIgnoreCase);
}
+
break;
}
@@ -456,16 +469,9 @@ namespace MediaBrowser.XbmcMetadata.Parsers
if (!string.IsNullOrWhiteSpace(val))
{
- //var parts = val.Split('/')
- // .Select(i => i.Trim())
- // .Where(i => !string.IsNullOrWhiteSpace(i));
-
- //foreach (var p in parts)
- //{
- // item.AddStudio(p);
- //}
item.AddStudio(val);
}
+
break;
}
@@ -478,10 +484,13 @@ namespace MediaBrowser.XbmcMetadata.Parsers
{
continue;
}
+
itemResult.AddPerson(p);
}
+
break;
}
+
case "credits":
{
var val = reader.ReadElementContentAsString();
@@ -497,9 +506,11 @@ namespace MediaBrowser.XbmcMetadata.Parsers
{
continue;
}
+
itemResult.AddPerson(p);
}
}
+
break;
}
@@ -512,8 +523,10 @@ namespace MediaBrowser.XbmcMetadata.Parsers
{
continue;
}
+
itemResult.AddPerson(p);
}
+
break;
}
@@ -535,6 +548,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
{
reader.Read();
}
+
break;
}
@@ -548,6 +562,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
item.AddTrailerUrl(val);
}
+
break;
}
@@ -563,6 +578,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
hasDisplayOrder.DisplayOrder = val;
}
}
+
break;
}
@@ -583,7 +599,6 @@ namespace MediaBrowser.XbmcMetadata.Parsers
case "rating":
{
-
var rating = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(rating))
@@ -594,6 +609,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
item.CommunityRating = val;
}
}
+
break;
}
@@ -650,6 +666,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
item.AddGenre(p);
}
}
+
break;
}
@@ -661,6 +678,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
{
item.AddTag(val);
}
+
break;
}
@@ -677,6 +695,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
{
reader.Read();
}
+
break;
}
@@ -694,6 +713,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
{
reader.Skip();
}
+
break;
}
}
@@ -717,10 +737,12 @@ namespace MediaBrowser.XbmcMetadata.Parsers
reader.Read();
continue;
}
+
using (var subtree = reader.ReadSubtree())
{
FetchFromStreamDetailsNode(subtree, item);
}
+
break;
}
@@ -755,10 +777,12 @@ namespace MediaBrowser.XbmcMetadata.Parsers
reader.Read();
continue;
}
+
using (var subtree = reader.ReadSubtree())
{
FetchFromVideoNode(subtree, item);
}
+
break;
}
@@ -815,6 +839,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
video.Video3DFormat = Video3DFormat.MVC;
}
}
+
break;
}
@@ -864,8 +889,10 @@ namespace MediaBrowser.XbmcMetadata.Parsers
{
role = val;
}
+
break;
}
+
case "sortorder":
{
var val = reader.ReadElementContentAsString();
@@ -877,6 +904,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
sortOrder = intVal;
}
}
+
break;
}
@@ -910,7 +938,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
};
/// <summary>
- /// Used to split names of comma or pipe delimeted genres and people
+ /// Used to split names of comma or pipe delimeted genres and people.
/// </summary>
/// <param name="value">The value.</param>
/// <returns>IEnumerable{System.String}.</returns>
@@ -920,7 +948,9 @@ namespace MediaBrowser.XbmcMetadata.Parsers
// Only split by comma if there is no pipe in the string
// We have to be careful to not split names like Matthew, Jr.
- var separator = value.IndexOf('|') == -1 && value.IndexOf(';') == -1 ? new[] { ',' } : new[] { '|', ';' };
+ var separator = value.IndexOf('|', StringComparison.Ordinal) == -1 && value.IndexOf(';', StringComparison.Ordinal) == -1
+ ? new[] { ',' }
+ : new[] { '|', ';' };
value = value.Trim().Trim(separator);
diff --git a/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs
index 82ac6c548..9cc0344c1 100644
--- a/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs
+++ b/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs
@@ -11,8 +11,17 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.XbmcMetadata.Parsers
{
+ /// <summary>
+ /// Nfo parser for episodes.
+ /// </summary>
public class EpisodeNfoParser : BaseNfoParser<Episode>
{
+ /// <summary>
+ /// Initializes a new instance of the <see cref="EpisodeNfoParser"/> class.
+ /// </summary>
+ /// <param name="logger">The logger.</param>
+ /// <param name="config">the configuration manager.</param>
+ /// <param name="providerManager">The provider manager.</param>
public EpisodeNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager)
: base(logger, config, providerManager)
{
@@ -63,7 +72,6 @@ namespace MediaBrowser.XbmcMetadata.Parsers
}
catch (XmlException)
{
-
}
}
}
@@ -86,6 +94,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
item.ParentIndexNumber = num;
}
}
+
break;
}
@@ -100,6 +109,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
item.IndexNumber = num;
}
}
+
break;
}
@@ -114,6 +124,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
item.IndexNumberEnd = num;
}
}
+
break;
}
@@ -197,7 +208,6 @@ namespace MediaBrowser.XbmcMetadata.Parsers
break;
}
-
default:
base.FetchDataFromXmlNode(reader, itemResult);
break;
diff --git a/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs
index 79d9111fe..c17212f31 100644
--- a/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs
+++ b/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs
@@ -11,8 +11,17 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.XbmcMetadata.Parsers
{
+ /// <summary>
+ /// Nfo parser for movies.
+ /// </summary>
public class MovieNfoParser : BaseNfoParser<Video>
{
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MovieNfoParser"/> class.
+ /// </summary>
+ /// <param name="logger">The logger.</param>
+ /// <param name="config">the configuration manager.</param>
+ /// <param name="providerManager">The provider manager.</param>
public MovieNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager)
: base(logger, config, providerManager)
{
@@ -50,6 +59,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
break;
}
+
case "set":
{
var movie = item as Movie;
@@ -65,7 +75,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
if (!string.IsNullOrWhiteSpace(val) && movie != null)
{
// TODO Handle this better later
- if (val.IndexOf('<') == -1)
+ if (val.IndexOf('<', StringComparison.Ordinal) == -1)
{
movie.CollectionName = val;
}
@@ -119,9 +129,6 @@ namespace MediaBrowser.XbmcMetadata.Parsers
private void ParseSetXml(string xml, Movie movie)
{
- //xml = xml.Substring(xml.IndexOf('<'));
- //xml = xml.Substring(0, xml.LastIndexOf('>'));
-
// These are not going to be valid xml so no sense in causing the provider to fail and spamming the log with exceptions
try
{
@@ -155,7 +162,6 @@ namespace MediaBrowser.XbmcMetadata.Parsers
}
catch (XmlException)
{
-
}
}
}
diff --git a/MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs
index d6c06f982..d8cd88b9a 100644
--- a/MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs
+++ b/MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs
@@ -7,8 +7,17 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.XbmcMetadata.Parsers
{
+ /// <summary>
+ /// Nfo parser for seasons.
+ /// </summary>
public class SeasonNfoParser : BaseNfoParser<Season>
{
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SeasonNfoParser"/> class.
+ /// </summary>
+ /// <param name="logger">The logger.</param>
+ /// <param name="config">the configuration manager.</param>
+ /// <param name="providerManager">The provider manager.</param>
public SeasonNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager)
: base(logger, config, providerManager)
{
@@ -32,6 +41,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
item.IndexNumber = num;
}
}
+
break;
}
diff --git a/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs
index 278858b4a..0954ae206 100644
--- a/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs
+++ b/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs
@@ -9,8 +9,17 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.XbmcMetadata.Parsers
{
+ /// <summary>
+ /// Nfo parser for series.
+ /// </summary>
public class SeriesNfoParser : BaseNfoParser<Series>
{
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SeriesNfoParser"/> class.
+ /// </summary>
+ /// <param name="logger">The logger.</param>
+ /// <param name="config">the configuration manager.</param>
+ /// <param name="providerManager">The provider manager.</param>
public SeriesNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager)
: base(logger, config, providerManager)
{
@@ -39,20 +48,25 @@ namespace MediaBrowser.XbmcMetadata.Parsers
{
tvdbId = reader.ReadElementContentAsString();
}
+
if (!string.IsNullOrWhiteSpace(imdbId))
{
item.SetProviderId(MetadataProviders.Imdb, imdbId);
}
+
if (!string.IsNullOrWhiteSpace(tmdbId))
{
item.SetProviderId(MetadataProviders.Tmdb, tmdbId);
}
+
if (!string.IsNullOrWhiteSpace(tvdbId))
{
item.SetProviderId(MetadataProviders.Tvdb, tvdbId);
}
+
break;
}
+
case "airs_dayofweek":
{
item.AirDays = TVUtils.GetAirDays(reader.ReadElementContentAsString());
@@ -67,6 +81,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
{
item.AirTime = val;
}
+
break;
}
diff --git a/MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs
index 3517bc32c..4b1ee4c9c 100644
--- a/MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs
+++ b/MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs
@@ -9,13 +9,27 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.XbmcMetadata.Providers
{
+ /// <summary>
+ /// Nfo provider for albums.
+ /// </summary>
public class AlbumNfoProvider : BaseNfoProvider<MusicAlbum>
{
private readonly ILogger _logger;
private readonly IConfigurationManager _config;
private readonly IProviderManager _providerManager;
- public AlbumNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager)
+ /// <summary>
+ /// Initializes a new instance of the <see cref="AlbumNfoProvider"/> class.
+ /// </summary>
+ /// <param name="logger">The logger.</param>
+ /// <param name="fileSystem">The file system.</param>
+ /// <param name="config">the configuration manager.</param>
+ /// <param name="providerManager">The provider manager.</param>
+ public AlbumNfoProvider(
+ ILogger<AlbumNfoProvider> logger,
+ IFileSystem fileSystem,
+ IConfigurationManager config,
+ IProviderManager providerManager)
: base(fileSystem)
{
_logger = logger;
diff --git a/MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs
index 03d8dbc7e..3bbfa6e83 100644
--- a/MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs
+++ b/MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs
@@ -9,13 +9,27 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.XbmcMetadata.Providers
{
+ /// <summary>
+ /// Nfo provider for artists.
+ /// </summary>
public class ArtistNfoProvider : BaseNfoProvider<MusicArtist>
{
private readonly ILogger _logger;
private readonly IConfigurationManager _config;
private readonly IProviderManager _providerManager;
- public ArtistNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager)
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ArtistNfoProvider"/> class.
+ /// </summary>
+ /// <param name="logger">The logger.</param>
+ /// <param name="fileSystem">The file system.</param>
+ /// <param name="config">the configuration manager.</param>
+ /// <param name="providerManager">The provider manager.</param>
+ public ArtistNfoProvider(
+ IFileSystem fileSystem,
+ ILogger<ArtistNfoProvider> logger,
+ IConfigurationManager config,
+ IProviderManager providerManager)
: base(fileSystem)
{
_logger = logger;
diff --git a/MediaBrowser.XbmcMetadata/Providers/BaseNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/BaseNfoProvider.cs
index ff3368bb9..6ad6c18a5 100644
--- a/MediaBrowser.XbmcMetadata/Providers/BaseNfoProvider.cs
+++ b/MediaBrowser.XbmcMetadata/Providers/BaseNfoProvider.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.IO;
using System.Threading;
using System.Threading.Tasks;
@@ -19,6 +21,9 @@ namespace MediaBrowser.XbmcMetadata.Providers
}
/// <inheritdoc />
+ public string Name => BaseNfoSaver.SaverName;
+
+ /// <inheritdoc />
public Task<MetadataResult<T>> GetMetadata(
ItemInfo info,
IDirectoryService directoryService,
@@ -55,12 +60,6 @@ namespace MediaBrowser.XbmcMetadata.Providers
}
/// <inheritdoc />
- protected abstract void Fetch(MetadataResult<T> result, string path, CancellationToken cancellationToken);
-
- /// <inheritdoc />
- protected abstract FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService);
-
- /// <inheritdoc />
public bool HasChanged(BaseItem item, IDirectoryService directoryService)
{
var file = GetXmlFile(new ItemInfo(item), directoryService);
@@ -73,6 +72,8 @@ namespace MediaBrowser.XbmcMetadata.Providers
return file.Exists && _fileSystem.GetLastWriteTimeUtc(file) > item.DateLastSaved;
}
- public string Name => BaseNfoSaver.SaverName;
+ protected abstract void Fetch(MetadataResult<T> result, string path, CancellationToken cancellationToken);
+
+ protected abstract FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService);
}
}
diff --git a/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs
index 7410b97e0..84c99664a 100644
--- a/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs
+++ b/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Linq;
using System.Threading;
using MediaBrowser.Common.Configuration;
@@ -10,14 +12,18 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.XbmcMetadata.Providers
{
- public class BaseVideoNfoProvider<T> : BaseNfoProvider<T>
+ public abstract class BaseVideoNfoProvider<T> : BaseNfoProvider<T>
where T : Video, new()
{
private readonly ILogger _logger;
private readonly IConfigurationManager _config;
private readonly IProviderManager _providerManager;
- public BaseVideoNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager)
+ public BaseVideoNfoProvider(
+ ILogger logger,
+ IFileSystem fileSystem,
+ IConfigurationManager config,
+ IProviderManager providerManager)
: base(fileSystem)
{
_logger = logger;
diff --git a/MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs
index b2278ba4a..b2dc2e38e 100644
--- a/MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs
+++ b/MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs
@@ -9,13 +9,27 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.XbmcMetadata.Providers
{
+ /// <summary>
+ /// Nfo provider for episodes.
+ /// </summary>
public class EpisodeNfoProvider : BaseNfoProvider<Episode>
{
private readonly ILogger _logger;
private readonly IConfigurationManager _config;
private readonly IProviderManager _providerManager;
- public EpisodeNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager)
+ /// <summary>
+ /// Initializes a new instance of the <see cref="EpisodeNfoProvider"/> class.
+ /// </summary>
+ /// <param name="logger">The logger.</param>
+ /// <param name="fileSystem">The file system.</param>
+ /// <param name="config">the configuration manager.</param>
+ /// <param name="providerManager">The provider manager.</param>
+ public EpisodeNfoProvider(
+ ILogger<EpisodeNfoProvider> logger,
+ IFileSystem fileSystem,
+ IConfigurationManager config,
+ IProviderManager providerManager)
: base(fileSystem)
{
_logger = logger;
diff --git a/MediaBrowser.XbmcMetadata/Providers/MovieNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/MovieNfoProvider.cs
index d21164c02..e3f6bada1 100644
--- a/MediaBrowser.XbmcMetadata/Providers/MovieNfoProvider.cs
+++ b/MediaBrowser.XbmcMetadata/Providers/MovieNfoProvider.cs
@@ -1,5 +1,4 @@
using MediaBrowser.Common.Configuration;
-using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.IO;
@@ -7,26 +6,24 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.XbmcMetadata.Providers
{
+ /// <summary>
+ /// Nfo provider for movies.
+ /// </summary>
public class MovieNfoProvider : BaseVideoNfoProvider<Movie>
{
- public MovieNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager)
- : base(fileSystem, logger, config, providerManager)
- {
- }
- }
-
- public class MusicVideoNfoProvider : BaseVideoNfoProvider<MusicVideo>
- {
- public MusicVideoNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager)
- : base(fileSystem, logger, config, providerManager)
- {
- }
- }
-
- public class VideoNfoProvider : BaseVideoNfoProvider<Video>
- {
- public VideoNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager)
- : base(fileSystem, logger, config, providerManager)
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MovieNfoProvider"/> class.
+ /// </summary>
+ /// <param name="logger">The logger.</param>
+ /// <param name="fileSystem">The file system.</param>
+ /// <param name="config">the configuration manager.</param>
+ /// <param name="providerManager">The provider manager.</param>
+ public MovieNfoProvider(
+ ILogger<MovieNfoProvider> logger,
+ IFileSystem fileSystem,
+ IConfigurationManager config,
+ IProviderManager providerManager)
+ : base(logger, fileSystem, config, providerManager)
{
}
}
diff --git a/MediaBrowser.XbmcMetadata/Providers/MusicVideoNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/MusicVideoNfoProvider.cs
new file mode 100644
index 000000000..b490a7120
--- /dev/null
+++ b/MediaBrowser.XbmcMetadata/Providers/MusicVideoNfoProvider.cs
@@ -0,0 +1,26 @@
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.IO;
+using Microsoft.Extensions.Logging;
+
+namespace MediaBrowser.XbmcMetadata.Providers
+{
+ /// <summary>
+ /// Nfo provider for music videos.
+ /// </summary>
+ public class MusicVideoNfoProvider : BaseVideoNfoProvider<MusicVideo>
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MusicVideoNfoProvider"/> class.
+ /// </summary>
+ /// <param name="logger">The logger.</param>
+ /// <param name="fileSystem">The file system.</param>
+ /// <param name="config">the configuration manager.</param>
+ /// <param name="providerManager">The provider manager.</param>
+ public MusicVideoNfoProvider(ILogger<MusicVideoNfoProvider> logger, IFileSystem fileSystem, IConfigurationManager config, IProviderManager providerManager)
+ : base(logger, fileSystem, config, providerManager)
+ {
+ }
+ }
+}
diff --git a/MediaBrowser.XbmcMetadata/Providers/SeasonNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/SeasonNfoProvider.cs
index 2cf542054..63ddd6025 100644
--- a/MediaBrowser.XbmcMetadata/Providers/SeasonNfoProvider.cs
+++ b/MediaBrowser.XbmcMetadata/Providers/SeasonNfoProvider.cs
@@ -9,13 +9,27 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.XbmcMetadata.Providers
{
+ /// <summary>
+ /// Nfo provider for seasons.
+ /// </summary>
public class SeasonNfoProvider : BaseNfoProvider<Season>
{
private readonly ILogger _logger;
private readonly IConfigurationManager _config;
private readonly IProviderManager _providerManager;
- public SeasonNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager)
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SeasonNfoProvider"/> class.
+ /// </summary>
+ /// <param name="logger">The logger.</param>
+ /// <param name="fileSystem">The file system.</param>
+ /// <param name="config">the configuration manager.</param>
+ /// <param name="providerManager">The provider manager.</param>
+ public SeasonNfoProvider(
+ ILogger<SeasonNfoProvider> logger,
+ IFileSystem fileSystem,
+ IConfigurationManager config,
+ IProviderManager providerManager)
: base(fileSystem)
{
_logger = logger;
@@ -34,4 +48,3 @@ namespace MediaBrowser.XbmcMetadata.Providers
=> directoryService.GetFile(Path.Combine(info.Path, "season.nfo"));
}
}
-
diff --git a/MediaBrowser.XbmcMetadata/Providers/SeriesNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/SeriesNfoProvider.cs
index 25c8e0faf..d65914542 100644
--- a/MediaBrowser.XbmcMetadata/Providers/SeriesNfoProvider.cs
+++ b/MediaBrowser.XbmcMetadata/Providers/SeriesNfoProvider.cs
@@ -9,13 +9,27 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.XbmcMetadata.Providers
{
+ /// <summary>
+ /// Nfo provider for series.
+ /// </summary>
public class SeriesNfoProvider : BaseNfoProvider<Series>
{
private readonly ILogger _logger;
private readonly IConfigurationManager _config;
private readonly IProviderManager _providerManager;
- public SeriesNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager)
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SeriesNfoProvider"/> class.
+ /// </summary>
+ /// <param name="logger">The logger.</param>
+ /// <param name="fileSystem">The file system.</param>
+ /// <param name="config">the configuration manager.</param>
+ /// <param name="providerManager">The provider manager.</param>
+ public SeriesNfoProvider(
+ ILogger<SeriesNfoProvider> logger,
+ IFileSystem fileSystem,
+ IConfigurationManager config,
+ IProviderManager providerManager)
: base(fileSystem)
{
_logger = logger;
diff --git a/MediaBrowser.XbmcMetadata/Providers/VideoNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/VideoNfoProvider.cs
new file mode 100644
index 000000000..f66ad30ca
--- /dev/null
+++ b/MediaBrowser.XbmcMetadata/Providers/VideoNfoProvider.cs
@@ -0,0 +1,26 @@
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.IO;
+using Microsoft.Extensions.Logging;
+
+namespace MediaBrowser.XbmcMetadata.Providers
+{
+ /// <summary>
+ /// Nfo provider for videos.
+ /// </summary>
+ public class VideoNfoProvider : BaseVideoNfoProvider<Video>
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="VideoNfoProvider"/> class.
+ /// </summary>
+ /// <param name="logger">The logger.</param>
+ /// <param name="fileSystem">The file system.</param>
+ /// <param name="config">the configuration manager.</param>
+ /// <param name="providerManager">The provider manager.</param>
+ public VideoNfoProvider(ILogger<VideoNfoProvider> logger, IFileSystem fileSystem, IConfigurationManager config, IProviderManager providerManager)
+ : base(logger, fileSystem, config, providerManager)
+ {
+ }
+ }
+}
diff --git a/MediaBrowser.XbmcMetadata/Savers/AlbumNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/AlbumNfoSaver.cs
index 233b3cb89..c22f77dcd 100644
--- a/MediaBrowser.XbmcMetadata/Savers/AlbumNfoSaver.cs
+++ b/MediaBrowser.XbmcMetadata/Savers/AlbumNfoSaver.cs
@@ -13,15 +13,27 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.XbmcMetadata.Savers
{
+ /// <summary>
+ /// Nfo saver for albums.
+ /// </summary>
public class AlbumNfoSaver : BaseNfoSaver
{
+ /// <summary>
+ /// Initializes a new instance of the <see cref="AlbumNfoSaver"/> class.
+ /// </summary>
+ /// <param name="fileSystem">The file system.</param>
+ /// <param name="configurationManager">the server configuration manager.</param>
+ /// <param name="libraryManager">The library manager.</param>
+ /// <param name="userManager">The user manager.</param>
+ /// <param name="userDataManager">The user data manager.</param>
+ /// <param name="logger">The logger.</param>
public AlbumNfoSaver(
IFileSystem fileSystem,
IServerConfigurationManager configurationManager,
ILibraryManager libraryManager,
IUserManager userManager,
IUserDataManager userDataManager,
- ILogger logger)
+ ILogger<AlbumNfoSaver> logger)
: base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger)
{
}
@@ -74,7 +86,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
if (track.RunTimeTicks.HasValue)
{
- var time = TimeSpan.FromTicks(track.RunTimeTicks.Value).ToString(@"mm\:ss");
+ var time = TimeSpan.FromTicks(track.RunTimeTicks.Value).ToString(@"mm\:ss", CultureInfo.InvariantCulture);
writer.WriteElementString("duration", time);
}
diff --git a/MediaBrowser.XbmcMetadata/Savers/ArtistNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/ArtistNfoSaver.cs
index 04565ff7e..6365cdecb 100644
--- a/MediaBrowser.XbmcMetadata/Savers/ArtistNfoSaver.cs
+++ b/MediaBrowser.XbmcMetadata/Savers/ArtistNfoSaver.cs
@@ -12,9 +12,27 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.XbmcMetadata.Savers
{
+ /// <summary>
+ /// Nfo saver for artsist.
+ /// </summary>
public class ArtistNfoSaver : BaseNfoSaver
{
- public ArtistNfoSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger)
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ArtistNfoSaver"/> class.
+ /// </summary>
+ /// <param name="fileSystem">The file system.</param>
+ /// <param name="configurationManager">the server configuration manager.</param>
+ /// <param name="libraryManager">The library manager.</param>
+ /// <param name="userManager">The user manager.</param>
+ /// <param name="userDataManager">The user data manager.</param>
+ /// <param name="logger">The logger.</param>
+ public ArtistNfoSaver(
+ IFileSystem fileSystem,
+ IServerConfigurationManager configurationManager,
+ ILibraryManager libraryManager,
+ IUserManager userManager,
+ IUserDataManager userDataManager,
+ ILogger<ArtistNfoSaver> logger)
: base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger)
{
}
@@ -40,7 +58,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
{
var formatString = ConfigurationManager.GetNfoConfiguration().ReleaseDateFormat;
- writer.WriteElementString("disbanded", artist.EndDate.Value.ToLocalTime().ToString(formatString));
+ writer.WriteElementString("disbanded", artist.EndDate.Value.ToLocalTime().ToString(formatString, CultureInfo.InvariantCulture));
}
var albums = artist
diff --git a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs
index 981c3a53a..90e8b4b99 100644
--- a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs
+++ b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -28,6 +30,9 @@ namespace MediaBrowser.XbmcMetadata.Savers
public const string YouTubeWatchUrl = "https://www.youtube.com/watch?v=";
+ // filters control characters but allows only properly-formed surrogate sequences
+ private const string _invalidXMLCharsRegex = @"(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F\uFEFF\uFFFE\uFFFF]";
+
private static readonly HashSet<string> _commonTags = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"plot",
@@ -94,9 +99,6 @@ namespace MediaBrowser.XbmcMetadata.Savers
"countrycode"
};
- // filters control characters but allows only properly-formed surrogate sequences
- private const string _invalidXMLCharsRegex = @"(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F\uFEFF\uFFFE\uFFFF]";
-
protected BaseNfoSaver(
IFileSystem fileSystem,
IServerConfigurationManager configurationManager,
@@ -247,7 +249,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
if (baseItem != null)
{
- AddCommonNodes(baseItem, writer, LibraryManager, UserManager, UserDataManager, FileSystem, ConfigurationManager);
+ AddCommonNodes(baseItem, writer, LibraryManager, UserManager, UserDataManager, ConfigurationManager);
}
WriteCustomElements(item, writer);
@@ -265,11 +267,9 @@ namespace MediaBrowser.XbmcMetadata.Savers
}
catch (FileNotFoundException)
{
-
}
catch (IOException)
{
-
}
catch (XmlException ex)
{
@@ -364,8 +364,8 @@ namespace MediaBrowser.XbmcMetadata.Savers
writer.WriteElementString("samplingrate", stream.SampleRate.Value.ToString(CultureInfo.InvariantCulture));
}
- writer.WriteElementString("default", stream.IsDefault.ToString());
- writer.WriteElementString("forced", stream.IsForced.ToString());
+ writer.WriteElementString("default", stream.IsDefault.ToString(CultureInfo.InvariantCulture));
+ writer.WriteElementString("forced", stream.IsForced.ToString(CultureInfo.InvariantCulture));
if (stream.Type == MediaStreamType.Video)
{
@@ -384,7 +384,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
if (item is Video video)
{
- //AddChapters(video, builder, itemRepository);
+ // AddChapters(video, builder, itemRepository);
if (video.Video3DFormat.HasValue)
{
@@ -420,21 +420,19 @@ namespace MediaBrowser.XbmcMetadata.Savers
/// <summary>
/// Adds the common nodes.
/// </summary>
- /// <returns>Task.</returns>
private void AddCommonNodes(
BaseItem item,
XmlWriter writer,
ILibraryManager libraryManager,
IUserManager userManager,
IUserDataManager userDataRepo,
- IFileSystem fileSystem,
IServerConfigurationManager config)
{
var writtenProviderIds = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
var overview = (item.Overview ?? string.Empty)
.StripHtml()
- .Replace("&quot;", "'");
+ .Replace("&quot;", "'", StringComparison.Ordinal);
var options = config.GetNfoConfiguration();
@@ -455,7 +453,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
{
var outline = (item.Tagline ?? string.Empty)
.StripHtml()
- .Replace("&quot;", "'");
+ .Replace("&quot;", "'", StringComparison.Ordinal);
writer.WriteElementString("outline", outline);
}
@@ -476,7 +474,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
writer.WriteElementString("lockedfields", string.Join("|", item.LockedFields));
}
- writer.WriteElementString("dateadded", item.DateCreated.ToLocalTime().ToString(DateAddedFormat));
+ writer.WriteElementString("dateadded", item.DateCreated.ToLocalTime().ToString(DateAddedFormat, CultureInfo.InvariantCulture));
writer.WriteElementString("title", item.Name ?? string.Empty);
@@ -590,6 +588,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
{
writer.WriteElementString("language", item.PreferredMetadataLanguage);
}
+
if (!string.IsNullOrEmpty(item.PreferredMetadataCountryCode))
{
writer.WriteElementString("countrycode", item.PreferredMetadataCountryCode);
@@ -603,16 +602,16 @@ namespace MediaBrowser.XbmcMetadata.Savers
{
writer.WriteElementString(
"formed",
- item.PremiereDate.Value.ToLocalTime().ToString(formatString));
+ item.PremiereDate.Value.ToLocalTime().ToString(formatString, CultureInfo.InvariantCulture));
}
else
{
writer.WriteElementString(
"premiered",
- item.PremiereDate.Value.ToLocalTime().ToString(formatString));
+ item.PremiereDate.Value.ToLocalTime().ToString(formatString, CultureInfo.InvariantCulture));
writer.WriteElementString(
"releasedate",
- item.PremiereDate.Value.ToLocalTime().ToString(formatString));
+ item.PremiereDate.Value.ToLocalTime().ToString(formatString, CultureInfo.InvariantCulture));
}
}
@@ -624,7 +623,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
writer.WriteElementString(
"enddate",
- item.EndDate.Value.ToLocalTime().ToString(formatString));
+ item.EndDate.Value.ToLocalTime().ToString(formatString, CultureInfo.InvariantCulture));
}
}
@@ -780,12 +779,12 @@ namespace MediaBrowser.XbmcMetadata.Savers
if (options.SaveImagePathsInNfo)
{
- AddImages(item, writer, libraryManager, config);
+ AddImages(item, writer, libraryManager);
}
AddUserData(item, writer, userManager, userDataRepo, options);
- AddActors(people, writer, libraryManager, fileSystem, config, options.SaveImagePathsInNfo);
+ AddActors(people, writer, libraryManager, options.SaveImagePathsInNfo);
if (item is BoxSet folder)
{
@@ -828,7 +827,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
return url.Replace(YouTubeWatchUrl, "plugin://plugin.video.youtube/?action=play_video&videoid=", StringComparison.OrdinalIgnoreCase);
}
- private void AddImages(BaseItem item, XmlWriter writer, ILibraryManager libraryManager, IServerConfigurationManager config)
+ private void AddImages(BaseItem item, XmlWriter writer, ILibraryManager libraryManager)
{
writer.WriteStartElement("art");
@@ -836,12 +835,12 @@ namespace MediaBrowser.XbmcMetadata.Savers
if (image != null)
{
- writer.WriteElementString("poster", GetImagePathToSave(image, libraryManager, config));
+ writer.WriteElementString("poster", GetImagePathToSave(image, libraryManager));
}
foreach (var backdrop in item.GetImages(ImageType.Backdrop))
{
- writer.WriteElementString("fanart", GetImagePathToSave(backdrop, libraryManager, config));
+ writer.WriteElementString("fanart", GetImagePathToSave(backdrop, libraryManager));
}
writer.WriteEndElement();
@@ -893,7 +892,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
{
writer.WriteElementString(
"lastplayed",
- userdata.LastPlayedDate.Value.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss").ToLowerInvariant());
+ userdata.LastPlayedDate.Value.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture).ToLowerInvariant());
}
writer.WriteStartElement("resume");
@@ -911,7 +910,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
writer.WriteEndElement();
}
- private void AddActors(List<PersonInfo> people, XmlWriter writer, ILibraryManager libraryManager, IFileSystem fileSystem, IServerConfigurationManager config, bool saveImagePath)
+ private void AddActors(List<PersonInfo> people, XmlWriter writer, ILibraryManager libraryManager, bool saveImagePath)
{
foreach (var person in people)
{
@@ -953,7 +952,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
{
writer.WriteElementString(
"thumb",
- GetImagePathToSave(image, libraryManager, config));
+ GetImagePathToSave(image, libraryManager));
}
}
@@ -961,7 +960,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
}
}
- private string GetImagePathToSave(ItemImageInfo image, ILibraryManager libraryManager, IServerConfigurationManager config)
+ private string GetImagePathToSave(ItemImageInfo image, ILibraryManager libraryManager)
{
if (!image.IsLocalFile)
{
diff --git a/MediaBrowser.XbmcMetadata/Savers/EpisodeNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/EpisodeNfoSaver.cs
index 091c1957e..ac2fbb8d2 100644
--- a/MediaBrowser.XbmcMetadata/Savers/EpisodeNfoSaver.cs
+++ b/MediaBrowser.XbmcMetadata/Savers/EpisodeNfoSaver.cs
@@ -12,15 +12,33 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.XbmcMetadata.Savers
{
+ /// <summary>
+ /// Nfo saver for episodes.
+ /// </summary>
public class EpisodeNfoSaver : BaseNfoSaver
{
- public EpisodeNfoSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger)
+ private readonly CultureInfo _usCulture = new CultureInfo("en-US");
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="EpisodeNfoSaver"/> class.
+ /// </summary>
+ /// <param name="fileSystem">The file system.</param>
+ /// <param name="configurationManager">the server configuration manager.</param>
+ /// <param name="libraryManager">The library manager.</param>
+ /// <param name="userManager">The user manager.</param>
+ /// <param name="userDataManager">The user data manager.</param>
+ /// <param name="logger">The logger.</param>
+ public EpisodeNfoSaver(
+ IFileSystem fileSystem,
+ IServerConfigurationManager configurationManager,
+ ILibraryManager libraryManager,
+ IUserManager userManager,
+ IUserDataManager userDataManager,
+ ILogger<EpisodeNfoSaver> logger)
: base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger)
{
}
- private readonly CultureInfo _usCulture = new CultureInfo("en-US");
-
/// <inheritdoc />
protected override string GetLocalSavePath(BaseItem item)
=> Path.ChangeExtension(item.Path, ".nfo");
@@ -57,7 +75,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
{
var formatString = ConfigurationManager.GetNfoConfiguration().ReleaseDateFormat;
- writer.WriteElementString("aired", episode.PremiereDate.Value.ToLocalTime().ToString(formatString));
+ writer.WriteElementString("aired", episode.PremiereDate.Value.ToLocalTime().ToString(formatString, CultureInfo.InvariantCulture));
}
if (!episode.ParentIndexNumber.HasValue || episode.ParentIndexNumber.Value == 0)
diff --git a/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs
index 08a752e33..eef989a5b 100644
--- a/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs
+++ b/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs
@@ -14,9 +14,27 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.XbmcMetadata.Savers
{
+ /// <summary>
+ /// Nfo saver for movies.
+ /// </summary>
public class MovieNfoSaver : BaseNfoSaver
{
- public MovieNfoSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger)
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MovieNfoSaver"/> class.
+ /// </summary>
+ /// <param name="fileSystem">The file system.</param>
+ /// <param name="configurationManager">the server configuration manager.</param>
+ /// <param name="libraryManager">The library manager.</param>
+ /// <param name="userManager">The user manager.</param>
+ /// <param name="userDataManager">The user data manager.</param>
+ /// <param name="logger">The logger.</param>
+ public MovieNfoSaver(
+ IFileSystem fileSystem,
+ IServerConfigurationManager configurationManager,
+ ILibraryManager libraryManager,
+ IUserManager userManager,
+ IUserDataManager userDataManager,
+ ILogger<MovieNfoSaver> logger)
: base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger)
{
}
@@ -25,7 +43,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
protected override string GetLocalSavePath(BaseItem item)
=> GetMovieSavePaths(new ItemInfo(item)).FirstOrDefault();
- public static IEnumerable<string> GetMovieSavePaths(ItemInfo item)
+ internal static IEnumerable<string> GetMovieSavePaths(ItemInfo item)
{
if (item.VideoType == VideoType.Dvd && !item.IsPlaceHolder)
{
@@ -42,13 +60,6 @@ namespace MediaBrowser.XbmcMetadata.Savers
}
else
{
- // http://kodi.wiki/view/NFO_files/Movies
- // movie.nfo will override all and any .nfo files in the same folder as the media files if you use the "Use foldernames for lookups" setting. If you don't, then moviename.nfo is used
- //if (!item.IsInMixedFolder && item.ItemType == typeof(Movie))
- //{
- // list.Add(Path.Combine(item.ContainingFolderPath, "movie.nfo"));
- //}
-
yield return Path.ChangeExtension(item.Path, ".nfo");
if (!item.IsInMixedFolder)
@@ -95,6 +106,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
{
writer.WriteElementString("artist", artist);
}
+
if (!string.IsNullOrEmpty(musicVideo.Album))
{
writer.WriteElementString("album", musicVideo.Album);
diff --git a/MediaBrowser.XbmcMetadata/Savers/SeasonNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/SeasonNfoSaver.cs
index 25695121d..925a230bd 100644
--- a/MediaBrowser.XbmcMetadata/Savers/SeasonNfoSaver.cs
+++ b/MediaBrowser.XbmcMetadata/Savers/SeasonNfoSaver.cs
@@ -11,15 +11,27 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.XbmcMetadata.Savers
{
+ /// <summary>
+ /// Nfo saver for seasons.
+ /// </summary>
public class SeasonNfoSaver : BaseNfoSaver
{
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SeasonNfoSaver"/> class.
+ /// </summary>
+ /// <param name="fileSystem">The file system.</param>
+ /// <param name="configurationManager">the server configuration manager.</param>
+ /// <param name="libraryManager">The library manager.</param>
+ /// <param name="userManager">The user manager.</param>
+ /// <param name="userDataManager">The user data manager.</param>
+ /// <param name="logger">The logger.</param>
public SeasonNfoSaver(
IFileSystem fileSystem,
IServerConfigurationManager configurationManager,
ILibraryManager libraryManager,
IUserManager userManager,
IUserDataManager userDataManager,
- ILogger logger)
+ ILogger<SeasonNfoSaver> logger)
: base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger)
{
}
diff --git a/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs
index 8d7faece7..2a5d36d40 100644
--- a/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs
+++ b/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs
@@ -12,15 +12,27 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.XbmcMetadata.Savers
{
+ /// <summary>
+ /// Nfo saver for series.
+ /// </summary>
public class SeriesNfoSaver : BaseNfoSaver
{
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SeriesNfoSaver"/> class.
+ /// </summary>
+ /// <param name="fileSystem">The file system.</param>
+ /// <param name="configurationManager">the server configuration manager.</param>
+ /// <param name="libraryManager">The library manager.</param>
+ /// <param name="userManager">The user manager.</param>
+ /// <param name="userDataManager">The user data manager.</param>
+ /// <param name="logger">The logger.</param>
public SeriesNfoSaver(
IFileSystem fileSystem,
IServerConfigurationManager configurationManager,
ILibraryManager libraryManager,
IUserManager userManager,
IUserDataManager userDataManager,
- ILogger logger)
+ ILogger<SeriesNfoSaver> logger)
: base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger)
{
}
@@ -56,7 +68,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
: language;
writer.WriteStartElement("url");
- writer.WriteAttributeString("cache", string.Format("{0}.xml", tvdb));
+ writer.WriteAttributeString("cache", tvdb + ".xml");
writer.WriteString(
string.Format(
CultureInfo.InvariantCulture,
diff --git a/MediaBrowser.sln b/MediaBrowser.sln
index 416a434f4..1c84622ac 100644
--- a/MediaBrowser.sln
+++ b/MediaBrowser.sln
@@ -1,4 +1,4 @@
-Microsoft Visual Studio Solution File, Format Version 12.00
+Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26730.3
MinimumVisualStudioVersion = 10.0.40219.1
@@ -58,6 +58,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Naming.Tests", "te
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Api.Tests", "tests\Jellyfin.Api.Tests\Jellyfin.Api.Tests.csproj", "{A2FD0A10-8F62-4F9D-B171-FFDF9F0AFA9D}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Server.Implementations.Tests", "tests\Jellyfin.Server.Implementations.Tests\Jellyfin.Server.Implementations.Tests.csproj", "{2E3A1B4B-4225-4AAA-8B29-0181A84E7AEE}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Controller.Tests", "tests\Jellyfin.Controller.Tests\Jellyfin.Controller.Tests.csproj", "{462584F7-5023-4019-9EAC-B98CA458C0A0}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -164,6 +168,14 @@ Global
{A2FD0A10-8F62-4F9D-B171-FFDF9F0AFA9D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A2FD0A10-8F62-4F9D-B171-FFDF9F0AFA9D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A2FD0A10-8F62-4F9D-B171-FFDF9F0AFA9D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2E3A1B4B-4225-4AAA-8B29-0181A84E7AEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2E3A1B4B-4225-4AAA-8B29-0181A84E7AEE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2E3A1B4B-4225-4AAA-8B29-0181A84E7AEE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2E3A1B4B-4225-4AAA-8B29-0181A84E7AEE}.Release|Any CPU.Build.0 = Release|Any CPU
+ {462584F7-5023-4019-9EAC-B98CA458C0A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {462584F7-5023-4019-9EAC-B98CA458C0A0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {462584F7-5023-4019-9EAC-B98CA458C0A0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {462584F7-5023-4019-9EAC-B98CA458C0A0}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -194,5 +206,7 @@ Global
{28464062-0939-4AA7-9F7B-24DDDA61A7C0} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6}
{3998657B-1CCC-49DD-A19F-275DC8495F57} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6}
{A2FD0A10-8F62-4F9D-B171-FFDF9F0AFA9D} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6}
+ {2E3A1B4B-4225-4AAA-8B29-0181A84E7AEE} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6}
+ {462584F7-5023-4019-9EAC-B98CA458C0A0} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6}
EndGlobalSection
EndGlobal
diff --git a/README.md b/README.md
index bbac4dd25..ea54b8c8b 100644
--- a/README.md
+++ b/README.md
@@ -47,7 +47,7 @@ Jellyfin is a Free Software Media System that puts you in control of managing an
For further details, please see [our documentation page](https://docs.jellyfin.org/). To receive the latest updates, get help with Jellyfin, and join the community, please visit [one of our communication channels](https://docs.jellyfin.org/general/getting-help.html). For more information about the project, please see our [about page](https://docs.jellyfin.org/general/about.html).
<strong>Want to get started?</strong><br/>
-Choose from <a href="https://docs.jellyfin.org/general/administration/installing.html">Prebuilt Packages</a> or <a href="https://docs.jellyfin.org/general/administration/building.html">Build from Source</a>, then see our <a href="https://docs.jellyfin.org/general/administration/quick-start.html">quick start guide</a>.<br/>
+Choose from <a href="https://docs.jellyfin.org/general/administration/installing.html">Prebuilt Packages</a> or <a href="https://docs.jellyfin.org/general/administration/building.html">Build from Source</a>, then see our <a href="https://docs.jellyfin.org/general/quick-start.html">quick start guide</a>.<br/>
<strong>Something not working right?</strong><br/>
Open an <a href="https://docs.jellyfin.org/general/contributing/issues.html">Issue</a> on GitHub.<br/>
diff --git a/RSSDP/DeviceEventArgs.cs b/RSSDP/DeviceEventArgs.cs
index 3925ba248..05eb4a256 100644
--- a/RSSDP/DeviceEventArgs.cs
+++ b/RSSDP/DeviceEventArgs.cs
@@ -1,6 +1,4 @@
using System;
-using System.Collections.Generic;
-using System.Text;
namespace Rssdp
{
diff --git a/RSSDP/DeviceUnavailableEventArgs.cs b/RSSDP/DeviceUnavailableEventArgs.cs
index d90ddfb60..ef04904bd 100644
--- a/RSSDP/DeviceUnavailableEventArgs.cs
+++ b/RSSDP/DeviceUnavailableEventArgs.cs
@@ -1,7 +1,4 @@
using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Threading.Tasks;
namespace Rssdp
{
diff --git a/RSSDP/DiscoveredSsdpDevice.cs b/RSSDP/DiscoveredSsdpDevice.cs
index f42e7c674..1244ce523 100644
--- a/RSSDP/DiscoveredSsdpDevice.cs
+++ b/RSSDP/DiscoveredSsdpDevice.cs
@@ -1,8 +1,4 @@
using System;
-using System.Collections.Generic;
-using System.Net.Http;
-using System.Text;
-using System.Threading.Tasks;
using System.Net.Http.Headers;
namespace Rssdp
diff --git a/RSSDP/DisposableManagedObjectBase.cs b/RSSDP/DisposableManagedObjectBase.cs
index 0f656fb46..bb36229c4 100644
--- a/RSSDP/DisposableManagedObjectBase.cs
+++ b/RSSDP/DisposableManagedObjectBase.cs
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Text;
-using System.Threading.Tasks;
namespace Rssdp.Infrastructure
{
diff --git a/RSSDP/HttpParserBase.cs b/RSSDP/HttpParserBase.cs
index 76d816e7b..773a06cdb 100644
--- a/RSSDP/HttpParserBase.cs
+++ b/RSSDP/HttpParserBase.cs
@@ -2,8 +2,6 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
-using System.Text;
-using System.IO;
namespace Rssdp.Infrastructure
{
diff --git a/RSSDP/HttpRequestParser.cs b/RSSDP/HttpRequestParser.cs
index d4505b8ad..279ef883c 100644
--- a/RSSDP/HttpRequestParser.cs
+++ b/RSSDP/HttpRequestParser.cs
@@ -1,10 +1,6 @@
using System;
-using System.Collections.Generic;
using System.Linq;
-using System.Net;
using System.Net.Http;
-using System.Text;
-using System.Threading.Tasks;
namespace Rssdp.Infrastructure
{
diff --git a/RSSDP/HttpResponseParser.cs b/RSSDP/HttpResponseParser.cs
index a77c898ff..b96eaf625 100644
--- a/RSSDP/HttpResponseParser.cs
+++ b/RSSDP/HttpResponseParser.cs
@@ -1,10 +1,7 @@
using System;
-using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
-using System.Text;
-using System.Threading.Tasks;
namespace Rssdp.Infrastructure
{
diff --git a/RSSDP/IEnumerableExtensions.cs b/RSSDP/IEnumerableExtensions.cs
index c96542dca..371454893 100644
--- a/RSSDP/IEnumerableExtensions.cs
+++ b/RSSDP/IEnumerableExtensions.cs
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Text;
namespace Rssdp.Infrastructure
{
diff --git a/RSSDP/ISsdpDevicePublisher.cs b/RSSDP/ISsdpDevicePublisher.cs
index 7e914c109..96c15443d 100644
--- a/RSSDP/ISsdpDevicePublisher.cs
+++ b/RSSDP/ISsdpDevicePublisher.cs
@@ -1,4 +1,3 @@
-using System;
using System.Threading.Tasks;
namespace Rssdp.Infrastructure
diff --git a/RSSDP/SsdpConstants.cs b/RSSDP/SsdpConstants.cs
index 8372d1cb3..28a014fce 100644
--- a/RSSDP/SsdpConstants.cs
+++ b/RSSDP/SsdpConstants.cs
@@ -1,8 +1,3 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Threading.Tasks;
-
namespace Rssdp.Infrastructure
{
/// <summary>
diff --git a/RSSDP/SsdpEmbeddedDevice.cs b/RSSDP/SsdpEmbeddedDevice.cs
index 6f05518a9..4810703d7 100644
--- a/RSSDP/SsdpEmbeddedDevice.cs
+++ b/RSSDP/SsdpEmbeddedDevice.cs
@@ -1,7 +1,3 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-
namespace Rssdp
{
/// <summary>
diff --git a/deployment/centos-package-x64/Dockerfile b/deployment/centos-package-x64/Dockerfile
index 2b346f46a..08219a2e4 100644
--- a/deployment/centos-package-x64/Dockerfile
+++ b/deployment/centos-package-x64/Dockerfile
@@ -17,7 +17,7 @@ RUN yum install -y @buildsys-build rpmdevtools yum-plugins-core libcurl-devel fo
# Install recent NodeJS and Yarn
RUN curl -fSsLo /etc/yum.repos.d/yarn.repo https://dl.yarnpkg.com/rpm/yarn.repo \
- && rpm -i https://rpm.nodesource.com/pub_8.x/el/7/x86_64/nodesource-release-el7-1.noarch.rpm \
+ && rpm -i https://rpm.nodesource.com/pub_10.x/el/7/x86_64/nodesource-release-el7-1.noarch.rpm \
&& yum install -y yarn
# Install DotNET SDK
diff --git a/deployment/fedora-package-x64/Dockerfile b/deployment/fedora-package-x64/Dockerfile
index f5c3ab7a6..87120f3a0 100644
--- a/deployment/fedora-package-x64/Dockerfile
+++ b/deployment/fedora-package-x64/Dockerfile
@@ -1,4 +1,4 @@
-FROM fedora:29
+FROM fedora:31
# Docker build arguments
ARG SOURCE_DIR=/jellyfin
ARG PLATFORM_DIR=/jellyfin/deployment/fedora-package-x64
@@ -12,11 +12,11 @@ ENV ARTIFACT_DIR=/dist
RUN dnf update -y
# Install build dependencies
-RUN dnf install -y @buildsys-build rpmdevtools dnf-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel nodejs-yarn
+RUN dnf install -y @buildsys-build rpmdevtools git dnf-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel nodejs-yarn
# Install DotNET SDK
-RUN dnf copr enable -y @dotnet-sig/dotnet \
- && rpmdev-setuptree \
+RUN rpm --import https://packages.microsoft.com/keys/microsoft.asc \
+ && curl -o /etc/yum.repos.d/microsoft-prod.repo https://packages.microsoft.com/config/fedora/$(rpm -E %fedora)/prod.repo \
&& dnf install -y dotnet-sdk-${SDK_VERSION} dotnet-runtime-${SDK_VERSION}
# Create symlinks and directories
diff --git a/deployment/fedora-package-x64/pkg-src/jellyfin.spec b/deployment/fedora-package-x64/pkg-src/jellyfin.spec
index 914f3d44a..33c6f6f64 100644
--- a/deployment/fedora-package-x64/pkg-src/jellyfin.spec
+++ b/deployment/fedora-package-x64/pkg-src/jellyfin.spec
@@ -26,13 +26,13 @@ Source16: jellyfin-firewalld.xml
%{?systemd_requires}
BuildRequires: systemd
Requires(pre): shadow-utils
-BuildRequires: libcurl-devel, fontconfig-devel, freetype-devel, openssl-devel, glibc-devel, libicu-devel
+BuildRequires: libcurl-devel, fontconfig-devel, freetype-devel, openssl-devel, glibc-devel, libicu-devel, git
%if 0%{?fedora}
-BuildRequires: nodejs-yarn
+BuildRequires: nodejs-yarn, git
%else
# Requirements not packaged in main repos
-# From https://rpm.nodesource.com/pub_8.x/el/7/x86_64/
-BuildRequires: nodejs >= 8 yarn
+# From https://rpm.nodesource.com/pub_10.x/el/7/x86_64/
+BuildRequires: nodejs >= 10 yarn
%endif
Requires: libcurl, fontconfig, freetype, openssl, glibc libicu
# Requirements not packaged in main repos
diff --git a/deployment/linux-x64/docker-build.sh b/deployment/linux-x64/docker-build.sh
index 8860f943c..e33328a36 100755
--- a/deployment/linux-x64/docker-build.sh
+++ b/deployment/linux-x64/docker-build.sh
@@ -26,7 +26,7 @@ rm -rf ${web_build_dir}
version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )"
# Build archives
-dotnet publish --configuration Release --self-contained --runtime linux-x64 --output /dist/jellyfin_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none;UseAppHost=true"
+dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime linux-x64 --output /dist/jellyfin_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none;UseAppHost=true"
tar -cvzf /jellyfin_${version}.portable.tar.gz -C /dist jellyfin_${version}
rm -rf /dist/jellyfin_${version}
diff --git a/deployment/macos/docker-build.sh b/deployment/macos/docker-build.sh
index 1b4a554e6..f9417388d 100755
--- a/deployment/macos/docker-build.sh
+++ b/deployment/macos/docker-build.sh
@@ -26,7 +26,7 @@ rm -rf ${web_build_dir}
version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )"
# Build archives
-dotnet publish --configuration Release --self-contained --runtime osx-x64 --output /dist/jellyfin_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none;UseAppHost=true"
+dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime osx-x64 --output /dist/jellyfin_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none;UseAppHost=true"
tar -cvzf /jellyfin_${version}.portable.tar.gz -C /dist jellyfin_${version}
rm -rf /dist/jellyfin_${version}
diff --git a/deployment/portable/docker-build.sh b/deployment/portable/docker-build.sh
index 0cc6e84f0..094190bbf 100755
--- a/deployment/portable/docker-build.sh
+++ b/deployment/portable/docker-build.sh
@@ -26,7 +26,7 @@ rm -rf ${web_build_dir}
version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )"
# Build archives
-dotnet publish --configuration Release --output /dist/jellyfin_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none"
+dotnet publish Jellyfin.Server --configuration Release --output /dist/jellyfin_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none"
tar -cvzf /jellyfin_${version}.portable.tar.gz -C /dist jellyfin_${version}
rm -rf /dist/jellyfin_${version}
diff --git a/deployment/ubuntu-package-arm64/Dockerfile.amd64 b/deployment/ubuntu-package-arm64/Dockerfile.amd64
index ac4f7404d..b11994a18 100644
--- a/deployment/ubuntu-package-arm64/Dockerfile.amd64
+++ b/deployment/ubuntu-package-arm64/Dockerfile.amd64
@@ -23,7 +23,7 @@ RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4
# Install npm package manager
RUN wget -q -O- https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add - \
- && echo "deb https://deb.nodesource.com/node_8.x $(lsb_release -s -c) main" > /etc/apt/sources.list.d/npm.list \
+ && echo "deb https://deb.nodesource.com/node_10.x $(lsb_release -s -c) main" > /etc/apt/sources.list.d/npm.list \
&& apt update \
&& apt install -y nodejs
diff --git a/deployment/ubuntu-package-arm64/Dockerfile.arm64 b/deployment/ubuntu-package-arm64/Dockerfile.arm64
index af7084459..8f004b2f1 100644
--- a/deployment/ubuntu-package-arm64/Dockerfile.arm64
+++ b/deployment/ubuntu-package-arm64/Dockerfile.arm64
@@ -23,7 +23,7 @@ RUN wget https://download.visualstudio.microsoft.com/download/pr/5a4c8f96-1c73-4
# Install npm package manager
RUN wget -q -O- https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add - \
- && echo "deb https://deb.nodesource.com/node_8.x $(lsb_release -s -c) main" > /etc/apt/sources.list.d/npm.list \
+ && echo "deb https://deb.nodesource.com/node_10.x $(lsb_release -s -c) main" > /etc/apt/sources.list.d/npm.list \
&& apt update \
&& apt install -y nodejs
diff --git a/deployment/ubuntu-package-armhf/Dockerfile.amd64 b/deployment/ubuntu-package-armhf/Dockerfile.amd64
index 590eecab7..e475b1438 100644
--- a/deployment/ubuntu-package-armhf/Dockerfile.amd64
+++ b/deployment/ubuntu-package-armhf/Dockerfile.amd64
@@ -23,7 +23,7 @@ RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4
# Install npm package manager
RUN wget -q -O- https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add - \
- && echo "deb https://deb.nodesource.com/node_8.x $(lsb_release -s -c) main" > /etc/apt/sources.list.d/npm.list \
+ && echo "deb https://deb.nodesource.com/node_10.x $(lsb_release -s -c) main" > /etc/apt/sources.list.d/npm.list \
&& apt update \
&& apt install -y nodejs
diff --git a/deployment/ubuntu-package-armhf/Dockerfile.armhf b/deployment/ubuntu-package-armhf/Dockerfile.armhf
index 06a8dace2..0e71fa693 100644
--- a/deployment/ubuntu-package-armhf/Dockerfile.armhf
+++ b/deployment/ubuntu-package-armhf/Dockerfile.armhf
@@ -23,7 +23,7 @@ RUN wget https://download.visualstudio.microsoft.com/download/pr/67766a96-eb8c-4
# Install npm package manager
RUN wget -q -O- https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add - \
- && echo "deb https://deb.nodesource.com/node_8.x $(lsb_release -s -c) main" > /etc/apt/sources.list.d/npm.list \
+ && echo "deb https://deb.nodesource.com/node_10.x $(lsb_release -s -c) main" > /etc/apt/sources.list.d/npm.list \
&& apt update \
&& apt install -y nodejs
diff --git a/deployment/ubuntu-package-x64/Dockerfile b/deployment/ubuntu-package-x64/Dockerfile
index 8237ced29..e2dda6392 100644
--- a/deployment/ubuntu-package-x64/Dockerfile
+++ b/deployment/ubuntu-package-x64/Dockerfile
@@ -25,7 +25,7 @@ RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4
# Install npm package manager
RUN wget -q -O- https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add - \
- && echo "deb https://deb.nodesource.com/node_8.x $(lsb_release -s -c) main" > /etc/apt/sources.list.d/npm.list \
+ && echo "deb https://deb.nodesource.com/node_10.x $(lsb_release -s -c) main" > /etc/apt/sources.list.d/npm.list \
&& apt update \
&& apt install -y nodejs
diff --git a/deployment/win-x64/docker-build.sh b/deployment/win-x64/docker-build.sh
index 3f1ad78b5..79e5fb0bc 100755
--- a/deployment/win-x64/docker-build.sh
+++ b/deployment/win-x64/docker-build.sh
@@ -8,7 +8,7 @@ set -o xtrace
# Version variables
NSSM_VERSION="nssm-2.24-101-g897c7ad"
NSSM_URL="http://files.evilt.win/nssm/${NSSM_VERSION}.zip"
-FFMPEG_VERSION="ffmpeg-4.0.2-win64-static"
+FFMPEG_VERSION="ffmpeg-4.2.1-win64-static"
FFMPEG_URL="https://ffmpeg.zeranoe.com/builds/win64/static/${FFMPEG_VERSION}.zip"
# Move to source directory
@@ -32,7 +32,7 @@ rm -rf ${web_build_dir}
version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )"
# Build binary
-dotnet publish --configuration Release --self-contained --runtime win-x64 --output /dist/jellyfin_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none;UseAppHost=true"
+dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime win-x64 --output /dist/jellyfin_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none;UseAppHost=true"
# Prepare addins
addin_build_dir="$( mktemp -d )"
diff --git a/deployment/win-x86/docker-build.sh b/deployment/win-x86/docker-build.sh
index 7d79ba495..977dcf78f 100755
--- a/deployment/win-x86/docker-build.sh
+++ b/deployment/win-x86/docker-build.sh
@@ -8,7 +8,7 @@ set -o xtrace
# Version variables
NSSM_VERSION="nssm-2.24-101-g897c7ad"
NSSM_URL="http://files.evilt.win/nssm/${NSSM_VERSION}.zip"
-FFMPEG_VERSION="ffmpeg-4.0.2-win32-static"
+FFMPEG_VERSION="ffmpeg-4.2.1-win32-static"
FFMPEG_URL="https://ffmpeg.zeranoe.com/builds/win32/static/${FFMPEG_VERSION}.zip"
# Move to source directory
@@ -32,7 +32,7 @@ rm -rf ${web_build_dir}
version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )"
# Build binary
-dotnet publish --configuration Release --self-contained --runtime win-x86 --output /dist/jellyfin_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none;UseAppHost=true"
+dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime win-x86 --output /dist/jellyfin_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none;UseAppHost=true"
# Prepare addins
addin_build_dir="$( mktemp -d )"
diff --git a/deployment/windows/build-jellyfin.ps1 b/deployment/windows/build-jellyfin.ps1
index dde6eb8fc..c762137a7 100644
--- a/deployment/windows/build-jellyfin.ps1
+++ b/deployment/windows/build-jellyfin.ps1
@@ -15,6 +15,8 @@ param(
[ValidateSet('x64','x86', 'arm', 'arm64')][string]$Architecture = 'x64'
)
+$ProgressPreference = 'SilentlyContinue' # Speedup all downloads by hiding progress bars.
+
#PowershellCore and *nix check to make determine which temp dir to use.
if(($PSVersionTable.PSEdition -eq 'Core') -and (-not $IsWindows)){
$TempDir = mktemp -d
@@ -44,7 +46,8 @@ function Build-JellyFin {
function Install-FFMPEG {
param(
[string]$ResolvedInstallLocation,
- [string]$Architecture
+ [string]$Architecture,
+ [string]$FFMPEGVersionX86 = "ffmpeg-4.2.1-win32-shared"
)
Write-Verbose "Checking Architecture"
if($Architecture -notin @('x86','x64')){
@@ -55,7 +58,7 @@ function Install-FFMPEG {
Invoke-WebRequest -Uri https://repo.jellyfin.org/releases/server/windows/ffmpeg/jellyfin-ffmpeg.zip -UseBasicParsing -OutFile "$tempdir/ffmpeg.zip" | Write-Verbose
}else{
Write-Verbose "Downloading 32 bit FFMPEG"
- Invoke-WebRequest -Uri https://ffmpeg.zeranoe.com/builds/win32/shared/ffmpeg-4.0.2-win32-shared.zip -UseBasicParsing -OutFile "$tempdir/ffmpeg.zip" | Write-Verbose
+ Invoke-WebRequest -Uri https://ffmpeg.zeranoe.com/builds/win32/shared/$FFMPEGVersionX86.zip -UseBasicParsing -OutFile "$tempdir/ffmpeg.zip" | Write-Verbose
}
Expand-Archive "$tempdir/ffmpeg.zip" -DestinationPath "$tempdir/ffmpeg/" -Force | Write-Verbose
@@ -66,7 +69,7 @@ function Install-FFMPEG {
}
}else{
Write-Verbose "Copying Binaries to Jellyfin location"
- Get-ChildItem "$tempdir/ffmpeg/ffmpeg-4.0.2-win32-shared/bin" | ForEach-Object {
+ Get-ChildItem "$tempdir/ffmpeg/$FFMPEGVersionX86/bin" | ForEach-Object {
Copy-Item $_.FullName -Destination $installLocation | Write-Verbose
}
}
diff --git a/deployment/windows/jellyfin.nsi b/deployment/windows/jellyfin.nsi
index 5666d30f0..fada62d98 100644
--- a/deployment/windows/jellyfin.nsi
+++ b/deployment/windows/jellyfin.nsi
@@ -1,9 +1,9 @@
-; Shows a lot of debug information while compiling
-; This can be removed once stable.
-!verbose 4
-SetCompressor lzma
+!verbose 3
+SetCompressor /SOLID bzip2
ShowInstDetails show
ShowUninstDetails show
+Unicode True
+
;--------------------------------
!define SF_USELECTED 0 ; used to check selected options status, rest are inherited from Sections.nsh
@@ -73,7 +73,7 @@ ShowUninstDetails show
; TODO: Replace with nice Jellyfin Icons
!ifdef UXPATH
!define MUI_ICON "${UXPATH}\branding\NSIS\modern-install.ico" ; Installer Icon
- !define MUI_UNICON "${UXPATH}\branding\NSIS\modern-uninstall.ico" ; Uninstaller Icon
+ !define MUI_UNICON "${UXPATH}\branding\NSIS\modern-install.ico" ; Uninstaller Icon
!define MUI_HEADERIMAGE
!define MUI_HEADERIMAGE_BITMAP "${UXPATH}\branding\NSIS\installer-header.bmp"
diff --git a/jellyfin.ruleset b/jellyfin.ruleset
index 27d8a7cd9..45ab725eb 100644
--- a/jellyfin.ruleset
+++ b/jellyfin.ruleset
@@ -5,6 +5,8 @@
<Rule Id="SA1202" Action="Info" />
<!-- disable warning SA1204: Static members must appear before non-static members -->
<Rule Id="SA1204" Action="Info" />
+ <!-- disable warning SA1404: Code analysis suppression should have justification -->
+ <Rule Id="SA1404" Action="Info" />
<!-- disable warning SA1009: Closing parenthesis should be followed by a space. -->
<Rule Id="SA1009" Action="None" />
@@ -24,6 +26,10 @@
<Rule Id="SA1413" Action="None" />
<!-- disable warning SA1512: Single-line comments must not be followed by blank line -->
<Rule Id="SA1512" Action="None" />
+ <!-- disable warning SA1515: Single-line comment should be preceded by blank line -->
+ <Rule Id="SA1515" Action="None" />
+ <!-- disable warning SA1600: Elements should be documented -->
+ <Rule Id="SA1600" Action="None" />
<!-- disable warning SA1633: The file header is missing or not located at the top of the file -->
<Rule Id="SA1633" Action="None" />
</Rules>
@@ -35,6 +41,8 @@
<Rule Id="CA1032" Action="Info" />
<!-- disable warning CA1062: Validate arguments of public methods -->
<Rule Id="CA1062" Action="Info" />
+ <!-- disable warning CA1716: Identifiers should not match keywords -->
+ <Rule Id="CA1716" Action="Info" />
<!-- disable warning CA1720: Identifiers should not contain type names -->
<Rule Id="CA1720" Action="Info" />
<!-- disable warning CA1812: internal class that is apparently never instantiated.
@@ -43,14 +51,18 @@
<Rule Id="CA1812" Action="Info" />
<!-- disable warning CA1822: Member does not access instance data and can be marked as static -->
<Rule Id="CA1822" Action="Info" />
+ <!-- disable warning CA2000: Dispose objects before losing scope -->
+ <Rule Id="CA2000" Action="Info" />
<!-- disable warning CA1054: Change the type of parameter url from string to System.Uri -->
<Rule Id="CA1054" Action="None" />
+ <!-- disable warning CA1055: URI return values should not be strings -->
+ <Rule Id="CA1055" Action="None" />
+ <!-- disable warning CA1056: URI properties should not be strings -->
+ <Rule Id="CA1056" Action="None" />
<!-- disable warning CA1303: Do not pass literals as localized parameters -->
<Rule Id="CA1303" Action="None" />
<!-- disable warning CA1308: Normalize strings to uppercase -->
<Rule Id="CA1308" Action="None" />
- <!-- disable warning CA2000: Dispose objects before losing scope -->
- <Rule Id="CA2000" Action="None" />
</Rules>
</RuleSet>
diff --git a/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs b/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs
new file mode 100644
index 000000000..3b3d03c8b
--- /dev/null
+++ b/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs
@@ -0,0 +1,175 @@
+using System;
+using System.Linq;
+using System.Security.Claims;
+using System.Text.Encodings.Web;
+using System.Threading.Tasks;
+using AutoFixture;
+using AutoFixture.AutoMoq;
+using Jellyfin.Api.Auth;
+using Jellyfin.Api.Constants;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Net;
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Abstractions;
+using Microsoft.Extensions.Options;
+using Moq;
+using Xunit;
+
+namespace Jellyfin.Api.Tests.Auth
+{
+ public class CustomAuthenticationHandlerTests
+ {
+ private readonly IFixture _fixture;
+
+ private readonly Mock<IAuthService> _jellyfinAuthServiceMock;
+ private readonly Mock<IOptionsMonitor<AuthenticationSchemeOptions>> _optionsMonitorMock;
+ private readonly Mock<ISystemClock> _clockMock;
+ private readonly Mock<IServiceProvider> _serviceProviderMock;
+ private readonly Mock<IAuthenticationService> _authenticationServiceMock;
+ private readonly UrlEncoder _urlEncoder;
+ private readonly HttpContext _context;
+
+ private readonly CustomAuthenticationHandler _sut;
+ private readonly AuthenticationScheme _scheme;
+
+ public CustomAuthenticationHandlerTests()
+ {
+ var fixtureCustomizations = new AutoMoqCustomization
+ {
+ ConfigureMembers = true
+ };
+
+ _fixture = new Fixture().Customize(fixtureCustomizations);
+ AllowFixtureCircularDependencies();
+
+ _jellyfinAuthServiceMock = _fixture.Freeze<Mock<IAuthService>>();
+ _optionsMonitorMock = _fixture.Freeze<Mock<IOptionsMonitor<AuthenticationSchemeOptions>>>();
+ _clockMock = _fixture.Freeze<Mock<ISystemClock>>();
+ _serviceProviderMock = _fixture.Freeze<Mock<IServiceProvider>>();
+ _authenticationServiceMock = _fixture.Freeze<Mock<IAuthenticationService>>();
+ _fixture.Register<ILoggerFactory>(() => new NullLoggerFactory());
+
+ _urlEncoder = UrlEncoder.Default;
+
+ _serviceProviderMock.Setup(s => s.GetService(typeof(IAuthenticationService)))
+ .Returns(_authenticationServiceMock.Object);
+
+ _optionsMonitorMock.Setup(o => o.Get(It.IsAny<string>()))
+ .Returns(new AuthenticationSchemeOptions
+ {
+ ForwardAuthenticate = null
+ });
+
+ _context = new DefaultHttpContext
+ {
+ RequestServices = _serviceProviderMock.Object
+ };
+
+ _scheme = new AuthenticationScheme(
+ _fixture.Create<string>(),
+ null,
+ typeof(CustomAuthenticationHandler));
+
+ _sut = _fixture.Create<CustomAuthenticationHandler>();
+ _sut.InitializeAsync(_scheme, _context).Wait();
+ }
+
+ [Fact]
+ public async Task HandleAuthenticateAsyncShouldFailWithNullUser()
+ {
+ _jellyfinAuthServiceMock.Setup(
+ a => a.Authenticate(
+ It.IsAny<HttpRequest>(),
+ It.IsAny<AuthenticatedAttribute>()))
+ .Returns((User?)null);
+
+ var authenticateResult = await _sut.AuthenticateAsync();
+
+ Assert.False(authenticateResult.Succeeded);
+ Assert.Equal("Invalid user", authenticateResult.Failure.Message);
+ }
+
+ [Fact]
+ public async Task HandleAuthenticateAsyncShouldFailOnSecurityException()
+ {
+ var errorMessage = _fixture.Create<string>();
+
+ _jellyfinAuthServiceMock.Setup(
+ a => a.Authenticate(
+ It.IsAny<HttpRequest>(),
+ It.IsAny<AuthenticatedAttribute>()))
+ .Throws(new SecurityException(errorMessage));
+
+ var authenticateResult = await _sut.AuthenticateAsync();
+
+ Assert.False(authenticateResult.Succeeded);
+ Assert.Equal(errorMessage, authenticateResult.Failure.Message);
+ }
+
+ [Fact]
+ public async Task HandleAuthenticateAsyncShouldSucceedWithUser()
+ {
+ SetupUser();
+ var authenticateResult = await _sut.AuthenticateAsync();
+
+ Assert.True(authenticateResult.Succeeded);
+ Assert.Null(authenticateResult.Failure);
+ }
+
+ [Fact]
+ public async Task HandleAuthenticateAsyncShouldAssignNameClaim()
+ {
+ var user = SetupUser();
+ var authenticateResult = await _sut.AuthenticateAsync();
+
+ Assert.True(authenticateResult.Principal.HasClaim(ClaimTypes.Name, user.Name));
+ }
+
+ [Theory]
+ [InlineData(true)]
+ [InlineData(false)]
+ public async Task HandleAuthenticateAsyncShouldAssignRoleClaim(bool isAdmin)
+ {
+ var user = SetupUser(isAdmin);
+ var authenticateResult = await _sut.AuthenticateAsync();
+
+ var expectedRole = user.Policy.IsAdministrator ? UserRoles.Administrator : UserRoles.User;
+ Assert.True(authenticateResult.Principal.HasClaim(ClaimTypes.Role, expectedRole));
+ }
+
+ [Fact]
+ public async Task HandleAuthenticateAsyncShouldAssignTicketCorrectScheme()
+ {
+ SetupUser();
+ var authenticatedResult = await _sut.AuthenticateAsync();
+
+ Assert.Equal(_scheme.Name, authenticatedResult.Ticket.AuthenticationScheme);
+ }
+
+ private User SetupUser(bool isAdmin = false)
+ {
+ var user = _fixture.Create<User>();
+ user.Policy.IsAdministrator = isAdmin;
+
+ _jellyfinAuthServiceMock.Setup(
+ a => a.Authenticate(
+ It.IsAny<HttpRequest>(),
+ It.IsAny<AuthenticatedAttribute>()))
+ .Returns(user);
+
+ return user;
+ }
+
+ private void AllowFixtureCircularDependencies()
+ {
+ // A circular dependency exists in the User entity around parent folders,
+ // this allows Autofixture to generate a User regardless, rather than throw
+ // an error.
+ _fixture.Behaviors.OfType<ThrowingRecursionBehavior>().ToList()
+ .ForEach(b => _fixture.Behaviors.Remove(b));
+ _fixture.Behaviors.Add(new OmitOnRecursionBehavior());
+ }
+ }
+}
diff --git a/tests/Jellyfin.Api.Tests/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandlerTests.cs b/tests/Jellyfin.Api.Tests/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandlerTests.cs
new file mode 100644
index 000000000..84cdbe360
--- /dev/null
+++ b/tests/Jellyfin.Api.Tests/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandlerTests.cs
@@ -0,0 +1,77 @@
+using System.Collections.Generic;
+using System.Security.Claims;
+using System.Threading.Tasks;
+using AutoFixture;
+using AutoFixture.AutoMoq;
+using Jellyfin.Api.Auth.FirstTimeSetupOrElevatedPolicy;
+using Jellyfin.Api.Constants;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Model.Configuration;
+using Microsoft.AspNetCore.Authorization;
+using Moq;
+using Xunit;
+
+namespace Jellyfin.Api.Tests.Auth.FirstTimeSetupOrElevatedPolicy
+{
+ public class FirstTimeSetupOrElevatedHandlerTests
+ {
+ private readonly Mock<IConfigurationManager> _configurationManagerMock;
+ private readonly List<IAuthorizationRequirement> _requirements;
+ private readonly FirstTimeSetupOrElevatedHandler _sut;
+
+ public FirstTimeSetupOrElevatedHandlerTests()
+ {
+ var fixture = new Fixture().Customize(new AutoMoqCustomization());
+ _configurationManagerMock = fixture.Freeze<Mock<IConfigurationManager>>();
+ _requirements = new List<IAuthorizationRequirement> {new FirstTimeSetupOrElevatedRequirement()};
+
+ _sut = fixture.Create<FirstTimeSetupOrElevatedHandler>();
+ }
+
+ [Theory]
+ [InlineData(UserRoles.Administrator)]
+ [InlineData(UserRoles.Guest)]
+ [InlineData(UserRoles.User)]
+ public async Task ShouldSucceedIfStartupWizardIncomplete(string userRole)
+ {
+ SetupConfigurationManager(false);
+ var user = SetupUser(userRole);
+ var context = new AuthorizationHandlerContext(_requirements, user, null);
+
+ await _sut.HandleAsync(context);
+ Assert.True(context.HasSucceeded);
+ }
+
+ [Theory]
+ [InlineData(UserRoles.Administrator, true)]
+ [InlineData(UserRoles.Guest, false)]
+ [InlineData(UserRoles.User, false)]
+ public async Task ShouldRequireAdministratorIfStartupWizardComplete(string userRole, bool shouldSucceed)
+ {
+ SetupConfigurationManager(true);
+ var user = SetupUser(userRole);
+ var context = new AuthorizationHandlerContext(_requirements, user, null);
+
+ await _sut.HandleAsync(context);
+ Assert.Equal(shouldSucceed, context.HasSucceeded);
+ }
+
+ private static ClaimsPrincipal SetupUser(string role)
+ {
+ var claims = new[] {new Claim(ClaimTypes.Role, role)};
+ var identity = new ClaimsIdentity(claims);
+ return new ClaimsPrincipal(identity);
+ }
+
+ private void SetupConfigurationManager(bool startupWizardCompleted)
+ {
+ var commonConfiguration = new BaseApplicationConfiguration
+ {
+ IsStartupWizardCompleted = startupWizardCompleted
+ };
+
+ _configurationManagerMock.Setup(c => c.CommonConfiguration)
+ .Returns(commonConfiguration);
+ }
+ }
+}
diff --git a/tests/Jellyfin.Api.Tests/Auth/RequiresElevationPolicy/RequiresElevationHandlerTests.cs b/tests/Jellyfin.Api.Tests/Auth/RequiresElevationPolicy/RequiresElevationHandlerTests.cs
new file mode 100644
index 000000000..e2beea1ad
--- /dev/null
+++ b/tests/Jellyfin.Api.Tests/Auth/RequiresElevationPolicy/RequiresElevationHandlerTests.cs
@@ -0,0 +1,38 @@
+using System.Collections.Generic;
+using System.Security.Claims;
+using System.Threading.Tasks;
+using Jellyfin.Api.Auth.RequiresElevationPolicy;
+using Jellyfin.Api.Constants;
+using Microsoft.AspNetCore.Authorization;
+using Xunit;
+
+namespace Jellyfin.Api.Tests.Auth.RequiresElevationPolicy
+{
+ public class RequiresElevationHandlerTests
+ {
+ private readonly RequiresElevationHandler _sut;
+
+ public RequiresElevationHandlerTests()
+ {
+ _sut = new RequiresElevationHandler();
+ }
+
+ [Theory]
+ [InlineData(UserRoles.Administrator, true)]
+ [InlineData(UserRoles.User, false)]
+ [InlineData(UserRoles.Guest, false)]
+ public async Task ShouldHandleRolesCorrectly(string role, bool shouldSucceed)
+ {
+ var requirements = new List<IAuthorizationRequirement> {new RequiresElevationRequirement()};
+
+ var claims = new[] {new Claim(ClaimTypes.Role, role)};
+ var identity = new ClaimsIdentity(claims);
+ var user = new ClaimsPrincipal(identity);
+
+ var context = new AuthorizationHandlerContext(requirements, user, null);
+
+ await _sut.HandleAsync(context);
+ Assert.Equal(shouldSucceed, context.HasSucceeded);
+ }
+ }
+}
diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
index 1671b8d79..1d7e4f7af 100644
--- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
+++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
@@ -1,20 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>netcoreapp3.0</TargetFramework>
+ <TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
+ <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+ <Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
+ <PackageReference Include="AutoFixture" Version="4.11.0" />
+ <PackageReference Include="AutoFixture.AutoMoq" Version="4.11.0" />
+ <PackageReference Include="AutoFixture.Xunit2" Version="4.11.0" />
+ <PackageReference Include="Microsoft.Extensions.Options" Version="3.1.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
- <PackageReference Include="coverlet.collector" Version="1.1.0" />
+ <PackageReference Include="coverlet.collector" Version="1.2.0" />
<PackageReference Include="Moq" Version="4.13.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../../MediaBrowser.Api/MediaBrowser.Api.csproj" />
+ <ProjectReference Include="../../Jellyfin.Api/Jellyfin.Api.csproj" />
</ItemGroup>
</Project>
diff --git a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj
index bc0114d1e..86bb11bd4 100644
--- a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj
+++ b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj
@@ -3,13 +3,15 @@
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
+ <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+ <Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
- <PackageReference Include="coverlet.collector" Version="1.1.0" />
+ <PackageReference Include="coverlet.collector" Version="1.2.0" />
</ItemGroup>
<ItemGroup>
diff --git a/tests/Jellyfin.Controller.Tests/AlphanumComparatorTests.cs b/tests/Jellyfin.Controller.Tests/AlphanumComparatorTests.cs
new file mode 100644
index 000000000..929bb92aa
--- /dev/null
+++ b/tests/Jellyfin.Controller.Tests/AlphanumComparatorTests.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Linq;
+using MediaBrowser.Common.Extensions;
+using MediaBrowser.Controller.Sorting;
+using Xunit;
+
+namespace Jellyfin.Controller.Tests
+{
+ public class AlphanumComparatorTests
+ {
+ private readonly Random _rng = new Random(42);
+
+ // InlineData is pre-sorted
+ [Theory]
+ [InlineData(null, "", "1", "9", "10", "a", "z")]
+ [InlineData("50F", "100F", "SR9", "SR100")]
+ [InlineData("image-1.jpg", "image-02.jpg", "image-4.jpg", "image-9.jpg", "image-10.jpg", "image-11.jpg", "image-22.jpg")]
+ [InlineData("Hard drive 2GB", "Hard drive 20GB")]
+ [InlineData("b", "e", "è", "ě", "f", "g", "k")]
+ [InlineData("123456789", "123456789a", "abc", "abcd")]
+ [InlineData("12345678912345678912345678913234567891", "123456789123456789123456789132345678912")]
+ [InlineData("12345678912345678912345678913234567891", "12345678912345678912345678913234567891")]
+ [InlineData("12345678912345678912345678913234567891", "12345678912345678912345678913234567892")]
+ [InlineData("12345678912345678912345678913234567891a", "12345678912345678912345678913234567891a")]
+ [InlineData("12345678912345678912345678913234567891a", "12345678912345678912345678913234567891b")]
+ public void AlphanumComparatorTest(params string?[] strings)
+ {
+ var copy = (string?[])strings.Clone();
+ if (strings.Length == 2)
+ {
+ var tmp = copy[0];
+ copy[0] = copy[1];
+ copy[1] = tmp;
+ }
+ else
+ {
+ copy.Shuffle(_rng);
+ }
+
+ Array.Sort(copy, new AlphanumComparator());
+ Assert.True(strings.SequenceEqual(copy));
+ }
+ }
+}
diff --git a/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj b/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj
new file mode 100644
index 000000000..c63f2e8c6
--- /dev/null
+++ b/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj
@@ -0,0 +1,21 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFramework>netcoreapp3.1</TargetFramework>
+ <IsPackable>false</IsPackable>
+ <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+ <Nullable>enable</Nullable>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
+ <PackageReference Include="xunit" Version="2.4.1" />
+ <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
+ <PackageReference Include="coverlet.collector" Version="1.2.0" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="../../MediaBrowser.Controller/MediaBrowser.Controller.csproj" />
+ </ItemGroup>
+
+</Project>
diff --git a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs
index a7848316e..e0f1f236c 100644
--- a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs
+++ b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs
@@ -9,15 +9,15 @@ namespace Jellyfin.MediaEncoding.Tests
{
public class EncoderValidatorTests
{
- private class GetFFmpegVersionTestData : IEnumerable<object[]>
+ private class GetFFmpegVersionTestData : IEnumerable<object?[]>
{
- public IEnumerator<object[]> GetEnumerator()
+ public IEnumerator<object?[]> GetEnumerator()
{
- 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.FFmpegGitUnknownOutput, null };
+ 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.FFmpegGitUnknownOutput, null };
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
@@ -25,7 +25,7 @@ namespace Jellyfin.MediaEncoding.Tests
[Theory]
[ClassData(typeof(GetFFmpegVersionTestData))]
- public void GetFFmpegVersionTest(string versionOutput, Version version)
+ public void GetFFmpegVersionTest(string versionOutput, Version? version)
{
Assert.Equal(version, EncoderValidator.GetFFmpegVersion(versionOutput));
}
diff --git a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs
index 12fde0770..c46c9578b 100644
--- a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs
+++ b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs
@@ -26,7 +26,7 @@ libswscale 5. 5.100 / 5. 5.100
libswresample 3. 5.100 / 3. 5.100
libpostproc 55. 5.100 / 55. 5.100";
- public const string FFmpegV414Output = @"ffmpeg version 4.1.4-1~deb10u1 Copyright (c) 2000-2019 the FFmpeg developers
+ public const string FFmpegV414Output = @"ffmpeg version 4.1.4-1~deb10u1 Copyright (c) 2000-2019 the FFmpeg developers
built with gcc 8 (Raspbian 8.3.0-6+rpi1)
configuration: --prefix=/usr --extra-version='1~deb10u1' --toolchain=hardened --libdir=/usr/lib/arm-linux-gnueabihf --incdir=/usr/include/arm-linux-gnueabihf --arch=arm --enable-gpl --disable-stripping --enable-avresample --disable-filter=resample --enable-avisynth --enable-gnutls --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libcodec2 --enable-libflite --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgme --enable-libgsm --enable-libjack --enable-libmp3lame --enable-libmysofa --enable-libopenjpeg --enable-libopenmpt --enable-libopus --enable-libpulse --enable-librsvg --enable-librubberband --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libssh --enable-libtheora --enable-libtwolame --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx265 --enable-libxml2 --enable-libxvid --enable-libzmq --enable-libzvbi --enable-lv2 --enable-omx --enable-openal --enable-opengl --enable-sdl2 --enable-libdc1394 --enable-libdrm --enable-libiec61883 --enable-chromaprint --enable-frei0r --enable-libx264 --enable-shared
libavutil 56. 22.100 / 56. 22.100
@@ -39,7 +39,7 @@ libswscale 5. 3.100 / 5. 3.100
libswresample 3. 3.100 / 3. 3.100
libpostproc 55. 3.100 / 55. 3.100";
- public const string FFmpegV404Output = @"ffmpeg version 4.0.4 Copyright (c) 2000-2019 the FFmpeg developers
+ public const string FFmpegV404Output = @"ffmpeg version 4.0.4 Copyright (c) 2000-2019 the FFmpeg developers
built with gcc 8 (Debian 8.3.0-6)
configuration: --toolchain=hardened --prefix=/usr --target-os=linux --enable-cross-compile --extra-cflags=--static --enable-gpl --enable-static --disable-doc --disable-ffplay --disable-shared --disable-libxcb --disable-sdl2 --disable-xlib --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 --enable-omx --enable-omx-rpi --enable-version3 --enable-vaapi --enable-vdpau --arch=amd64 --enable-nvenc --enable-nvdec
libavutil 56. 14.100 / 56. 14.100
@@ -51,7 +51,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 FFmpegGitUnknownOutput = @"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
diff --git a/tests/Jellyfin.MediaEncoding.Tests/FFprobeParserTests.cs b/tests/Jellyfin.MediaEncoding.Tests/FFprobeParserTests.cs
new file mode 100644
index 000000000..2032f6cec
--- /dev/null
+++ b/tests/Jellyfin.MediaEncoding.Tests/FFprobeParserTests.cs
@@ -0,0 +1,22 @@
+using System.IO;
+using System.Text.Json;
+using System.Threading.Tasks;
+using MediaBrowser.MediaEncoding.Probing;
+using Xunit;
+
+namespace Jellyfin.MediaEncoding.Tests
+{
+ public class FFprobeParserTests
+ {
+ [Theory]
+ [InlineData("ffprobe1.json")]
+ public async Task Test(string fileName)
+ {
+ var path = Path.Join("Test Data", fileName);
+ using (var stream = File.OpenRead(path))
+ {
+ await JsonSerializer.DeserializeAsync<InternalMediaInfoResult>(stream).ConfigureAwait(false);
+ }
+ }
+ }
+}
diff --git a/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj b/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj
index 7f6b90533..b5e4a1287 100644
--- a/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj
+++ b/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj
@@ -3,13 +3,21 @@
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
+ <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+ <Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
+ <None Include="Test Data\**\*.*">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </None>
+ </ItemGroup>
+
+ <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
- <PackageReference Include="coverlet.collector" Version="1.1.0" />
+ <PackageReference Include="coverlet.collector" Version="1.2.0" />
</ItemGroup>
<ItemGroup>
diff --git a/tests/Jellyfin.MediaEncoding.Tests/Test Data/ffprobe1.json b/tests/Jellyfin.MediaEncoding.Tests/Test Data/ffprobe1.json
new file mode 100644
index 000000000..cdad5df50
--- /dev/null
+++ b/tests/Jellyfin.MediaEncoding.Tests/Test Data/ffprobe1.json
@@ -0,0 +1,105 @@
+{
+ "streams": [
+ {
+ "index": 0,
+ "codec_name": "h264",
+ "codec_long_name": "H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10",
+ "profile": "Main",
+ "codec_type": "video",
+ "codec_time_base": "1/50",
+ "codec_tag_string": "[27][0][0][0]",
+ "codec_tag": "0x001b",
+ "width": 1920,
+ "height": 1080,
+ "coded_width": 1920,
+ "coded_height": 1080,
+ "has_b_frames": 0,
+ "sample_aspect_ratio": "0:1",
+ "display_aspect_ratio": "0:1",
+ "pix_fmt": "yuvj420p",
+ "level": 42,
+ "color_range": "pc",
+ "color_space": "bt709",
+ "color_transfer": "bt709",
+ "color_primaries": "bt709",
+ "chroma_location": "left",
+ "field_order": "progressive",
+ "refs": 1,
+ "is_avc": "false",
+ "nal_length_size": "0",
+ "id": "0x1",
+ "r_frame_rate": "25/1",
+ "avg_frame_rate": "25/1",
+ "time_base": "1/90000",
+ "start_pts": 8570867078,
+ "start_time": "95231.856422",
+ "duration_ts": 31694552,
+ "duration": "352.161689",
+ "bits_per_raw_sample": "8",
+ "disposition": {
+ "default": 0,
+ "dub": 0,
+ "original": 0,
+ "comment": 0,
+ "lyrics": 0,
+ "karaoke": 0,
+ "forced": 0,
+ "hearing_impaired": 0,
+ "visual_impaired": 0,
+ "clean_effects": 0,
+ "attached_pic": 0,
+ "timed_thumbnails": 0
+ }
+ },
+ {
+ "index": 1,
+ "codec_name": "aac",
+ "codec_long_name": "AAC (Advanced Audio Coding)",
+ "profile": "LC",
+ "codec_type": "audio",
+ "codec_time_base": "1/44100",
+ "codec_tag_string": "[15][0][0][0]",
+ "codec_tag": "0x000f",
+ "sample_fmt": "fltp",
+ "sample_rate": "44100",
+ "channels": 2,
+ "channel_layout": "stereo",
+ "bits_per_sample": 0,
+ "id": "0x2",
+ "r_frame_rate": "0/0",
+ "avg_frame_rate": "0/0",
+ "time_base": "1/90000",
+ "start_pts": 8570867697,
+ "start_time": "95231.863300",
+ "duration_ts": 31695687,
+ "duration": "352.174300",
+ "bit_rate": "98191",
+ "disposition": {
+ "default": 0,
+ "dub": 0,
+ "original": 0,
+ "comment": 0,
+ "lyrics": 0,
+ "karaoke": 0,
+ "forced": 0,
+ "hearing_impaired": 0,
+ "visual_impaired": 0,
+ "clean_effects": 0,
+ "attached_pic": 0,
+ "timed_thumbnails": 0
+ }
+ }
+ ],
+ "format": {
+ "filename": "TS Test record.ts",
+ "nb_streams": 2,
+ "nb_programs": 1,
+ "format_name": "mpegts",
+ "format_long_name": "MPEG-TS (MPEG-2 Transport Stream)",
+ "start_time": "95231.856422",
+ "duration": "352.181178",
+ "size": "179003772",
+ "bit_rate": "4066174",
+ "probe_score": 50
+ }
+}
diff --git a/tests/Jellyfin.Naming.Tests/EpisodePathParserTest.cs b/tests/Jellyfin.Naming.Tests/EpisodePathParserTest.cs
deleted file mode 100644
index dd1e04215..000000000
--- a/tests/Jellyfin.Naming.Tests/EpisodePathParserTest.cs
+++ /dev/null
@@ -1,55 +0,0 @@
-using Emby.Naming.Common;
-using Emby.Naming.TV;
-using Xunit;
-
-namespace Jellyfin.Naming.Tests
-{
- public class EpisodePathParserTest
- {
- [Theory]
- [InlineData("/media/Foo/Foo-S01E01", "Foo", 1, 1)]
- [InlineData("/media/Foo - S04E011", "Foo", 4, 11)]
- [InlineData("/media/Foo/Foo s01x01", "Foo", 1, 1)]
- [InlineData("/media/Foo (2019)/Season 4/Foo (2019).S04E03", "Foo (2019)", 4, 3)]
- public void ParseEpisodesCorrectly(string path, string name, int season, int episode)
- {
- NamingOptions o = new NamingOptions();
- EpisodePathParser p = new EpisodePathParser(o);
- var res = p.Parse(path, false);
-
- Assert.True(res.Success);
- Assert.Equal(name, res.SeriesName);
- Assert.Equal(season, res.SeasonNumber);
- Assert.Equal(episode, res.EpisodeNumber);
-
- // testing other paths delimeter
- var res2 = p.Parse(path.Replace('/', '\\'), false);
- Assert.True(res2.Success);
- Assert.Equal(name, res2.SeriesName);
- Assert.Equal(season, res2.SeasonNumber);
- Assert.Equal(episode, res2.EpisodeNumber);
- }
-
- [Theory]
- [InlineData("/media/Foo/Foo 889", "Foo", 889)]
- [InlineData("/media/Foo/[Bar] Foo Baz - 11 [1080p]", "Foo Baz", 11)]
- public void ParseEpisodeWithoutSeason(string path, string name, int episode)
- {
- NamingOptions o = new NamingOptions();
- EpisodePathParser p = new EpisodePathParser(o);
- var res = p.Parse(path, true, fillExtendedInfo: true);
-
- Assert.True(res.Success);
- Assert.Equal(name, res.SeriesName);
- Assert.Null(res.SeasonNumber);
- Assert.Equal(episode, res.EpisodeNumber);
-
- // testing other paths delimeter
- var res2 = p.Parse(path.Replace('/', '\\'), false, fillExtendedInfo: false);
- Assert.True(res2.Success);
- Assert.Equal(name, res2.SeriesName);
- Assert.Null(res2.SeasonNumber);
- Assert.Equal(episode, res2.EpisodeNumber);
- }
- }
-}
diff --git a/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj b/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj
index 79d2f2144..9602d9e58 100644
--- a/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj
+++ b/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj
@@ -3,13 +3,14 @@
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
+ <Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
- <PackageReference Include="coverlet.collector" Version="1.1.0" />
+ <PackageReference Include="coverlet.collector" Version="1.2.0" />
</ItemGroup>
<ItemGroup>
diff --git a/tests/Jellyfin.Naming.Tests/Music/MultiDiscAlbumTests.cs b/tests/Jellyfin.Naming.Tests/Music/MultiDiscAlbumTests.cs
new file mode 100644
index 000000000..9a4b0b542
--- /dev/null
+++ b/tests/Jellyfin.Naming.Tests/Music/MultiDiscAlbumTests.cs
@@ -0,0 +1,66 @@
+using Emby.Naming.Audio;
+using Emby.Naming.Common;
+using Xunit;
+
+namespace Jellyfin.Naming.Tests.Music
+{
+ public class MultiDiscAlbumTests
+ {
+ [Fact]
+ public void TestMultiDiscAlbums()
+ {
+ Assert.False(IsMultiDiscAlbumFolder(@"blah blah"));
+ Assert.False(IsMultiDiscAlbumFolder(@"D:/music/weezer/03 Pinkerton"));
+ Assert.False(IsMultiDiscAlbumFolder(@"D:/music/michael jackson/Bad (2012 Remaster)"));
+
+ Assert.True(IsMultiDiscAlbumFolder(@"cd1"));
+ Assert.True(IsMultiDiscAlbumFolder(@"disc18"));
+ Assert.True(IsMultiDiscAlbumFolder(@"disk10"));
+ Assert.True(IsMultiDiscAlbumFolder(@"vol7"));
+ Assert.True(IsMultiDiscAlbumFolder(@"volume1"));
+
+ Assert.True(IsMultiDiscAlbumFolder(@"cd 1"));
+ Assert.True(IsMultiDiscAlbumFolder(@"disc 1"));
+ Assert.True(IsMultiDiscAlbumFolder(@"disk 1"));
+
+ Assert.False(IsMultiDiscAlbumFolder(@"disk"));
+ Assert.False(IsMultiDiscAlbumFolder(@"disk ·"));
+ Assert.False(IsMultiDiscAlbumFolder(@"disk a"));
+
+ Assert.False(IsMultiDiscAlbumFolder(@"disk volume"));
+ Assert.False(IsMultiDiscAlbumFolder(@"disc disc"));
+ Assert.False(IsMultiDiscAlbumFolder(@"disk disc 6"));
+
+ Assert.True(IsMultiDiscAlbumFolder(@"cd - 1"));
+ Assert.True(IsMultiDiscAlbumFolder(@"disc- 1"));
+ Assert.True(IsMultiDiscAlbumFolder(@"disk - 1"));
+
+ Assert.True(IsMultiDiscAlbumFolder(@"Disc 01 (Hugo Wolf · 24 Lieder)"));
+ Assert.True(IsMultiDiscAlbumFolder(@"Disc 04 (Encores and Folk Songs)"));
+ Assert.True(IsMultiDiscAlbumFolder(@"Disc04 (Encores and Folk Songs)"));
+ Assert.True(IsMultiDiscAlbumFolder(@"Disc 04(Encores and Folk Songs)"));
+ Assert.True(IsMultiDiscAlbumFolder(@"Disc04(Encores and Folk Songs)"));
+
+ Assert.True(IsMultiDiscAlbumFolder(@"D:/Video/MBTestLibrary/VideoTest/music/.38 special/anth/Disc 2"));
+ }
+
+ [Fact]
+ public void TestMultiDiscAlbums1()
+ {
+ Assert.False(IsMultiDiscAlbumFolder(@"[1985] Opportunities (Let's make lots of money) (1985)"));
+ }
+
+ [Fact]
+ public void TestMultiDiscAlbums2()
+ {
+ Assert.False(IsMultiDiscAlbumFolder(@"Blah 04(Encores and Folk Songs)"));
+ }
+
+ private bool IsMultiDiscAlbumFolder(string path)
+ {
+ var parser = new AlbumParser(new NamingOptions());
+
+ return parser.IsMultiPart(path);
+ }
+ }
+}
diff --git a/tests/Jellyfin.Naming.Tests/Subtitles/SubtitleParserTests.cs b/tests/Jellyfin.Naming.Tests/Subtitles/SubtitleParserTests.cs
new file mode 100644
index 000000000..41da889c2
--- /dev/null
+++ b/tests/Jellyfin.Naming.Tests/Subtitles/SubtitleParserTests.cs
@@ -0,0 +1,39 @@
+using Emby.Naming.Common;
+using Emby.Naming.Subtitles;
+using Xunit;
+
+namespace Jellyfin.Naming.Tests.Subtitles
+{
+ public class SubtitleParserTests
+ {
+ private SubtitleParser GetParser()
+ {
+ var options = new NamingOptions();
+
+ return new SubtitleParser(options);
+ }
+
+ [Fact]
+ public void TestSubtitles()
+ {
+ Test("The Skin I Live In (2011).srt", null, false, false);
+ Test("The Skin I Live In (2011).eng.srt", "eng", false, false);
+ Test("The Skin I Live In (2011).eng.default.srt", "eng", true, false);
+ Test("The Skin I Live In (2011).eng.forced.srt", "eng", false, true);
+ Test("The Skin I Live In (2011).eng.foreign.srt", "eng", false, true);
+ Test("The Skin I Live In (2011).eng.default.foreign.srt", "eng", true, true);
+ Test("The Skin I Live In (2011).default.foreign.eng.srt", "eng", true, true);
+ }
+
+ private void Test(string input, string language, bool isDefault, bool isForced)
+ {
+ var parser = GetParser();
+
+ var result = parser.ParseFile(input);
+
+ Assert.Equal(language, result.Language, true);
+ Assert.Equal(isDefault, result.IsDefault);
+ Assert.Equal(isForced, result.IsForced);
+ }
+ }
+}
diff --git a/tests/Jellyfin.Naming.Tests/TV/AbsoluteEpisodeNumberTests.cs b/tests/Jellyfin.Naming.Tests/TV/AbsoluteEpisodeNumberTests.cs
new file mode 100644
index 000000000..553d06681
--- /dev/null
+++ b/tests/Jellyfin.Naming.Tests/TV/AbsoluteEpisodeNumberTests.cs
@@ -0,0 +1,27 @@
+using Emby.Naming.Common;
+using Emby.Naming.TV;
+using Xunit;
+
+namespace Jellyfin.Naming.Tests.TV
+{
+ public class AbsoluteEpisodeNumberTests
+ {
+ [Theory]
+ [InlineData("The Simpsons/12.avi", 12)]
+ [InlineData("The Simpsons/The Simpsons 12.avi", 12)]
+ [InlineData("The Simpsons/The Simpsons 82.avi", 82)]
+ [InlineData("The Simpsons/The Simpsons 112.avi", 112)]
+ [InlineData("The Simpsons/Foo_ep_02.avi", 2)]
+ [InlineData("The Simpsons/The Simpsons 889.avi", 889)]
+ [InlineData("The Simpsons/The Simpsons 101.avi", 101)]
+ public void GetEpisodeNumberFromFileTest(string path, int episodeNumber)
+ {
+ var options = new NamingOptions();
+
+ var result = new EpisodeResolver(options)
+ .Resolve(path, false, null, null, true);
+
+ Assert.Equal(episodeNumber, result.EpisodeNumber);
+ }
+ }
+}
diff --git a/tests/Jellyfin.Naming.Tests/TV/DailyEpisodeTests.cs b/tests/Jellyfin.Naming.Tests/TV/DailyEpisodeTests.cs
new file mode 100644
index 000000000..6ecffe80b
--- /dev/null
+++ b/tests/Jellyfin.Naming.Tests/TV/DailyEpisodeTests.cs
@@ -0,0 +1,34 @@
+using Emby.Naming.Common;
+using Emby.Naming.TV;
+using Xunit;
+
+namespace Jellyfin.Naming.Tests.TV
+{
+ public class DailyEpisodeTests
+ {
+
+
+ [Theory]
+ [InlineData(@"/server/anything_1996.11.14.mp4", "anything", 1996, 11, 14)]
+ [InlineData(@"/server/anything_1996-11-14.mp4", "anything", 1996, 11, 14)]
+ [InlineData(@"/server/james.corden.2017.04.20.anne.hathaway.720p.hdtv.x264-crooks.mkv", "james.corden", 2017, 04, 20)]
+ [InlineData(@"/server/ABC News 2018_03_24_19_00_00.mkv", "ABC News", 2018, 03, 24)]
+ // TODO: [InlineData(@"/server/anything_14.11.1996.mp4", "anything", 1996, 11, 14)]
+ // TODO: [InlineData(@"/server/A Daily Show - (2015-01-15) - Episode Name - [720p].mkv", "A Daily Show", 2015, 01, 15)]
+ // TODO: [InlineData(@"/server/Last Man Standing_KTLADT_2018_05_25_01_28_00.wtv", "Last Man Standing", 2018, 05, 25)]
+ public void Test(string path, string seriesName, int? year, int? month, int? day)
+ {
+ var options = new NamingOptions();
+
+ var result = new EpisodeResolver(options)
+ .Resolve(path, false);
+
+ Assert.Null(result.SeasonNumber);
+ Assert.Null(result.EpisodeNumber);
+ Assert.Equal(year, result.Year);
+ Assert.Equal(month, result.Month);
+ Assert.Equal(day, result.Day);
+ Assert.Equal(seriesName, result.SeriesName, true);
+ }
+ }
+}
diff --git a/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs b/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs
new file mode 100644
index 000000000..5e023bdb0
--- /dev/null
+++ b/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs
@@ -0,0 +1,83 @@
+using Emby.Naming.Common;
+using Emby.Naming.TV;
+using Xunit;
+
+namespace Jellyfin.Naming.Tests.TV
+{
+ public class EpisodeNumberTests
+ {
+ private readonly NamingOptions _namingOptions = new NamingOptions();
+
+ [Theory]
+ [InlineData("Watchmen (2019)/Watchmen 1x03 [WEBDL-720p][EAC3 5.1][h264][-TBS] - She Was Killed by Space Junk.mkv", 3)]
+ [InlineData("The Daily Show/The Daily Show 25x22 - [WEBDL-720p][AAC 2.0][x264] Noah Baumbach-TBS.mkv", 22)]
+ [InlineData("Castle Rock 2x01 Que el rio siga su curso [WEB-DL HULU 1080p h264 Dual DD5.1 Subs].mkv", 1)]
+ [InlineData("After Life 1x06 Episodio 6 [WEB-DL NF 1080p h264 Dual DD 5.1 Sub].mkv", 6)]
+ [InlineData("Season 02/S02E03 blah.avi", 3)]
+ [InlineData("Season 2/02x03 - 02x04 - 02x15 - Ep Name.mp4", 3)]
+ [InlineData("Season 02/02x03 - x04 - x15 - Ep Name.mp4", 3)]
+ [InlineData("Season 1/01x02 blah.avi", 2)]
+ [InlineData("Season 1/S01x02 blah.avi", 2)]
+ [InlineData("Season 1/S01E02 blah.avi", 2)]
+ [InlineData("Season 2/Elementary - 02x03-04-15 - Ep Name.mp4", 3)]
+ [InlineData("Season 1/S01xE02 blah.avi", 2)]
+ [InlineData("Season 1/seriesname S01E02 blah.avi", 2)]
+ [InlineData("Season 2/Episode - 16.avi", 16)]
+ [InlineData("Season 2/Episode 16.avi", 16)]
+ [InlineData("Season 2/Episode 16 - Some Title.avi", 16)]
+ [InlineData("Season 2/16 Some Title.avi", 16)]
+ [InlineData("Season 2/16 - 12 Some Title.avi", 16)]
+ [InlineData("Season 2/7 - 12 Angry Men.avi", 7)]
+ [InlineData("Season 1/seriesname 01x02 blah.avi", 2)]
+ [InlineData("Season 25/The Simpsons.S25E09.Steal this episode.mp4", 9)]
+ [InlineData("Season 1/seriesname S01x02 blah.avi", 2)]
+ [InlineData("Season 2/Elementary - 02x03 - 02x04 - 02x15 - Ep Name.mp4", 3)]
+ [InlineData("Season 1/seriesname S01xE02 blah.avi", 2)]
+ [InlineData("Season 02/Elementary - 02x03 - x04 - x15 - Ep Name.mp4", 3)]
+ [InlineData("Season 02/Elementary - 02x03x04x15 - Ep Name.mp4", 3)]
+ [InlineData("Season 2/02x03-04-15 - Ep Name.mp4", 3)]
+ [InlineData("Season 02/02x03-E15 - Ep Name.mp4", 3)]
+ [InlineData("Season 02/Elementary - 02x03-E15 - Ep Name.mp4", 3)]
+ [InlineData("Season 1/Elementary - S01E23-E24-E26 - The Woman.mp4", 23)]
+ [InlineData("Season 2009/S2009E23-E24-E26 - The Woman.mp4", 23)]
+ [InlineData("Season 2009/2009x02 blah.avi", 2)]
+ [InlineData("Season 2009/S2009x02 blah.avi", 2)]
+ [InlineData("Season 2009/S2009E02 blah.avi", 2)]
+ [InlineData("Season 2009/seriesname 2009x02 blah.avi", 2)]
+ [InlineData("Season 2009/Elementary - 2009x03x04x15 - Ep Name.mp4", 3)]
+ [InlineData("Season 2009/2009x03x04x15 - Ep Name.mp4", 3)]
+ [InlineData("Season 2009/Elementary - 2009x03-E15 - Ep Name.mp4", 3)]
+ [InlineData("Season 2009/S2009xE02 blah.avi", 2)]
+ [InlineData("Season 2009/Elementary - S2009E23-E24-E26 - The Woman.mp4", 23)]
+ [InlineData("Season 2009/seriesname S2009xE02 blah.avi", 2)]
+ [InlineData("Season 2009/2009x03-E15 - Ep Name.mp4", 3)]
+ [InlineData("Season 2009/seriesname S2009E02 blah.avi", 2)]
+ [InlineData("Season 2009/2009x03 - 2009x04 - 2009x15 - Ep Name.mp4", 3)]
+ [InlineData("Season 2009/2009x03 - x04 - x15 - Ep Name.mp4", 3)]
+ [InlineData("Season 2009/seriesname S2009x02 blah.avi", 2)]
+ [InlineData("Season 2009/Elementary - 2009x03 - 2009x04 - 2009x15 - Ep Name.mp4", 3)]
+ [InlineData("Season 2009/Elementary - 2009x03-04-15 - Ep Name.mp4", 3)]
+ [InlineData("Season 2009/2009x03-04-15 - Ep Name.mp4", 3)]
+ [InlineData("Season 2009/Elementary - 2009x03 - x04 - x15 - Ep Name.mp4", 3)]
+ [InlineData("Season 1/02 - blah-02 a.avi", 2)]
+ [InlineData("Season 1/02 - blah.avi", 2)]
+ [InlineData("Season 2/02 - blah 14 blah.avi", 2)]
+ [InlineData("Season 2/02.avi", 2)]
+ [InlineData("Season 2/2. Infestation.avi", 2)]
+ [InlineData("The Wonder Years/The.Wonder.Years.S04.PDTV.x264-JCH/The Wonder Years s04e07 Christmas Party NTSC PDTV.avi", 7)]
+ [InlineData("Running Man/Running Man S2017E368.mkv", 368)]
+ // TODO: [InlineData("Season 2/16 12 Some Title.avi", 16)]
+ // TODO: [InlineData("/The.Legend.of.Condor.Heroes.2017.V2.web-dl.1080p.h264.aac-hdctv/The.Legend.of.Condor.Heroes.2017.E07.V2.web-dl.1080p.h264.aac-hdctv.mkv", 7)]
+ // TODO: [InlineData("Season 4/Uchuu.Senkan.Yamato.2199.E03.avi", 3)]
+ // TODO: [InlineData("Season 2/7 12 Angry Men.avi", 7)]
+ // TODO: [InlineData("Season 02/02x03x04x15 - Ep Name.mp4", 2)]
+ // TODO: [InlineData("Season 2/[HorribleSubs] Hunter X Hunter - 136 [720p].mkv", 136)]
+ public void GetEpisodeNumberFromFileTest(string path, int? expected)
+ {
+ var result = new EpisodePathParser(_namingOptions)
+ .Parse(path, false);
+
+ Assert.Equal(expected, result.EpisodeNumber);
+ }
+ }
+}
diff --git a/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberWithoutSeasonTests.cs b/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberWithoutSeasonTests.cs
new file mode 100644
index 000000000..0c7d9520e
--- /dev/null
+++ b/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberWithoutSeasonTests.cs
@@ -0,0 +1,36 @@
+using Emby.Naming.Common;
+using Emby.Naming.TV;
+using Xunit;
+
+namespace Jellyfin.Naming.Tests.TV
+{
+ public class EpisodeNumberWithoutSeasonTests
+ {
+
+ [Theory]
+ [InlineData(8, @"The Simpsons/The Simpsons.S25E08.Steal this episode.mp4")]
+ [InlineData(2, @"The Simpsons/The Simpsons - 02 - Ep Name.avi")]
+ [InlineData(2, @"The Simpsons/02.avi")]
+ [InlineData(2, @"The Simpsons/02 - Ep Name.avi")]
+ [InlineData(2, @"The Simpsons/02-Ep Name.avi")]
+ [InlineData(2, @"The Simpsons/02.EpName.avi")]
+ [InlineData(2, @"The Simpsons/The Simpsons - 02.avi")]
+ [InlineData(2, @"The Simpsons/The Simpsons - 02 Ep Name.avi")]
+ [InlineData(7, @"GJ Club (2013)/GJ Club - 07.mkv")]
+ [InlineData(17, @"Case Closed (1996-2007)/Case Closed - 317.mkv")]
+ // TODO: [InlineData(2, @"The Simpsons/The Simpsons 5 - 02 - Ep Name.avi")]
+ // TODO: [InlineData(2, @"The Simpsons/The Simpsons 5 - 02 Ep Name.avi")]
+ // TODO: [InlineData(7, @"Seinfeld/Seinfeld 0807 The Checks.avi")]
+ // This is not supported anymore after removing the episode number 365+ hack from EpisodePathParser
+ // TODO: [InlineData(13, @"Case Closed (1996-2007)/Case Closed - 13.mkv")]
+ public void GetEpisodeNumberFromFileTest(int episodeNumber, string path)
+ {
+ var options = new NamingOptions();
+
+ var result = new EpisodeResolver(options)
+ .Resolve(path, false);
+
+ Assert.Equal(episodeNumber, result.EpisodeNumber);
+ }
+ }
+}
diff --git a/tests/Jellyfin.Naming.Tests/TV/EpisodePathParserTest.cs b/tests/Jellyfin.Naming.Tests/TV/EpisodePathParserTest.cs
new file mode 100644
index 000000000..4b5606715
--- /dev/null
+++ b/tests/Jellyfin.Naming.Tests/TV/EpisodePathParserTest.cs
@@ -0,0 +1,50 @@
+using Emby.Naming.Common;
+using Emby.Naming.TV;
+using Xunit;
+
+namespace Jellyfin.Naming.Tests.TV
+{
+ public class EpisodePathParserTest
+ {
+ [Theory]
+ [InlineData("/media/Foo/Foo-S01E01", "Foo", 1, 1)]
+ [InlineData("/media/Foo - S04E011", "Foo", 4, 11)]
+ [InlineData("/media/Foo/Foo s01x01", "Foo", 1, 1)]
+ [InlineData("/media/Foo (2019)/Season 4/Foo (2019).S04E03", "Foo (2019)", 4, 3)]
+ [InlineData("D:\\media\\Foo\\Foo-S01E01", "Foo", 1, 1)]
+ [InlineData("D:\\media\\Foo - S04E011", "Foo", 4, 11)]
+ [InlineData("D:\\media\\Foo\\Foo s01x01", "Foo", 1, 1)]
+ [InlineData("D:\\media\\Foo (2019)\\Season 4\\Foo (2019).S04E03", "Foo (2019)", 4, 3)]
+ [InlineData("/Season 2/Elementary - 02x03-04-15 - Ep Name.mp4", "Elementary", 2, 3)]
+ [InlineData("/Season 1/seriesname S01E02 blah.avi", "seriesname", 1, 2)]
+ [InlineData("/Running Man/Running Man S2017E368.mkv", "Running Man", 2017, 368)]
+ [InlineData("/Season 1/seriesname 01x02 blah.avi", "seriesname", 1, 2)]
+ [InlineData("/Season 25/The Simpsons.S25E09.Steal this episode.mp4", "The Simpsons", 25, 9)]
+ [InlineData("/Season 1/seriesname S01x02 blah.avi", "seriesname", 1, 2)]
+ [InlineData("/Season 2/Elementary - 02x03 - 02x04 - 02x15 - Ep Name.mp4", "Elementary", 2, 3)]
+ [InlineData("/Season 1/seriesname S01xE02 blah.avi", "seriesname", 1, 2)]
+ [InlineData("/Season 02/Elementary - 02x03 - x04 - x15 - Ep Name.mp4", "Elementary", 2, 3)]
+ [InlineData("/Season 02/Elementary - 02x03x04x15 - Ep Name.mp4", "Elementary", 2, 3)]
+ [InlineData("/Season 02/Elementary - 02x03-E15 - Ep Name.mp4", "Elementary", 2, 3)]
+ [InlineData("/Season 1/Elementary - S01E23-E24-E26 - The Woman.mp4", "Elementary", 1, 23)]
+ [InlineData("/The Wonder Years/The.Wonder.Years.S04.PDTV.x264-JCH/The Wonder Years s04e07 Christmas Party NTSC PDTV.avi", "The Wonder Years", 4, 7)]
+ // TODO: [InlineData("/Castle Rock 2x01 Que el rio siga su curso [WEB-DL HULU 1080p h264 Dual DD5.1 Subs].mkv", "Castle Rock", 2, 1)]
+ // TODO: [InlineData("/After Life 1x06 Episodio 6 [WEB-DL NF 1080p h264 Dual DD 5.1 Sub].mkv", "After Life", 1, 6)]
+ // TODO: [InlineData("/Season 4/Uchuu.Senkan.Yamato.2199.E03.avi", "Uchuu Senkan Yamoto 2199", 4, 3)]
+ // TODO: [InlineData("The Daily Show/The Daily Show 25x22 - [WEBDL-720p][AAC 2.0][x264] Noah Baumbach-TBS.mkv", "The Daily Show", 25, 22)]
+ // TODO: [InlineData("Watchmen (2019)/Watchmen 1x03 [WEBDL-720p][EAC3 5.1][h264][-TBS] - She Was Killed by Space Junk.mkv", "Watchmen (2019)", 1, 3)]
+ // TODO: [InlineData("/The.Legend.of.Condor.Heroes.2017.V2.web-dl.1080p.h264.aac-hdctv/The.Legend.of.Condor.Heroes.2017.E07.V2.web-dl.1080p.h264.aac-hdctv.mkv", "The Legend of Condor Heroes 2017", 1, 7)]
+ public void ParseEpisodesCorrectly(string path, string name, int season, int episode)
+
+ {
+ NamingOptions o = new NamingOptions();
+ EpisodePathParser p = new EpisodePathParser(o);
+ var res = p.Parse(path, false);
+
+ Assert.True(res.Success);
+ Assert.Equal(name, res.SeriesName);
+ Assert.Equal(season, res.SeasonNumber);
+ Assert.Equal(episode, res.EpisodeNumber);
+ }
+ }
+}
diff --git a/tests/Jellyfin.Naming.Tests/TV/EpisodeWithoutSeasonTests.cs b/tests/Jellyfin.Naming.Tests/TV/EpisodeWithoutSeasonTests.cs
new file mode 100644
index 000000000..364eb7ff8
--- /dev/null
+++ b/tests/Jellyfin.Naming.Tests/TV/EpisodeWithoutSeasonTests.cs
@@ -0,0 +1,27 @@
+using Emby.Naming.Common;
+using Emby.Naming.TV;
+using Xunit;
+
+namespace Jellyfin.Naming.Tests.TV
+{
+ public class EpisodeWithoutSeasonTests
+ {
+ // TODO: [Theory]
+ // TODO: [InlineData(@"/server/anything_ep02.mp4", "anything", null, 2)]
+ // TODO: [InlineData(@"/server/anything_ep_02.mp4", "anything", null, 2)]
+ // TODO: [InlineData(@"/server/anything_part.II.mp4", "anything", null, null)]
+ // TODO: [InlineData(@"/server/anything_pt.II.mp4", "anything", null, null)]
+ // TODO: [InlineData(@"/server/anything_pt_II.mp4", "anything", null, null)]
+ public void Test(string path, string seriesName, int? seasonNumber, int? episodeNumber)
+ {
+ var options = new NamingOptions();
+
+ var result = new EpisodeResolver(options)
+ .Resolve(path, false);
+
+ Assert.Equal(seasonNumber, result.SeasonNumber);
+ Assert.Equal(episodeNumber, result.EpisodeNumber);
+ Assert.Equal(seriesName, result.SeriesName, true);
+ }
+ }
+}
diff --git a/tests/Jellyfin.Naming.Tests/TV/MultiEpisodeTests.cs b/tests/Jellyfin.Naming.Tests/TV/MultiEpisodeTests.cs
new file mode 100644
index 000000000..3513050b6
--- /dev/null
+++ b/tests/Jellyfin.Naming.Tests/TV/MultiEpisodeTests.cs
@@ -0,0 +1,80 @@
+using Emby.Naming.Common;
+using Emby.Naming.TV;
+using Xunit;
+
+namespace Jellyfin.Naming.Tests.TV
+{
+ public class MultiEpisodeTests
+ {
+ [Theory]
+ [InlineData(@"Season 1/4x01 – 20 Hours in America (1).mkv", null)]
+ [InlineData(@"Season 1/01x02 blah.avi", null)]
+ [InlineData(@"Season 1/S01x02 blah.avi", null)]
+ [InlineData(@"Season 1/S01E02 blah.avi", null)]
+ [InlineData(@"Season 1/S01xE02 blah.avi", null)]
+ [InlineData(@"Season 1/seriesname 01x02 blah.avi", null)]
+ [InlineData(@"Season 1/seriesname S01x02 blah.avi", null)]
+ [InlineData(@"Season 1/seriesname S01E02 blah.avi", null)]
+ [InlineData(@"Season 1/seriesname S01xE02 blah.avi", null)]
+ [InlineData(@"Season 2/02x03 - 04 Ep Name.mp4", null)]
+ [InlineData(@"Season 2/My show name 02x03 - 04 Ep Name.mp4", null)]
+ [InlineData(@"Season 2/Elementary - 02x03 - 02x04 - 02x15 - Ep Name.mp4", 15)]
+ [InlineData(@"Season 2/02x03 - 02x04 - 02x15 - Ep Name.mp4", 15)]
+ [InlineData(@"Season 2/02x03-04-15 - Ep Name.mp4", 15)]
+ [InlineData(@"Season 2/Elementary - 02x03-04-15 - Ep Name.mp4", 15)]
+ [InlineData(@"Season 02/02x03-E15 - Ep Name.mp4", 15)]
+ [InlineData(@"Season 02/Elementary - 02x03-E15 - Ep Name.mp4", 15)]
+ [InlineData(@"Season 02/02x03 - x04 - x15 - Ep Name.mp4", 15)]
+ [InlineData(@"Season 02/Elementary - 02x03 - x04 - x15 - Ep Name.mp4", 15)]
+ [InlineData(@"Season 02/02x03x04x15 - Ep Name.mp4", 15)]
+ [InlineData(@"Season 02/Elementary - 02x03x04x15 - Ep Name.mp4", 15)]
+ [InlineData(@"Season 1/Elementary - S01E23-E24-E26 - The Woman.mp4", 26)]
+ [InlineData(@"Season 1/S01E23-E24-E26 - The Woman.mp4", 26)]
+ // Four Digits seasons
+ [InlineData(@"Season 2009/2009x02 blah.avi", null)]
+ [InlineData(@"Season 2009/S2009x02 blah.avi", null)]
+ [InlineData(@"Season 2009/S2009E02 blah.avi", null)]
+ [InlineData(@"Season 2009/S2009xE02 blah.avi", null)]
+ [InlineData(@"Season 2009/seriesname 2009x02 blah.avi", null)]
+ [InlineData(@"Season 2009/seriesname S2009x02 blah.avi", null)]
+ [InlineData(@"Season 2009/seriesname S2009E02 blah.avi", null)]
+ [InlineData(@"Season 2009/seriesname S2009xE02 blah.avi", null)]
+ [InlineData(@"Season 2009/Elementary - 2009x03 - 2009x04 - 2009x15 - Ep Name.mp4", 15)]
+ [InlineData(@"Season 2009/2009x03 - 2009x04 - 2009x15 - Ep Name.mp4", 15)]
+ [InlineData(@"Season 2009/2009x03-04-15 - Ep Name.mp4", 15)]
+ [InlineData(@"Season 2009/Elementary - 2009x03-04-15 - Ep Name.mp4", 15)]
+ [InlineData(@"Season 2009/2009x03-E15 - Ep Name.mp4", 15)]
+ [InlineData(@"Season 2009/Elementary - 2009x03-E15 - Ep Name.mp4", 15)]
+ [InlineData(@"Season 2009/2009x03 - x04 - x15 - Ep Name.mp4", 15)]
+ [InlineData(@"Season 2009/Elementary - 2009x03 - x04 - x15 - Ep Name.mp4", 15)]
+ [InlineData(@"Season 2009/2009x03x04x15 - Ep Name.mp4", 15)]
+ [InlineData(@"Season 2009/Elementary - 2009x03x04x15 - Ep Name.mp4", 15)]
+ [InlineData(@"Season 2009/Elementary - S2009E23-E24-E26 - The Woman.mp4", 26)]
+ [InlineData(@"Season 2009/S2009E23-E24-E26 - The Woman.mp4", 26)]
+ // Without season number
+ [InlineData(@"Season 1/02 - blah.avi", null)]
+ [InlineData(@"Season 2/02 - blah 14 blah.avi", null)]
+ [InlineData(@"Season 1/02 - blah-02 a.avi", null)]
+ [InlineData(@"Season 2/02.avi", null)]
+ [InlineData(@"Season 1/02-03 - blah.avi", 3)]
+ [InlineData(@"Season 2/02-04 - blah 14 blah.avi", 4)]
+ [InlineData(@"Season 1/02-05 - blah-02 a.avi", 5)]
+ [InlineData(@"Season 2/02-04.avi", 4)]
+ [InlineData(@"Season 2 /[HorribleSubs] Hunter X Hunter - 136[720p].mkv", null)]
+ // With format specification that must not be detected as ending episode number
+ [InlineData(@"Season 1/series-s09e14-1080p.mkv", null)]
+ [InlineData(@"Season 1/series-s09e14-720p.mkv", null)]
+ [InlineData(@"Season 1/series-s09e14-720i.mkv", null)]
+ [InlineData(@"Season 1/MOONLIGHTING_s01e01-e04.mkv", 4)]
+ [InlineData(@"Season 1/MOONLIGHTING_s01e01-e04", 4)]
+ public void TestGetEndingEpisodeNumberFromFile(string filename, int? endingEpisodeNumber)
+ {
+ var options = new NamingOptions();
+
+ var result = new EpisodePathParser(options)
+ .Parse(filename, false);
+
+ Assert.Equal(result.EndingEpsiodeNumber, endingEpisodeNumber);
+ }
+ }
+}
diff --git a/tests/Jellyfin.Naming.Tests/TV/SeasonFolderTests.cs b/tests/Jellyfin.Naming.Tests/TV/SeasonFolderTests.cs
new file mode 100644
index 000000000..078f940b2
--- /dev/null
+++ b/tests/Jellyfin.Naming.Tests/TV/SeasonFolderTests.cs
@@ -0,0 +1,31 @@
+using Emby.Naming.TV;
+using Xunit;
+
+namespace Jellyfin.Naming.Tests.TV
+{
+ public class SeasonFolderTests
+ {
+ [Theory]
+ [InlineData(@"/Drive/Season 1", 1)]
+ [InlineData(@"/Drive/Season 2", 2)]
+ [InlineData(@"/Drive/Season 02", 2)]
+ [InlineData(@"/Drive/Seinfeld/S02", 2)]
+ [InlineData(@"/Drive/Seinfeld/2", 2)]
+ [InlineData(@"/Drive/Season 2009", 2009)]
+ [InlineData(@"/Drive/Season1", 1)]
+ [InlineData(@"The Wonder Years/The.Wonder.Years.S04.PDTV.x264-JCH", 4)]
+ [InlineData(@"/Drive/Season 7 (2016)", 7)]
+ [InlineData(@"/Drive/Staffel 7 (2016)", 7)]
+ [InlineData(@"/Drive/Stagione 7 (2016)", 7)]
+ [InlineData(@"/Drive/Season (8)", null)]
+ [InlineData(@"/Drive/3.Staffel", 3)]
+ [InlineData(@"/Drive/s06e05", null)]
+ [InlineData(@"/Drive/The.Legend.of.Condor.Heroes.2017.V2.web-dl.1080p.h264.aac-hdctv", null)]
+ public void GetSeasonNumberFromPathTest(string path, int? seasonNumber)
+ {
+ var result = SeasonPathParser.Parse(path, true, true);
+
+ Assert.Equal(result.SeasonNumber, seasonNumber);
+ }
+ }
+}
diff --git a/tests/Jellyfin.Naming.Tests/TV/SeasonNumberTests.cs b/tests/Jellyfin.Naming.Tests/TV/SeasonNumberTests.cs
new file mode 100644
index 000000000..9eaf897b9
--- /dev/null
+++ b/tests/Jellyfin.Naming.Tests/TV/SeasonNumberTests.cs
@@ -0,0 +1,65 @@
+using Emby.Naming.Common;
+using Emby.Naming.TV;
+using Xunit;
+
+namespace Jellyfin.Naming.Tests.TV
+{
+ public class SeasonNumberTests
+ {
+ private readonly NamingOptions _namingOptions = new NamingOptions();
+
+ [Theory]
+ [InlineData("The Daily Show/The Daily Show 25x22 - [WEBDL-720p][AAC 2.0][x264] Noah Baumbach-TBS.mkv", 25)]
+ [InlineData("/Show/Season 02/S02E03 blah.avi", 2)]
+ [InlineData("Season 1/seriesname S01x02 blah.avi", 1)]
+ [InlineData("Season 1/S01x02 blah.avi", 1)]
+ [InlineData("Season 1/seriesname S01xE02 blah.avi", 1)]
+ [InlineData("Season 1/01x02 blah.avi", 1)]
+ [InlineData("Season 1/S01E02 blah.avi", 1)]
+ [InlineData("Season 1/S01xE02 blah.avi", 1)]
+ [InlineData("Season 1/seriesname 01x02 blah.avi", 1)]
+ [InlineData("Season 1/seriesname S01E02 blah.avi", 1)]
+ [InlineData("Season 2/Elementary - 02x03 - 02x04 - 02x15 - Ep Name.mp4", 2)]
+ [InlineData("Season 2/02x03 - 02x04 - 02x15 - Ep Name.mp4", 2)]
+ [InlineData("Season 2/02x03-04-15 - Ep Name.mp4", 2)]
+ [InlineData("Season 2/Elementary - 02x03-04-15 - Ep Name.mp4", 2)]
+ [InlineData("Season 02/02x03-E15 - Ep Name.mp4", 2)]
+ [InlineData("Season 02/Elementary - 02x03-E15 - Ep Name.mp4", 2)]
+ [InlineData("Season 02/02x03 - x04 - x15 - Ep Name.mp4", 2)]
+ [InlineData("Season 02/Elementary - 02x03 - x04 - x15 - Ep Name.mp4", 2)]
+ [InlineData("Season 02/02x03x04x15 - Ep Name.mp4", 2)]
+ [InlineData("Season 02/Elementary - 02x03x04x15 - Ep Name.mp4", 2)]
+ [InlineData("Season 1/Elementary - S01E23-E24-E26 - The Woman.mp4", 1)]
+ [InlineData("Season 1/S01E23-E24-E26 - The Woman.mp4", 1)]
+ [InlineData("Season 25/The Simpsons.S25E09.Steal this episode.mp4", 25)]
+ [InlineData("The Simpsons/The Simpsons.S25E09.Steal this episode.mp4", 25)]
+ [InlineData("2016/Season s2016e1.mp4", 2016)]
+ [InlineData("2016/Season 2016x1.mp4", 2016)]
+ [InlineData("Season 2009/2009x02 blah.avi", 2009)]
+ [InlineData("Season 2009/S2009x02 blah.avi", 2009)]
+ [InlineData("Season 2009/S2009E02 blah.avi", 2009)]
+ [InlineData("Season 2009/S2009xE02 blah.avi", 2009)]
+ [InlineData("Season 2009/seriesname 2009x02 blah.avi", 2009)]
+ [InlineData("Season 2009/seriesname S2009x02 blah.avi", 2009)]
+ [InlineData("Season 2009/seriesname S2009E02 blah.avi", 2009)]
+ [InlineData("Season 2009/Elementary - 2009x03 - 2009x04 - 2009x15 - Ep Name.mp4", 2009)]
+ [InlineData("Season 2009/2009x03 - 2009x04 - 2009x15 - Ep Name.mp4", 2009)]
+ [InlineData("Season 2009/2009x03-04-15 - Ep Name.mp4", 2009)]
+ [InlineData("Season 2009/Elementary - 2009x03 - x04 - x15 - Ep Name.mp4", 2009)]
+ [InlineData("Season 2009/2009x03x04x15 - Ep Name.mp4", 2009)]
+ [InlineData("Season 2009/Elementary - 2009x03x04x15 - Ep Name.mp4", 2009)]
+ [InlineData("Season 2009/Elementary - S2009E23-E24-E26 - The Woman.mp4", 2009)]
+ [InlineData("Season 2009/S2009E23-E24-E26 - The Woman.mp4", 2009)]
+ [InlineData("Series/1-12 - The Woman.mp4", 1)]
+ [InlineData(@"Running Man/Running Man S2017E368.mkv", 2017)]
+ [InlineData(@"Case Closed (1996-2007)/Case Closed - 317.mkv", 3)]
+ // TODO: [InlineData(@"Seinfeld/Seinfeld 0807 The Checks.avi", 8)]
+ public void GetSeasonNumberFromEpisodeFileTest(string path, int? expected)
+ {
+ var result = new EpisodeResolver(_namingOptions)
+ .Resolve(path, false);
+
+ Assert.Equal(expected, result.SeasonNumber);
+ }
+ }
+}
diff --git a/tests/Jellyfin.Naming.Tests/TV/SimpleEpisodeTests.cs b/tests/Jellyfin.Naming.Tests/TV/SimpleEpisodeTests.cs
new file mode 100644
index 000000000..de253ce37
--- /dev/null
+++ b/tests/Jellyfin.Naming.Tests/TV/SimpleEpisodeTests.cs
@@ -0,0 +1,39 @@
+using Emby.Naming.Common;
+using Emby.Naming.TV;
+using Xunit;
+
+namespace Jellyfin.Naming.Tests.TV
+{
+ public class SimpleEpisodeTests
+ {
+ [Theory]
+ [InlineData("/server/anything_s01e02.mp4", "anything", 1, 2)]
+ [InlineData("/server/anything_s1e2.mp4", "anything", 1, 2)]
+ [InlineData("/server/anything_s01.e02.mp4", "anything", 1, 2)]
+ [InlineData("/server/anything_102.mp4", "anything", 1, 2)]
+ [InlineData("/server/anything_1x02.mp4", "anything", 1, 2)]
+ [InlineData("/server/The Walking Dead 4x01.mp4", "The Walking Dead", 4, 1)]
+ [InlineData("/server/the_simpsons-s02e01_18536.mp4", "the_simpsons", 2, 1)]
+ [InlineData("/server/Temp/S01E02 foo.mp4", "", 1, 2)]
+ [InlineData("Series/4-12 - The Woman.mp4", "", 4, 12)]
+ [InlineData("Series/4x12 - The Woman.mp4", "", 4, 12)]
+ [InlineData("Series/LA X, Pt. 1_s06e32.mp4", "LA X, Pt. 1", 6, 32)]
+ [InlineData("[Baz-Bar]Foo - [1080p][Multiple Subtitle]/[Baz-Bar] Foo - 05 [1080p][Multiple Subtitle].mkv", "Foo", null, 5)]
+ [InlineData(@"/Foo/The.Series.Name.S01E04.WEBRip.x264-Baz[Bar]/the.series.name.s01e04.webrip.x264-Baz[Bar].mkv", "The.Series.Name", 1, 4)]
+ [InlineData(@"Love.Death.and.Robots.S01.1080p.NF.WEB-DL.DDP5.1.x264-NTG/Love.Death.and.Robots.S01E01.Sonnies.Edge.1080p.NF.WEB-DL.DDP5.1.x264-NTG.mkv", "Love.Death.and.Robots", 1, 1)]
+ // TODO: [InlineData("[Baz-Bar]Foo - 01 - 12[1080p][Multiple Subtitle]/[Baz-Bar] Foo - 05 [1080p][Multiple Subtitle].mkv", "Foo", null, 5)]
+ // TODO: [InlineData("E:\\Anime\\Yahari Ore no Seishun Love Comedy wa Machigatteiru\\Yahari Ore no Seishun Love Comedy wa Machigatteiru. Zoku\\Oregairu Zoku 11 - Hayama Hayato Always Renconds to Everyone's Expectations..mkv", "Yahari Ore no Seishun Love Comedy wa Machigatteiru", null, 11)]
+ // TODO: [InlineData(@"/Library/Series/The Grand Tour (2016)/Season 1/S01E01 The Holy Trinity.mkv", "The Grand Tour", 1, 1)]
+ public void Test(string path, string seriesName, int? seasonNumber, int? episodeNumber)
+ {
+ var options = new NamingOptions();
+
+ var result = new EpisodeResolver(options)
+ .Resolve(path, false);
+
+ Assert.Equal(seasonNumber, result.SeasonNumber);
+ Assert.Equal(episodeNumber, result.EpisodeNumber);
+ Assert.Equal(seriesName, result.SeriesName, true);
+ }
+ }
+}
diff --git a/tests/Jellyfin.Naming.Tests/Video/BaseVideoTest.cs b/tests/Jellyfin.Naming.Tests/Video/BaseVideoTest.cs
new file mode 100644
index 000000000..0c2978aca
--- /dev/null
+++ b/tests/Jellyfin.Naming.Tests/Video/BaseVideoTest.cs
@@ -0,0 +1,13 @@
+using Emby.Naming.Common;
+using Emby.Naming.Video;
+
+namespace Jellyfin.Naming.Tests.Video
+{
+ public abstract class BaseVideoTest
+ {
+ private readonly NamingOptions _namingOptions = new NamingOptions();
+
+ protected VideoResolver GetParser()
+ => new VideoResolver(_namingOptions);
+ }
+}
diff --git a/tests/Jellyfin.Naming.Tests/Video/CleanDateTimeTests.cs b/tests/Jellyfin.Naming.Tests/Video/CleanDateTimeTests.cs
new file mode 100644
index 000000000..a2ef2dcd6
--- /dev/null
+++ b/tests/Jellyfin.Naming.Tests/Video/CleanDateTimeTests.cs
@@ -0,0 +1,59 @@
+using System.IO;
+using Emby.Naming.Common;
+using Emby.Naming.Video;
+using Xunit;
+
+namespace Jellyfin.Naming.Tests.Video
+{
+ public sealed class CleanDateTimeTests
+ {
+ private readonly NamingOptions _namingOptions = new NamingOptions();
+
+ [Theory]
+ [InlineData(@"The Wolf of Wall Street (2013).mkv", "The Wolf of Wall Street", 2013)]
+ [InlineData(@"The Wolf of Wall Street 2 (2013).mkv", "The Wolf of Wall Street 2", 2013)]
+ [InlineData(@"The Wolf of Wall Street - 2 (2013).mkv", "The Wolf of Wall Street - 2", 2013)]
+ [InlineData(@"The Wolf of Wall Street 2001 (2013).mkv", "The Wolf of Wall Street 2001", 2013)]
+ [InlineData(@"300 (2006).mkv", "300", 2006)]
+ [InlineData(@"d:/movies/300 (2006).mkv", "300", 2006)]
+ [InlineData(@"300 2 (2006).mkv", "300 2", 2006)]
+ [InlineData(@"300 - 2 (2006).mkv", "300 - 2", 2006)]
+ [InlineData(@"300 2001 (2006).mkv", "300 2001", 2006)]
+ [InlineData(@"curse.of.chucky.2013.stv.unrated.multi.1080p.bluray.x264-rough", "curse.of.chucky", 2013)]
+ [InlineData(@"curse.of.chucky.2013.stv.unrated.multi.2160p.bluray.x264-rough", "curse.of.chucky", 2013)]
+ [InlineData(@"/server/Movies/300 (2007)/300 (2006).bluray.disc", "300", 2006)]
+ [InlineData(@"Arrival.2016.2160p.Blu-Ray.HEVC.mkv", "Arrival", 2016)]
+ [InlineData(@"The Wolf of Wall Street (2013)", "The Wolf of Wall Street", 2013)]
+ [InlineData(@"The Wolf of Wall Street 2 (2013)", "The Wolf of Wall Street 2", 2013)]
+ [InlineData(@"The Wolf of Wall Street - 2 (2013)", "The Wolf of Wall Street - 2", 2013)]
+ [InlineData(@"The Wolf of Wall Street 2001 (2013)", "The Wolf of Wall Street 2001", 2013)]
+ [InlineData(@"300 (2006)", "300", 2006)]
+ [InlineData(@"d:/movies/300 (2006)", "300", 2006)]
+ [InlineData(@"300 2 (2006)", "300 2", 2006)]
+ [InlineData(@"300 - 2 (2006)", "300 - 2", 2006)]
+ [InlineData(@"300 2001 (2006)", "300 2001", 2006)]
+ [InlineData(@"/server/Movies/300 (2007)/300 (2006)", "300", 2006)]
+ [InlineData(@"/server/Movies/300 (2007)/300 (2006).mkv", "300", 2006)]
+ [InlineData(@"American.Psycho.mkv", "American.Psycho.mkv", null)]
+ [InlineData(@"American Psycho.mkv", "American Psycho.mkv", null)]
+ [InlineData(@"[rec].mkv", "[rec].mkv", null)]
+ [InlineData(@"St. Vincent (2014)", "St. Vincent", 2014)]
+ [InlineData("Super movie(2009).mp4", "Super movie", 2009)]
+ // FIXME: [InlineData("Drug War 2013.mp4", "Drug War", 2013)]
+ [InlineData("My Movie (1997) - GreatestReleaseGroup 2019.mp4", "My Movie", 1997)]
+ // FIXME: [InlineData("First Man 2018 1080p.mkv", "First Man", 2018)]
+ [InlineData("First Man (2018) 1080p.mkv", "First Man", 2018)]
+ // FIXME: [InlineData("Maximum Ride - 2016 - WEBDL-1080p - x264 AC3.mkv", "Maximum Ride", 2016)]
+ // FIXME: [InlineData("Robin Hood [Multi-Subs] [2018].mkv", "Robin Hood", 2018)]
+ [InlineData(@"3.Days.to.Kill.2014.720p.BluRay.x264.YIFY.mkv", "3.Days.to.Kill", 2014)] // In this test case, running CleanDateTime first produces no date, so it will attempt to run CleanString first and then CleanDateTime again
+ public void CleanDateTimeTest(string input, string expectedName, int? expectedYear)
+ {
+ input = Path.GetFileName(input);
+
+ var result = new VideoResolver(_namingOptions).CleanDateTime(input);
+
+ Assert.Equal(expectedName, result.Name, true);
+ Assert.Equal(expectedYear, result.Year);
+ }
+ }
+}
diff --git a/tests/Jellyfin.Naming.Tests/Video/CleanStringTests.cs b/tests/Jellyfin.Naming.Tests/Video/CleanStringTests.cs
new file mode 100644
index 000000000..fde06c5a1
--- /dev/null
+++ b/tests/Jellyfin.Naming.Tests/Video/CleanStringTests.cs
@@ -0,0 +1,45 @@
+using System;
+using Emby.Naming.Common;
+using Emby.Naming.Video;
+using Xunit;
+
+namespace Jellyfin.Naming.Tests.Video
+{
+ public sealed class CleanStringTests
+ {
+ private readonly NamingOptions _namingOptions = new NamingOptions();
+
+ [Theory]
+ [InlineData("Super movie 480p.mp4", "Super movie")]
+ [InlineData("Super movie 480p 2001.mp4", "Super movie")]
+ [InlineData("Super movie [480p].mp4", "Super movie")]
+ [InlineData("480 Super movie [tmdbid=12345].mp4", "480 Super movie")]
+ [InlineData("Super movie(2009).mp4", "Super movie(2009).mp4")]
+ [InlineData("Run lola run (lola rennt) (2009).mp4", "Run lola run (lola rennt) (2009).mp4")]
+ [InlineData(@"American.Psycho.mkv", "American.Psycho.mkv")]
+ [InlineData(@"American Psycho.mkv", "American Psycho.mkv")]
+ [InlineData(@"[rec].mkv", "[rec].mkv")]
+ [InlineData("Crouching.Tiger.Hidden.Dragon.4k.mkv", "Crouching.Tiger.Hidden.Dragon")]
+ [InlineData("Crouching.Tiger.Hidden.Dragon.UltraHD.mkv", "Crouching.Tiger.Hidden.Dragon")]
+ [InlineData("Crouching.Tiger.Hidden.Dragon.UHD.mkv", "Crouching.Tiger.Hidden.Dragon")]
+ [InlineData("Crouching.Tiger.Hidden.Dragon.HDR.mkv", "Crouching.Tiger.Hidden.Dragon")]
+ [InlineData("Crouching.Tiger.Hidden.Dragon.HDC.mkv", "Crouching.Tiger.Hidden.Dragon")]
+ [InlineData("Crouching.Tiger.Hidden.Dragon-HDC.mkv", "Crouching.Tiger.Hidden.Dragon")]
+ [InlineData("Crouching.Tiger.Hidden.Dragon.BDrip.mkv", "Crouching.Tiger.Hidden.Dragon")]
+ [InlineData("Crouching.Tiger.Hidden.Dragon.BDrip-HDC.mkv", "Crouching.Tiger.Hidden.Dragon")]
+ [InlineData("Crouching.Tiger.Hidden.Dragon.4K.UltraHD.HDR.BDrip-HDC.mkv", "Crouching.Tiger.Hidden.Dragon")]
+ // FIXME: [InlineData("After The Sunset - [0004].mkv", "After The Sunset")]
+ public void CleanStringTest(string input, string expectedName)
+ {
+ if (new VideoResolver(_namingOptions).TryCleanString(input, out ReadOnlySpan<char> newName))
+ {
+ // TODO: compare spans when XUnit supports it
+ Assert.Equal(expectedName, newName.ToString());
+ }
+ else
+ {
+ Assert.Equal(expectedName, input);
+ }
+ }
+ }
+}
diff --git a/tests/Jellyfin.Naming.Tests/Video/ExtraTests.cs b/tests/Jellyfin.Naming.Tests/Video/ExtraTests.cs
new file mode 100644
index 000000000..1646237a0
--- /dev/null
+++ b/tests/Jellyfin.Naming.Tests/Video/ExtraTests.cs
@@ -0,0 +1,77 @@
+using Emby.Naming.Common;
+using Emby.Naming.Video;
+using MediaBrowser.Model.Entities;
+using Xunit;
+
+namespace Jellyfin.Naming.Tests.Video
+{
+ public class ExtraTests : BaseVideoTest
+ {
+ // Requirements
+ // movie-deleted = ExtraType deletedscene
+
+ // All of the above rules should be configurable through the options objects (ideally, even the ExtraTypes)
+
+ [Fact]
+ public void TestKodiExtras()
+ {
+ var videoOptions = new NamingOptions();
+
+ Test("trailer.mp4", ExtraType.Trailer, videoOptions);
+ Test("300-trailer.mp4", ExtraType.Trailer, videoOptions);
+
+ Test("theme.mp3", ExtraType.ThemeSong, videoOptions);
+ }
+
+ [Fact]
+ public void TestExpandedExtras()
+ {
+ var videoOptions = new NamingOptions();
+
+ Test("trailer.mp4", ExtraType.Trailer, videoOptions);
+ Test("trailer.mp3", null, videoOptions);
+ Test("300-trailer.mp4", ExtraType.Trailer, videoOptions);
+
+ Test("theme.mp3", ExtraType.ThemeSong, videoOptions);
+ Test("theme.mkv", null, videoOptions);
+
+ Test("300-scene.mp4", ExtraType.Scene, videoOptions);
+ Test("300-scene2.mp4", ExtraType.Scene, videoOptions);
+ Test("300-clip.mp4", ExtraType.Clip, videoOptions);
+
+ Test("300-deleted.mp4", ExtraType.DeletedScene, videoOptions);
+ Test("300-deletedscene.mp4", ExtraType.DeletedScene, videoOptions);
+ Test("300-interview.mp4", ExtraType.Interview, videoOptions);
+ Test("300-behindthescenes.mp4", ExtraType.BehindTheScenes, videoOptions);
+ }
+
+ [Fact]
+ public void TestSample()
+ {
+ var videoOptions = new NamingOptions();
+
+ Test("300-sample.mp4", ExtraType.Sample, videoOptions);
+ }
+
+ private void Test(string input, ExtraType? expectedType, NamingOptions videoOptions)
+ {
+ var parser = GetExtraTypeParser(videoOptions);
+
+ var extraType = parser.GetExtraInfo(input).ExtraType;
+
+ if (expectedType == null)
+ {
+ Assert.Null(extraType);
+ }
+ else
+ {
+ Assert.Equal(expectedType, extraType);
+ }
+ }
+
+ private ExtraResolver GetExtraTypeParser(NamingOptions videoOptions)
+ {
+ return new ExtraResolver(videoOptions);
+ }
+ }
+}
diff --git a/tests/Jellyfin.Naming.Tests/Video/Format3DTests.cs b/tests/Jellyfin.Naming.Tests/Video/Format3DTests.cs
new file mode 100644
index 000000000..ed3112936
--- /dev/null
+++ b/tests/Jellyfin.Naming.Tests/Video/Format3DTests.cs
@@ -0,0 +1,78 @@
+using Emby.Naming.Common;
+using Emby.Naming.Video;
+using Xunit;
+
+namespace Jellyfin.Naming.Tests.Video
+{
+ public class Format3DTests : BaseVideoTest
+ {
+ [Fact]
+ public void TestKodiFormat3D()
+ {
+ var options = new NamingOptions();
+
+ Test("Super movie.3d.mp4", false, null, options);
+ Test("Super movie.3d.hsbs.mp4", true, "hsbs", options);
+ Test("Super movie.3d.sbs.mp4", true, "sbs", options);
+ Test("Super movie.3d.htab.mp4", true, "htab", options);
+ Test("Super movie.3d.tab.mp4", true, "tab", options);
+ Test("Super movie 3d hsbs.mp4", true, "hsbs", options);
+ }
+
+ [Fact]
+ public void Test3DName()
+ {
+ var result =
+ GetParser().ResolveFile(@"C:/Users/media/Desktop/Video Test/Movies/Oblivion/Oblivion.3d.hsbs.mkv");
+
+ Assert.Equal("hsbs", result.Format3D);
+ Assert.Equal("Oblivion", result.Name);
+ }
+
+ [Fact]
+ public void TestExpandedFormat3D()
+ {
+ // These were introduced for Media Browser 3
+ // Kodi conventions are preferred but these still need to be supported
+ var options = new NamingOptions();
+
+ Test("Super movie.3d.mp4", false, null, options);
+ Test("Super movie.3d.hsbs.mp4", true, "hsbs", options);
+ Test("Super movie.3d.sbs.mp4", true, "sbs", options);
+ Test("Super movie.3d.htab.mp4", true, "htab", options);
+ Test("Super movie.3d.tab.mp4", true, "tab", options);
+
+ Test("Super movie.hsbs.mp4", true, "hsbs", options);
+ Test("Super movie.sbs.mp4", true, "sbs", options);
+ Test("Super movie.htab.mp4", true, "htab", options);
+ Test("Super movie.tab.mp4", true, "tab", options);
+ Test("Super movie.sbs3d.mp4", true, "sbs3d", options);
+ Test("Super movie.3d.mvc.mp4", true, "mvc", options);
+
+ Test("Super movie [3d].mp4", false, null, options);
+ Test("Super movie [hsbs].mp4", true, "hsbs", options);
+ Test("Super movie [fsbs].mp4", true, "fsbs", options);
+ Test("Super movie [ftab].mp4", true, "ftab", options);
+ Test("Super movie [htab].mp4", true, "htab", options);
+ Test("Super movie [sbs3d].mp4", true, "sbs3d", options);
+ }
+
+ private void Test(string input, bool is3D, string format3D, NamingOptions options)
+ {
+ var parser = new Format3DParser(options);
+
+ var result = parser.Parse(input);
+
+ Assert.Equal(is3D, result.Is3D);
+
+ if (format3D == null)
+ {
+ Assert.Null(result.Format3D);
+ }
+ else
+ {
+ Assert.Equal(format3D, result.Format3D, true);
+ }
+ }
+ }
+}
diff --git a/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs b/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs
new file mode 100644
index 000000000..b8fbb2cb2
--- /dev/null
+++ b/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs
@@ -0,0 +1,437 @@
+using System.Linq;
+using Emby.Naming.Common;
+using Emby.Naming.Video;
+using MediaBrowser.Model.IO;
+using Xunit;
+
+namespace Jellyfin.Naming.Tests.Video
+{
+ public class MultiVersionTests
+ {
+ // FIXME
+ // [Fact]
+ public void TestMultiEdition1()
+ {
+ var files = new[]
+ {
+ @"/movies/X-Men Days of Future Past/X-Men Days of Future Past - 1080p.mkv",
+ @"/movies/X-Men Days of Future Past/X-Men Days of Future Past-trailer.mp4",
+ @"/movies/X-Men Days of Future Past/X-Men Days of Future Past - [hsbs].mkv",
+ @"/movies/X-Men Days of Future Past/X-Men Days of Future Past [hsbs].mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Single(result);
+ Assert.Single(result[0].Extras);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestMultiEdition2()
+ {
+ var files = new[]
+ {
+ @"/movies/X-Men Days of Future Past/X-Men Days of Future Past - apple.mkv",
+ @"/movies/X-Men Days of Future Past/X-Men Days of Future Past-trailer.mp4",
+ @"/movies/X-Men Days of Future Past/X-Men Days of Future Past - banana.mkv",
+ @"/movies/X-Men Days of Future Past/X-Men Days of Future Past [banana].mp4"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Single(result);
+ Assert.Single(result[0].Extras);
+ Assert.Equal(2, result[0].AlternateVersions.Count);
+ }
+
+ [Fact]
+ public void TestMultiEdition3()
+ {
+ var files = new[]
+ {
+ @"/movies/The Phantom of the Opera (1925)/The Phantom of the Opera (1925) - 1925 version.mkv",
+ @"/movies/The Phantom of the Opera (1925)/The Phantom of the Opera (1925) - 1929 version.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Single(result);
+ Assert.Single(result[0].AlternateVersions);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestLetterFolders()
+ {
+ var files = new[]
+ {
+ @"/movies/M/Movie 1.mkv",
+ @"/movies/M/Movie 2.mkv",
+ @"/movies/M/Movie 3.mkv",
+ @"/movies/M/Movie 4.mkv",
+ @"/movies/M/Movie 5.mkv",
+ @"/movies/M/Movie 6.mkv",
+ @"/movies/M/Movie 7.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Equal(7, result.Count);
+ Assert.Empty(result[0].Extras);
+ Assert.Empty(result[0].AlternateVersions);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestMultiVersionLimit()
+ {
+ var files = new[]
+ {
+ @"/movies/Movie/Movie.mkv",
+ @"/movies/Movie/Movie-2.mkv",
+ @"/movies/Movie/Movie-3.mkv",
+ @"/movies/Movie/Movie-4.mkv",
+ @"/movies/Movie/Movie-5.mkv",
+ @"/movies/Movie/Movie-6.mkv",
+ @"/movies/Movie/Movie-7.mkv",
+ @"/movies/Movie/Movie-8.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Single(result);
+ Assert.Empty(result[0].Extras);
+ Assert.Equal(7, result[0].AlternateVersions.Count);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestMultiVersionLimit2()
+ {
+ var files = new[]
+ {
+ @"/movies/Mo/Movie 1.mkv",
+ @"/movies/Mo/Movie 2.mkv",
+ @"/movies/Mo/Movie 3.mkv",
+ @"/movies/Mo/Movie 4.mkv",
+ @"/movies/Mo/Movie 5.mkv",
+ @"/movies/Mo/Movie 6.mkv",
+ @"/movies/Mo/Movie 7.mkv",
+ @"/movies/Mo/Movie 8.mkv",
+ @"/movies/Mo/Movie 9.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Equal(9, result.Count);
+ Assert.Empty(result[0].Extras);
+ Assert.Empty(result[0].AlternateVersions);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestMultiVersion3()
+ {
+ var files = new[]
+ {
+ @"/movies/Movie/Movie 1.mkv",
+ @"/movies/Movie/Movie 2.mkv",
+ @"/movies/Movie/Movie 3.mkv",
+ @"/movies/Movie/Movie 4.mkv",
+ @"/movies/Movie/Movie 5.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Equal(5, result.Count);
+ Assert.Empty(result[0].Extras);
+ Assert.Empty(result[0].AlternateVersions);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestMultiVersion4()
+ {
+ // Test for false positive
+
+ var files = new[]
+ {
+ @"/movies/Iron Man/Iron Man.mkv",
+ @"/movies/Iron Man/Iron Man (2008).mkv",
+ @"/movies/Iron Man/Iron Man (2009).mkv",
+ @"/movies/Iron Man/Iron Man (2010).mkv",
+ @"/movies/Iron Man/Iron Man (2011).mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Equal(5, result.Count);
+ Assert.Empty(result[0].Extras);
+ Assert.Empty(result[0].AlternateVersions);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestMultiVersion5()
+ {
+ var files = new[]
+ {
+ @"/movies/Iron Man/Iron Man.mkv",
+ @"/movies/Iron Man/Iron Man-720p.mkv",
+ @"/movies/Iron Man/Iron Man-test.mkv",
+ @"/movies/Iron Man/Iron Man-bluray.mkv",
+ @"/movies/Iron Man/Iron Man-3d.mkv",
+ @"/movies/Iron Man/Iron Man-3d-hsbs.mkv",
+ @"/movies/Iron Man/Iron Man-3d.hsbs.mkv",
+ @"/movies/Iron Man/Iron Man[test].mkv",
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Single(result);
+ Assert.Empty(result[0].Extras);
+ Assert.Equal(7, result[0].AlternateVersions.Count);
+ Assert.False(result[0].AlternateVersions[2].Is3D);
+ Assert.True(result[0].AlternateVersions[3].Is3D);
+ Assert.True(result[0].AlternateVersions[4].Is3D);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestMultiVersion6()
+ {
+ var files = new[]
+ {
+ @"/movies/Iron Man/Iron Man.mkv",
+ @"/movies/Iron Man/Iron Man - 720p.mkv",
+ @"/movies/Iron Man/Iron Man - test.mkv",
+ @"/movies/Iron Man/Iron Man - bluray.mkv",
+ @"/movies/Iron Man/Iron Man - 3d.mkv",
+ @"/movies/Iron Man/Iron Man - 3d-hsbs.mkv",
+ @"/movies/Iron Man/Iron Man - 3d.hsbs.mkv",
+ @"/movies/Iron Man/Iron Man [test].mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Single(result);
+ Assert.Empty(result[0].Extras);
+ Assert.Equal(7, result[0].AlternateVersions.Count);
+ Assert.False(result[0].AlternateVersions[3].Is3D);
+ Assert.True(result[0].AlternateVersions[4].Is3D);
+ Assert.True(result[0].AlternateVersions[5].Is3D);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestMultiVersion7()
+ {
+ var files = new[]
+ {
+ @"/movies/Iron Man/Iron Man - B (2006).mkv",
+ @"/movies/Iron Man/Iron Man - C (2007).mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Equal(2, result.Count);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestMultiVersion8()
+ {
+ // This is not actually supported yet
+
+ var files = new[]
+ {
+ @"/movies/Iron Man/Iron Man.mkv",
+ @"/movies/Iron Man/Iron Man_720p.mkv",
+ @"/movies/Iron Man/Iron Man_test.mkv",
+ @"/movies/Iron Man/Iron Man_bluray.mkv",
+ @"/movies/Iron Man/Iron Man_3d.mkv",
+ @"/movies/Iron Man/Iron Man_3d-hsbs.mkv",
+ @"/movies/Iron Man/Iron Man_3d.hsbs.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Single(result);
+ Assert.Empty(result[0].Extras);
+ Assert.Equal(6, result[0].AlternateVersions.Count);
+ Assert.False(result[0].AlternateVersions[2].Is3D);
+ Assert.True(result[0].AlternateVersions[3].Is3D);
+ Assert.True(result[0].AlternateVersions[4].Is3D);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestMultiVersion9()
+ {
+ // Test for false positive
+
+ var files = new[]
+ {
+ @"/movies/Iron Man/Iron Man (2007).mkv",
+ @"/movies/Iron Man/Iron Man (2008).mkv",
+ @"/movies/Iron Man/Iron Man (2009).mkv",
+ @"/movies/Iron Man/Iron Man (2010).mkv",
+ @"/movies/Iron Man/Iron Man (2011).mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Equal(5, result.Count);
+ Assert.Empty(result[0].Extras);
+ Assert.Empty(result[0].AlternateVersions);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestMultiVersion10()
+ {
+ var files = new[]
+ {
+ @"/movies/Blade Runner (1982)/Blade Runner (1982) [Final Cut] [1080p HEVC AAC].mkv",
+ @"/movies/Blade Runner (1982)/Blade Runner (1982) [EE by ADM] [480p HEVC AAC,AAC,AAC].mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Single(result);
+ Assert.Empty(result[0].Extras);
+ Assert.Single(result[0].AlternateVersions);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestMultiVersion11()
+ {
+ // Currently not supported but we should probably handle this.
+
+ var files = new[]
+ {
+ @"/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016) [1080p] Blu-ray.x264.DTS.mkv",
+ @"/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016) [2160p] Blu-ray.x265.AAC.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Single(result);
+ Assert.Empty(result[0].Extras);
+ Assert.Single(result[0].AlternateVersions);
+ }
+
+ private VideoListResolver GetResolver()
+ {
+ var options = new NamingOptions();
+ return new VideoListResolver(options);
+ }
+ }
+}
diff --git a/tests/Jellyfin.Naming.Tests/Video/StackTests.cs b/tests/Jellyfin.Naming.Tests/Video/StackTests.cs
new file mode 100644
index 000000000..3e0cbaf0c
--- /dev/null
+++ b/tests/Jellyfin.Naming.Tests/Video/StackTests.cs
@@ -0,0 +1,452 @@
+using System.Linq;
+using Emby.Naming.Common;
+using Emby.Naming.Video;
+using MediaBrowser.Model.IO;
+using Xunit;
+
+namespace Jellyfin.Naming.Tests.Video
+{
+ public class StackTests : BaseVideoTest
+ {
+ [Fact]
+ public void TestSimpleStack()
+ {
+ var files = new[]
+ {
+ "Bad Boys (2006) part1.mkv",
+ "Bad Boys (2006) part2.mkv",
+ "Bad Boys (2006) part3.mkv",
+ "Bad Boys (2006) part4.mkv",
+ "Bad Boys (2006)-trailer.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.ResolveFiles(files).ToList();
+
+ Assert.Single(result);
+ TestStackInfo(result[0], "Bad Boys (2006)", 4);
+ }
+
+ [Fact]
+ public void TestFalsePositives()
+ {
+ var files = new[]
+ {
+ "Bad Boys (2006).mkv",
+ "Bad Boys (2007).mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.ResolveFiles(files).ToList();
+
+ Assert.Empty(result);
+ }
+
+ [Fact]
+ public void TestFalsePositives2()
+ {
+ var files = new[]
+ {
+ "Bad Boys 2006.mkv",
+ "Bad Boys 2007.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.ResolveFiles(files).ToList();
+
+ Assert.Empty(result);
+ }
+
+ [Fact]
+ public void TestFalsePositives3()
+ {
+ var files = new[]
+ {
+ "300 (2006).mkv",
+ "300 (2007).mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.ResolveFiles(files).ToList();
+
+ Assert.Empty(result);
+ }
+
+ [Fact]
+ public void TestFalsePositives4()
+ {
+ var files = new[]
+ {
+ "300 2006.mkv",
+ "300 2007.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.ResolveFiles(files).ToList();
+
+ Assert.Empty(result);
+ }
+
+ [Fact]
+ public void TestFalsePositives5()
+ {
+ var files = new[]
+ {
+ "Star Trek 1 - The motion picture.mkv",
+ "Star Trek 2- The wrath of khan.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.ResolveFiles(files).ToList();
+ Assert.Empty(result);
+ }
+
+ [Fact]
+ public void TestFalsePositives6()
+ {
+ var files = new[]
+ {
+ "Red Riding in the Year of Our Lord 1983 (2009).mkv",
+ "Red Riding in the Year of Our Lord 1980 (2009).mkv",
+ "Red Riding in the Year of Our Lord 1974 (2009).mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.ResolveFiles(files).ToList();
+
+ Assert.Empty(result);
+ }
+
+ [Fact]
+ public void TestStackName()
+ {
+ var files = new[]
+ {
+ "d:/movies/300 2006 part1.mkv",
+ "d:/movies/300 2006 part2.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.ResolveFiles(files).ToList();
+
+ Assert.Single(result);
+ TestStackInfo(result[0], "300 2006", 2);
+ }
+
+ [Fact]
+ public void TestDirtyNames()
+ {
+ var files = new[]
+ {
+ "Bad Boys (2006).part1.stv.unrated.multi.1080p.bluray.x264-rough.mkv",
+ "Bad Boys (2006).part2.stv.unrated.multi.1080p.bluray.x264-rough.mkv",
+ "Bad Boys (2006).part3.stv.unrated.multi.1080p.bluray.x264-rough.mkv",
+ "Bad Boys (2006).part4.stv.unrated.multi.1080p.bluray.x264-rough.mkv",
+ "Bad Boys (2006)-trailer.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.ResolveFiles(files).ToList();
+
+ Assert.Single(result);
+ TestStackInfo(result[0], "Bad Boys (2006).stv.unrated.multi.1080p.bluray.x264-rough", 4);
+ }
+
+ [Fact]
+ public void TestNumberedFiles()
+ {
+ var files = new[]
+ {
+ "Bad Boys (2006).mkv",
+ "Bad Boys (2006) 1.mkv",
+ "Bad Boys (2006) 2.mkv",
+ "Bad Boys (2006) 3.mkv",
+ "Bad Boys (2006)-trailer.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.ResolveFiles(files).ToList();
+
+ Assert.Empty(result);
+ }
+
+ [Fact]
+ public void TestSimpleStackWithNumericName()
+ {
+ var files = new[]
+ {
+ "300 (2006) part1.mkv",
+ "300 (2006) part2.mkv",
+ "300 (2006) part3.mkv",
+ "300 (2006) part4.mkv",
+ "300 (2006)-trailer.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.ResolveFiles(files).ToList();
+
+ Assert.Single(result);
+ TestStackInfo(result[0], "300 (2006)", 4);
+ }
+
+ [Fact]
+ public void TestMixedExpressionsNotAllowed()
+ {
+ var files = new[]
+ {
+ "Bad Boys (2006) part1.mkv",
+ "Bad Boys (2006) part2.mkv",
+ "Bad Boys (2006) part3.mkv",
+ "Bad Boys (2006) parta.mkv",
+ "Bad Boys (2006)-trailer.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.ResolveFiles(files).ToList();
+
+ Assert.Single(result);
+ TestStackInfo(result[0], "Bad Boys (2006)", 3);
+ }
+
+ [Fact]
+ public void TestDualStacks()
+ {
+ var files = new[]
+ {
+ "Bad Boys (2006) part1.mkv",
+ "Bad Boys (2006) part2.mkv",
+ "Bad Boys (2006) part3.mkv",
+ "Bad Boys (2006) part4.mkv",
+ "Bad Boys (2006)-trailer.mkv",
+ "300 (2006) part1.mkv",
+ "300 (2006) part2.mkv",
+ "300 (2006) part3.mkv",
+ "300 (2006)-trailer.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.ResolveFiles(files).ToList();
+
+ Assert.Equal(2, result.Count);
+ TestStackInfo(result[1], "Bad Boys (2006)", 4);
+ TestStackInfo(result[0], "300 (2006)", 3);
+ }
+
+ [Fact]
+ public void TestDirectories()
+ {
+ var files = new[]
+ {
+ "blah blah - cd 1",
+ "blah blah - cd 2"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.ResolveDirectories(files).ToList();
+
+ Assert.Single(result);
+ TestStackInfo(result[0], "blah blah", 2);
+ }
+
+ [Fact]
+ public void TestFalsePositive()
+ {
+ var files = new[]
+ {
+ "300a.mkv",
+ "300b.mkv",
+ "300c.mkv",
+ "300-trailer.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.ResolveFiles(files).ToList();
+
+ Assert.Single(result);
+
+ TestStackInfo(result[0], "300", 3);
+ }
+
+ [Fact]
+ public void TestFailSequence()
+ {
+ var files = new[]
+ {
+ "300 part1.mkv",
+ "300 part2.mkv",
+ "Avatar",
+ "Avengers part1.mkv",
+ "Avengers part2.mkv",
+ "Avengers part3.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.ResolveFiles(files).ToList();
+
+ Assert.Equal(2, result.Count);
+
+ TestStackInfo(result[0], "300", 2);
+ TestStackInfo(result[1], "Avengers", 3);
+ }
+
+ [Fact]
+ public void TestMixedExpressions()
+ {
+ var files = new[]
+ {
+ "Bad Boys (2006) part1.mkv",
+ "Bad Boys (2006) part2.mkv",
+ "Bad Boys (2006) part3.mkv",
+ "Bad Boys (2006) part4.mkv",
+ "Bad Boys (2006)-trailer.mkv",
+ "300 (2006) parta.mkv",
+ "300 (2006) partb.mkv",
+ "300 (2006) partc.mkv",
+ "300 (2006) partd.mkv",
+ "300 (2006)-trailer.mkv",
+ "300a.mkv",
+ "300b.mkv",
+ "300c.mkv",
+ "300-trailer.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.ResolveFiles(files).ToList();
+
+ Assert.Equal(3, result.Count);
+
+ TestStackInfo(result[0], "300 (2006)", 4);
+ TestStackInfo(result[1], "300", 3);
+ TestStackInfo(result[2], "Bad Boys (2006)", 4);
+ }
+
+ [Fact]
+ public void TestAlphaLimitOfFour()
+ {
+ var files = new[]
+ {
+ "300 (2006) parta.mkv",
+ "300 (2006) partb.mkv",
+ "300 (2006) partc.mkv",
+ "300 (2006) partd.mkv",
+ "300 (2006) parte.mkv",
+ "300 (2006) partf.mkv",
+ "300 (2006) partg.mkv",
+ "300 (2006)-trailer.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.ResolveFiles(files).ToList();
+
+ Assert.Single(result);
+
+ TestStackInfo(result[0], "300 (2006)", 4);
+ }
+
+ [Fact]
+ public void TestMixed()
+ {
+ var files = new[]
+ {
+ new FileSystemMetadata{FullName = "Bad Boys (2006) part1.mkv", IsDirectory = false},
+ new FileSystemMetadata{FullName = "Bad Boys (2006) part2.mkv", IsDirectory = false},
+ new FileSystemMetadata{FullName = "300 (2006) part2", IsDirectory = true},
+ new FileSystemMetadata{FullName = "300 (2006) part3", IsDirectory = true},
+ new FileSystemMetadata{FullName = "300 (2006) part1", IsDirectory = true}
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files).ToList();
+
+ Assert.Equal(2, result.Count);
+ TestStackInfo(result[0], "300 (2006)", 3);
+ TestStackInfo(result[1], "Bad Boys (2006)", 2);
+ }
+
+ [Fact]
+ public void TestNamesWithoutParts()
+ {
+ // No stacking here because there is no part/disc/etc
+ var files = new[]
+ {
+ "Harry Potter and the Deathly Hallows.mkv",
+ "Harry Potter and the Deathly Hallows 1.mkv",
+ "Harry Potter and the Deathly Hallows 2.mkv",
+ "Harry Potter and the Deathly Hallows 3.mkv",
+ "Harry Potter and the Deathly Hallows 4.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.ResolveFiles(files).ToList();
+
+ Assert.Empty(result);
+ }
+
+ [Fact]
+ public void TestNumbersAppearingBeforePartNumber()
+ {
+ // No stacking here because there is no part/disc/etc
+ var files = new[]
+ {
+ "Neverland (2011)[720p][PG][Voted 6.5][Family-Fantasy]part1.mkv",
+ "Neverland (2011)[720p][PG][Voted 6.5][Family-Fantasy]part2.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.ResolveFiles(files).ToList();
+
+ Assert.Single(result);
+ Assert.Equal(2, result[0].Files.Count);
+ }
+
+ [Fact]
+ public void TestMultiDiscs()
+ {
+ // No stacking here because there is no part/disc/etc
+ var files = new[]
+ {
+ @"M:/Movies (DVD)/Movies (Musical)/The Sound of Music/The Sound of Music (1965) (Disc 01)",
+ @"M:/Movies (DVD)/Movies (Musical)/The Sound of Music/The Sound of Music (1965) (Disc 02)"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.ResolveDirectories(files).ToList();
+
+ Assert.Single(result);
+ Assert.Equal(2, result[0].Files.Count);
+ }
+
+ private void TestStackInfo(FileStack stack, string name, int fileCount)
+ {
+ Assert.Equal(fileCount, stack.Files.Count);
+ Assert.Equal(name, stack.Name);
+ }
+
+ private StackResolver GetResolver()
+ {
+ return new StackResolver(new NamingOptions());
+ }
+ }
+}
diff --git a/tests/Jellyfin.Naming.Tests/Video/StubTests.cs b/tests/Jellyfin.Naming.Tests/Video/StubTests.cs
new file mode 100644
index 000000000..8d5ced9a4
--- /dev/null
+++ b/tests/Jellyfin.Naming.Tests/Video/StubTests.cs
@@ -0,0 +1,53 @@
+using Emby.Naming.Common;
+using Emby.Naming.Video;
+using Xunit;
+
+namespace Jellyfin.Naming.Tests.Video
+{
+ public class StubTests : BaseVideoTest
+ {
+ [Fact]
+ public void TestStubs()
+ {
+ Test("video.mkv", false, null);
+ Test("video.disc", true, null);
+ Test("video.dvd.disc", true, "dvd");
+ Test("video.hddvd.disc", true, "hddvd");
+ Test("video.bluray.disc", true, "bluray");
+ Test("video.brrip.disc", true, "bluray");
+ Test("video.bd25.disc", true, "bluray");
+ Test("video.bd50.disc", true, "bluray");
+ Test("video.vhs.disc", true, "vhs");
+ Test("video.hdtv.disc", true, "tv");
+ Test("video.pdtv.disc", true, "tv");
+ Test("video.dsr.disc", true, "tv");
+ }
+
+ [Fact]
+ public void TestStubName()
+ {
+ var result =
+ GetParser().ResolveFile(@"C:/Users/media/Desktop/Video Test/Movies/Oblivion/Oblivion.dvd.disc");
+
+ Assert.Equal("Oblivion", result.Name);
+ }
+
+ private void Test(string path, bool isStub, string stubType)
+ {
+ var options = new NamingOptions();
+
+ var isStubResult = StubResolver.TryResolveFile(path, options, out var stubTypeResult);
+
+ Assert.Equal(isStub, isStubResult);
+
+ if (isStub)
+ {
+ Assert.Equal(stubType, stubTypeResult);
+ }
+ else
+ {
+ Assert.Null(stubTypeResult);
+ }
+ }
+ }
+}
diff --git a/tests/Jellyfin.Naming.Tests/Video/VideoListResolverTests.cs b/tests/Jellyfin.Naming.Tests/Video/VideoListResolverTests.cs
new file mode 100644
index 000000000..ef8a17898
--- /dev/null
+++ b/tests/Jellyfin.Naming.Tests/Video/VideoListResolverTests.cs
@@ -0,0 +1,457 @@
+using System.Linq;
+using Emby.Naming.Common;
+using Emby.Naming.Video;
+using MediaBrowser.Model.IO;
+using Xunit;
+
+namespace Jellyfin.Naming.Tests.Video
+{
+ public class VideoListResolverTests
+ {
+ // FIXME
+ // [Fact]
+ public void TestStackAndExtras()
+ {
+ // No stacking here because there is no part/disc/etc
+ var files = new[]
+ {
+ "Harry Potter and the Deathly Hallows-trailer.mkv",
+ "Harry Potter and the Deathly Hallows.trailer.mkv",
+ "Harry Potter and the Deathly Hallows part1.mkv",
+ "Harry Potter and the Deathly Hallows part2.mkv",
+ "Harry Potter and the Deathly Hallows part3.mkv",
+ "Harry Potter and the Deathly Hallows part4.mkv",
+ "Batman-deleted.mkv",
+ "Batman-sample.mkv",
+ "Batman-trailer.mkv",
+ "Batman part1.mkv",
+ "Batman part2.mkv",
+ "Batman part3.mkv",
+ "Avengers.mkv",
+ "Avengers-trailer.mkv",
+
+ // Despite having a keyword in the name that will return an ExtraType, there's no original video to match it to
+ // So this is just a standalone video
+ "trailer.mkv",
+
+ // Same as above
+ "WillyWonka-trailer.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Equal(5, result.Count);
+
+ Assert.Equal(3, result[1].Files.Count);
+ Assert.Equal(3, result[1].Extras.Count);
+ Assert.Equal("Batman", result[1].Name);
+
+ Assert.Equal(4, result[2].Files.Count);
+ Assert.Equal(2, result[2].Extras.Count);
+ Assert.Equal("Harry Potter and the Deathly Hallows", result[2].Name);
+ }
+
+ [Fact]
+ public void TestWithMetadata()
+ {
+ var files = new[]
+ {
+ "300.mkv",
+ "300.nfo"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Single(result);
+ }
+
+ [Fact]
+ public void TestWithExtra()
+ {
+ var files = new[]
+ {
+ "300.mkv",
+ "300 trailer.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Single(result);
+ }
+
+ [Fact]
+ public void TestVariationWithFolderName()
+ {
+ var files = new[]
+ {
+ "X-Men Days of Future Past - 1080p.mkv",
+ "X-Men Days of Future Past-trailer.mp4"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Single(result);
+ }
+
+ [Fact]
+ public void TestTrailer2()
+ {
+ var files = new[]
+ {
+ "X-Men Days of Future Past - 1080p.mkv",
+ "X-Men Days of Future Past-trailer.mp4",
+ "X-Men Days of Future Past-trailer2.mp4"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Single(result);
+ }
+
+ [Fact]
+ public void TestDifferentNames()
+ {
+ var files = new[]
+ {
+ "Looper (2012)-trailer.mkv",
+ "Looper.2012.bluray.720p.x264.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Single(result);
+ }
+
+ [Fact]
+ public void TestSeparateFiles()
+ {
+ // These should be considered separate, unrelated videos
+ var files = new[]
+ {
+ "My video 1.mkv",
+ "My video 2.mkv",
+ "My video 3.mkv",
+ "My video 4.mkv",
+ "My video 5.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Equal(5, result.Count);
+ }
+
+ [Fact]
+ public void TestMultiDisc()
+ {
+ var files = new[]
+ {
+ @"M:/Movies (DVD)/Movies (Musical)/Sound of Music (1965)/Sound of Music Disc 1",
+ @"M:/Movies (DVD)/Movies (Musical)/Sound of Music (1965)/Sound of Music Disc 2"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = true,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Single(result);
+ }
+
+ [Fact]
+ public void TestPoundSign()
+ {
+ // These should be considered separate, unrelated videos
+ var files = new[]
+ {
+ @"My movie #1.mp4",
+ @"My movie #2.mp4"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = true,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Equal(2, result.Count);
+ }
+
+ [Fact]
+ public void TestStackedWithTrailer()
+ {
+ var files = new[]
+ {
+ @"No (2012) part1.mp4",
+ @"No (2012) part2.mp4",
+ @"No (2012) part1-trailer.mp4"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Single(result);
+ }
+
+ [Fact]
+ public void TestStackedWithTrailer2()
+ {
+ var files = new[]
+ {
+ @"No (2012) part1.mp4",
+ @"No (2012) part2.mp4",
+ @"No (2012)-trailer.mp4"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Single(result);
+ }
+
+ [Fact]
+ public void TestExtrasByFolderName()
+ {
+ var files = new[]
+ {
+ @"/Movies/Top Gun (1984)/movie.mp4",
+ @"/Movies/Top Gun (1984)/Top Gun (1984)-trailer.mp4",
+ @"/Movies/Top Gun (1984)/Top Gun (1984)-trailer2.mp4",
+ @"trailer.mp4"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Single(result);
+ }
+
+ [Fact]
+ public void TestDoubleTags()
+ {
+ var files = new[]
+ {
+ @"/MCFAMILY-PC/Private3$/Heterosexual/Breast In Class 2 Counterfeit Racks (2011)/Breast In Class 2 Counterfeit Racks (2011) Disc 1 cd1.avi",
+ @"/MCFAMILY-PC/Private3$/Heterosexual/Breast In Class 2 Counterfeit Racks (2011)/Breast In Class 2 Counterfeit Racks (2011) Disc 1 cd2.avi",
+ @"/MCFAMILY-PC/Private3$/Heterosexual/Breast In Class 2 Counterfeit Racks (2011)/Breast In Class 2 Disc 2 cd1.avi",
+ @"/MCFAMILY-PC/Private3$/Heterosexual/Breast In Class 2 Counterfeit Racks (2011)/Breast In Class 2 Disc 2 cd2.avi"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Equal(2, result.Count);
+ }
+
+ [Fact]
+ public void TestArgumentOutOfRangeException()
+ {
+ var files = new[]
+ {
+ @"/nas-markrobbo78/Videos/INDEX HTPC/Movies/Watched/3 - ACTION/Argo (2012)/movie.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Single(result);
+ }
+
+ [Fact]
+ public void TestColony()
+ {
+ var files = new[]
+ {
+ @"The Colony.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Single(result);
+ }
+
+ [Fact]
+ public void TestFourSisters()
+ {
+ var files = new[]
+ {
+ @"Four Sisters and a Wedding - A.avi",
+ @"Four Sisters and a Wedding - B.avi"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Single(result);
+ }
+
+ [Fact]
+ public void TestMovieTrailer()
+ {
+ var files = new[]
+ {
+ @"/Server/Despicable Me/Despicable Me (2010).mkv",
+ @"/Server/Despicable Me/movie-trailer.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Single(result);
+ }
+
+ [Fact]
+ public void TestTrailerFalsePositives()
+ {
+ var files = new[]
+ {
+ @"/Server/Despicable Me/Skyscraper (2018) - Big Game Spot.mkv",
+ @"/Server/Despicable Me/Skyscraper (2018) - Trailer.mkv",
+ @"/Server/Despicable Me/Baywatch (2017) - Big Game Spot.mkv",
+ @"/Server/Despicable Me/Baywatch (2017) - Trailer.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Equal(4, result.Count);
+ }
+
+ [Fact]
+ public void TestSubfolders()
+ {
+ var files = new[]
+ {
+ @"/Movies/Despicable Me/Despicable Me.mkv",
+ @"/Movies/Despicable Me/trailers/trailer.mkv"
+ };
+
+ var resolver = GetResolver();
+
+ var result = resolver.Resolve(files.Select(i => new FileSystemMetadata
+ {
+ IsDirectory = false,
+ FullName = i
+
+ }).ToList()).ToList();
+
+ Assert.Single(result);
+ }
+
+ private VideoListResolver GetResolver()
+ {
+ var options = new NamingOptions();
+ return new VideoListResolver(options);
+ }
+ }
+}
diff --git a/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs b/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs
new file mode 100644
index 000000000..5a3ce8886
--- /dev/null
+++ b/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs
@@ -0,0 +1,275 @@
+using MediaBrowser.Model.Entities;
+using Xunit;
+
+namespace Jellyfin.Naming.Tests.Video
+{
+ public class VideoResolverTests : BaseVideoTest
+ {
+ // FIXME
+ // [Fact]
+ public void TestSimpleFile()
+ {
+ var parser = GetParser();
+
+ var result =
+ parser.ResolveFile(@"/server/Movies/Brave (2007)/Brave (2006).mkv");
+
+ Assert.Equal(2006, result.Year);
+ Assert.False(result.IsStub);
+ Assert.False(result.Is3D);
+ Assert.Equal("Brave", result.Name);
+ Assert.Null(result.ExtraType);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestSimpleFile2()
+ {
+ var parser = GetParser();
+
+ var result =
+ parser.ResolveFile(@"/server/Movies/Bad Boys (1995)/Bad Boys (1995).mkv");
+
+ Assert.Equal(1995, result.Year);
+ Assert.False(result.IsStub);
+ Assert.False(result.Is3D);
+ Assert.Equal("Bad Boys", result.Name);
+ Assert.Null(result.ExtraType);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestSimpleFileWithNumericName()
+ {
+ var parser = GetParser();
+
+ var result =
+ parser.ResolveFile(@"/server/Movies/300 (2007)/300 (2006).mkv");
+
+ Assert.Equal(2006, result.Year);
+ Assert.False(result.IsStub);
+ Assert.False(result.Is3D);
+ Assert.Equal("300", result.Name);
+ Assert.Null(result.ExtraType);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestExtra()
+ {
+ var parser = GetParser();
+
+ var result =
+ parser.ResolveFile(@"/server/Movies/Brave (2007)/Brave (2006)-trailer.mkv");
+
+ Assert.Equal(2006, result.Year);
+ Assert.False(result.IsStub);
+ Assert.False(result.Is3D);
+ Assert.Equal(ExtraType.Trailer, result.ExtraType);
+ Assert.Equal("Brave (2006)-trailer", result.Name);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestExtraWithNumericName()
+ {
+ var parser = GetParser();
+
+ var result =
+ parser.ResolveFile(@"/server/Movies/300 (2007)/300 (2006)-trailer.mkv");
+
+ Assert.Equal(2006, result.Year);
+ Assert.False(result.IsStub);
+ Assert.False(result.Is3D);
+ Assert.Equal("300 (2006)-trailer", result.Name);
+ Assert.Equal(ExtraType.Trailer, result.ExtraType);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestStubFileWithNumericName()
+ {
+ var parser = GetParser();
+
+ var result =
+ parser.ResolveFile(@"/server/Movies/300 (2007)/300 (2006).bluray.disc");
+
+ Assert.Equal(2006, result.Year);
+ Assert.True(result.IsStub);
+ Assert.Equal("bluray", result.StubType);
+ Assert.False(result.Is3D);
+ Assert.Equal("300", result.Name);
+ Assert.Null(result.ExtraType);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestStubFile()
+ {
+ var parser = GetParser();
+
+ var result =
+ parser.ResolveFile(@"/server/Movies/Brave (2007)/Brave (2006).bluray.disc");
+
+ Assert.Equal(2006, result.Year);
+ Assert.True(result.IsStub);
+ Assert.Equal("bluray", result.StubType);
+ Assert.False(result.Is3D);
+ Assert.Equal("Brave", result.Name);
+ Assert.Null(result.ExtraType);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestExtraStubWithNumericNameNotSupported()
+ {
+ var parser = GetParser();
+
+ var result =
+ parser.ResolveFile(@"/server/Movies/300 (2007)/300 (2006)-trailer.bluray.disc");
+
+ Assert.Equal(2006, result.Year);
+ Assert.True(result.IsStub);
+ Assert.Equal("bluray", result.StubType);
+ Assert.False(result.Is3D);
+ Assert.Equal("300", result.Name);
+ Assert.Null(result.ExtraType);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestExtraStubNotSupported()
+ {
+ // Using a stub for an extra is currently not supported
+ var parser = GetParser();
+
+ var result =
+ parser.ResolveFile(@"/server/Movies/brave (2007)/brave (2006)-trailer.bluray.disc");
+
+ Assert.Equal(2006, result.Year);
+ Assert.True(result.IsStub);
+ Assert.Equal("bluray", result.StubType);
+ Assert.False(result.Is3D);
+ Assert.Equal("brave", result.Name);
+ Assert.Null(result.ExtraType);
+ }
+
+ // FIXME
+ // [Fact]
+ public void Test3DFileWithNumericName()
+ {
+ var parser = GetParser();
+
+ var result =
+ parser.ResolveFile(@"/server/Movies/300 (2007)/300 (2006).3d.sbs.mkv");
+
+ Assert.Equal(2006, result.Year);
+ Assert.False(result.IsStub);
+ Assert.True(result.Is3D);
+ Assert.Equal("sbs", result.Format3D);
+ Assert.Equal("300", result.Name);
+ Assert.Null(result.ExtraType);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestBad3DFileWithNumericName()
+ {
+ var parser = GetParser();
+
+ var result =
+ parser.ResolveFile(@"/server/Movies/300 (2007)/300 (2006).3d1.sbas.mkv");
+
+ Assert.Equal(2006, result.Year);
+ Assert.False(result.IsStub);
+ Assert.False(result.Is3D);
+ Assert.Equal("300", result.Name);
+ Assert.Null(result.ExtraType);
+ Assert.Null(result.Format3D);
+ }
+
+ // FIXME
+ // [Fact]
+ public void Test3DFile()
+ {
+ var parser = GetParser();
+
+ var result =
+ parser.ResolveFile(@"/server/Movies/brave (2007)/brave (2006).3d.sbs.mkv");
+
+ Assert.Equal(2006, result.Year);
+ Assert.False(result.IsStub);
+ Assert.True(result.Is3D);
+ Assert.Equal("sbs", result.Format3D);
+ Assert.Equal("brave", result.Name);
+ Assert.Null(result.ExtraType);
+ }
+
+ [Fact]
+ public void TestNameWithoutDate()
+ {
+ var parser = GetParser();
+
+ var result =
+ parser.ResolveFile(@"/server/Movies/American Psycho/American.Psycho.mkv");
+
+ Assert.Null(result.Year);
+ Assert.False(result.IsStub);
+ Assert.False(result.Is3D);
+ Assert.Null(result.Format3D);
+ Assert.Equal("American.Psycho", result.Name);
+ Assert.Null(result.ExtraType);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestCleanDateAndStringsSequence()
+ {
+ var parser = GetParser();
+
+ // In this test case, running CleanDateTime first produces no date, so it will attempt to run CleanString first and then CleanDateTime again
+ var result =
+ parser.ResolveFile(@"/server/Movies/3.Days.to.Kill/3.Days.to.Kill.2014.720p.BluRay.x264.YIFY.mkv");
+
+ Assert.Equal(2014, result.Year);
+ Assert.False(result.IsStub);
+ Assert.False(result.Is3D);
+ Assert.Null(result.Format3D);
+ Assert.Equal("3.Days.to.Kill", result.Name);
+ Assert.Null(result.ExtraType);
+ }
+
+ // FIXME
+ // [Fact]
+ public void TestCleanDateAndStringsSequence1()
+ {
+ var parser = GetParser();
+
+ // In this test case, running CleanDateTime first produces no date, so it will attempt to run CleanString first and then CleanDateTime again
+ var result =
+ parser.ResolveFile(@"/server/Movies/3 days to kill (2005)/3 days to kill (2005).mkv");
+
+ Assert.Equal(2005, result.Year);
+ Assert.False(result.IsStub);
+ Assert.False(result.Is3D);
+ Assert.Null(result.Format3D);
+ Assert.Equal("3 days to kill", result.Name);
+ Assert.Null(result.ExtraType);
+ }
+
+ [Fact]
+ public void TestFolderNameWithExtension()
+ {
+ var parser = GetParser();
+
+ var result =
+ parser.ResolveFile(@"/server/Movies/7 Psychos.mkv/7 Psychos.mkv");
+
+ Assert.Null(result.Year);
+ Assert.False(result.IsStub);
+ Assert.False(result.Is3D);
+ Assert.Equal("7 Psychos", result.Name);
+ Assert.Null(result.ExtraType);
+ }
+ }
+}
diff --git a/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs b/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs
new file mode 100644
index 000000000..671c59b2e
--- /dev/null
+++ b/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs
@@ -0,0 +1,42 @@
+using AutoFixture;
+using AutoFixture.AutoMoq;
+using Emby.Server.Implementations.IO;
+using MediaBrowser.Model.System;
+using Xunit;
+
+namespace Jellyfin.Server.Implementations.Tests.IO
+{
+ public class ManagedFileSystemTests
+ {
+ private readonly IFixture _fixture;
+ private readonly ManagedFileSystem _sut;
+
+ public ManagedFileSystemTests()
+ {
+ _fixture = new Fixture().Customize(new AutoMoqCustomization { ConfigureMembers = true });
+ _sut = _fixture.Create<ManagedFileSystem>();
+ }
+
+ [Theory]
+ [InlineData("/Volumes/Library/Sample/Music/Playlists/", "../Beethoven/Misc/Moonlight Sonata.mp3", "/Volumes/Library/Sample/Music/Beethoven/Misc/Moonlight Sonata.mp3")]
+ [InlineData("/Volumes/Library/Sample/Music/Playlists/", "../../Beethoven/Misc/Moonlight Sonata.mp3", "/Volumes/Library/Sample/Beethoven/Misc/Moonlight Sonata.mp3")]
+ [InlineData("/Volumes/Library/Sample/Music/Playlists/", "Beethoven/Misc/Moonlight Sonata.mp3", "/Volumes/Library/Sample/Music/Playlists/Beethoven/Misc/Moonlight Sonata.mp3")]
+ public void MakeAbsolutePathCorrectlyHandlesRelativeFilePaths(
+ string folderPath,
+ string filePath,
+ string expectedAbsolutePath)
+ {
+ var generatedPath = _sut.MakeAbsolutePath(folderPath, filePath);
+
+ if (MediaBrowser.Common.System.OperatingSystem.Id == OperatingSystemId.Windows)
+ {
+ var expectedWindowsPath = expectedAbsolutePath.Replace('/', '\\');
+ Assert.Equal(expectedWindowsPath, generatedPath.Split(':')[1]);
+ }
+ else
+ {
+ Assert.Equal(expectedAbsolutePath, generatedPath);
+ }
+ }
+ }
+}
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj
new file mode 100644
index 000000000..29733a1c4
--- /dev/null
+++ b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj
@@ -0,0 +1,24 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFramework>netcoreapp3.1</TargetFramework>
+ <IsPackable>false</IsPackable>
+ <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+ <Nullable>enable</Nullable>
+ <RootNamespace>Jellyfin.Server.Implementations.Tests</RootNamespace>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <PackageReference Include="AutoFixture" Version="4.11.0" />
+ <PackageReference Include="AutoFixture.AutoMoq" Version="4.11.0" />
+ <PackageReference Include="Moq" Version="4.13.1" />
+ <PackageReference Include="xunit" Version="2.4.1" />
+ <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
+ <PackageReference Include="coverlet.collector" Version="1.2.0" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\..\Emby.Server.Implementations\Emby.Server.Implementations.csproj" />
+ </ItemGroup>
+
+</Project>
diff --git a/tests/coverletArgs.runsettings b/tests/coverletArgs.runsettings
new file mode 100644
index 000000000..3113957e0
--- /dev/null
+++ b/tests/coverletArgs.runsettings
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<RunSettings>
+ <DataCollectionRunSettings>
+ <DataCollectors>
+ <DataCollector friendlyName="XPlat code coverage">
+ <Configuration>
+ <Format>cobertura</Format>
+ <Exclude>[coverlet.*.tests?]*,[*]Coverlet.Core*,[*]Moq*</Exclude> <!-- [Assembly-Filter]Type-Filter -->
+ <ExcludeByAttribute>Obsolete,GeneratedCodeAttribute,CompilerGeneratedAttribute</ExcludeByAttribute>
+ <SingleHit>false</SingleHit>
+ <UseSourceLink>true</UseSourceLink>
+ <IncludeTestAssembly>false</IncludeTestAssembly>
+ </Configuration>
+ </DataCollector>
+ </DataCollectors>
+ </DataCollectionRunSettings>
+</RunSettings> \ No newline at end of file